Define remote playback proto buffer

This allows us to send RPC message to remote device for content playback.

Review-Url: https://codereview.chromium.org/2261503002
Cr-Commit-Position: refs/heads/master@{#420821}
diff --git a/media/remoting/BUILD.gn b/media/remoting/BUILD.gn
new file mode 100644
index 0000000..90aeeada
--- /dev/null
+++ b/media/remoting/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+import("//testing/test.gni")
+
+proto_library("media_remoting_proto") {
+  proto_out_dir = "media/remoting"
+  sources = [
+    "proto/remoting_rpc_message.proto",
+  ]
+}
+
+source_set("rpc") {
+  sources = [
+    "rpc/proto_enum_utils.cc",
+    "rpc/proto_enum_utils.h",
+    "rpc/proto_utils.cc",
+    "rpc/proto_utils.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+
+  public_deps = [
+    ":media_remoting_proto",
+  ]
+}
+
+test("media_remoting_unittests") {
+  sources = [
+    "rpc/proto_utils_unittest.cc",
+  ]
+
+  deps = [
+    ":rpc",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//media",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//ui/gfx:test_support",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/media/remoting/OWNERS b/media/remoting/OWNERS
new file mode 100644
index 0000000..98698443
--- /dev/null
+++ b/media/remoting/OWNERS
@@ -0,0 +1,2 @@
+erickung@chromium.org
+miu@chromium.org
diff --git a/media/remoting/README b/media/remoting/README
new file mode 100644
index 0000000..6ef5278
--- /dev/null
+++ b/media/remoting/README
@@ -0,0 +1,3 @@
+This folder provides an interface to play local content to the remote device.
+All the command and control messages will be converted into proto buffer as
+RPC message to the end point.
\ No newline at end of file
diff --git a/media/remoting/proto/remoting_rpc_message.proto b/media/remoting/proto/remoting_rpc_message.proto
new file mode 100644
index 0000000..e9b9e7e
--- /dev/null
+++ b/media/remoting/proto/remoting_rpc_message.proto
@@ -0,0 +1,563 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Protocol buffer for Media Remoting.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package media.remoting.pb;
+
+// DecoderBuffer information which will be sent using RTP packets. The actual
+// decoder buffer is not included in this proto data structure.
+message DecoderBuffer {
+  optional int64 timestamp_usec = 1;
+  optional int64 duration_usec = 2;
+  optional bool is_key_frame = 3;
+  optional DecryptConfig decrypt_config = 4;
+  optional int64 front_discard_usec = 5;
+  optional int64 back_discard_usec = 6;
+  optional int64 splice_timestamp_usec = 7;
+  optional bytes side_data = 8;
+  // To distinguish from valid 0-length buffers
+  optional bool is_eos = 9;
+}
+
+// Utility proto data structure
+message Size {
+  optional int32 width = 1;
+  optional int32 height = 2;
+}
+
+message EncryptionScheme {
+  // Align with EncryptionScheme::CipherMode
+  enum CipherMode {
+    CIPHER_MODE_UNENCRYPTED = 0;
+    CIPHER_MODE_AES_CTR = 1;
+    CIPHER_MODE_AES_CBC = 2;
+  }
+
+  optional CipherMode mode = 1;
+  optional uint32 encrypt_blocks = 2;
+  optional uint32 skip_blocks = 3;
+}
+
+message AudioDecoderConfig {
+  // Should align with ::media::Codec
+  enum Codec {
+    kUnknownAudioCodec = 0;
+    kCodecAAC = 1;
+    kCodecMP3 = 2;
+    kCodecPCM = 3;
+    kCodecVorbis = 4;
+    kCodecFLAC = 5;
+    kCodecAMR_NB = 6;
+    kCodecAMR_WB = 7;
+    kCodecPCM_MULAW = 8;
+    kCodecGSM_MS = 9;
+    kCodecPCM_S16BE = 10;
+    kCodecPCM_S24BE = 11;
+    kCodecOpus = 12;
+    kCodecEAC3 = 13;
+    kCodecPCM_ALAW = 14;
+    kCodecALAC = 15;
+    kCodecAC3 = 16;
+  }
+
+  // Should align with ::media::SampleFormat
+  enum SampleFormat {
+    kUnknownSampleFormat = 0;
+    kSampleFormatU8 = 1;
+    kSampleFormatS16 = 2;
+    kSampleFormatS32 = 3;
+    kSampleFormatF32 = 4;
+    kSampleFormatPlanarS16 = 5;
+    kSampleFormatPlanarF32 = 6;
+    kSampleFormatPlanarS32 = 7;
+    kSampleFormatS24 = 8;
+  };
+
+  // Should align with ::media::ChannelLayout
+  enum ChannelLayout {
+    CHANNEL_LAYOUT_NONE = 0;
+    CHANNEL_LAYOUT_UNSUPPORTED = 1;
+    CHANNEL_LAYOUT_MONO = 2;
+    CHANNEL_LAYOUT_STEREO = 3;
+    CHANNEL_LAYOUT_2_1 = 4;
+    CHANNEL_LAYOUT_SURROUND = 5;
+    CHANNEL_LAYOUT_4_0 = 6;
+    CHANNEL_LAYOUT_2_2 = 7;
+    CHANNEL_LAYOUT_QUAD = 8;
+    CHANNEL_LAYOUT_5_0 = 9;
+    CHANNEL_LAYOUT_5_1 = 10;
+    CHANNEL_LAYOUT_5_0_BACK = 11;
+    CHANNEL_LAYOUT_5_1_BACK = 12;
+    CHANNEL_LAYOUT_7_0 = 13;
+    CHANNEL_LAYOUT_7_1 = 14;
+    CHANNEL_LAYOUT_7_1_WIDE = 15;
+    CHANNEL_LAYOUT_STEREO_DOWNMIX = 16;
+    CHANNEL_LAYOUT_2POINT1 = 17;
+    CHANNEL_LAYOUT_3_1 = 18;
+    CHANNEL_LAYOUT_4_1 = 19;
+    CHANNEL_LAYOUT_6_0 = 20;
+    CHANNEL_LAYOUT_6_0_FRONT = 21;
+    CHANNEL_LAYOUT_HEXAGONAL = 22;
+    CHANNEL_LAYOUT_6_1 = 23;
+    CHANNEL_LAYOUT_6_1_BACK = 24;
+    CHANNEL_LAYOUT_6_1_FRONT = 25;
+    CHANNEL_LAYOUT_7_0_FRONT = 26;
+    CHANNEL_LAYOUT_7_1_WIDE_BACK = 27;
+    CHANNEL_LAYOUT_OCTAGONAL = 28;
+    CHANNEL_LAYOUT_DISCRETE = 29;
+    CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC = 30;
+    CHANNEL_LAYOUT_4_1_QUAD_SIDE = 31;
+  };
+
+  optional Codec codec = 1;
+  optional EncryptionScheme encryption_scheme = 2;
+  optional SampleFormat sample_format = 3;
+  optional ChannelLayout channel_layout = 4;
+  optional int32 samples_per_second = 5;
+  optional int64 seek_preroll_usec = 6;
+  optional int32 codec_delay = 7;
+  optional bytes extra_data = 8;
+}
+
+message Rect {
+  optional int32 x = 1;
+  optional int32 y = 2;
+  optional int32 width = 3;
+  optional int32 height = 4;
+}
+
+message VideoDecoderConfig {
+  // Should align with ::media::VideoCodec
+  enum Codec {
+    kUnknownVideoCodec = 0;
+    kCodecH264 = 1;
+    kCodecVC1 = 2;
+    kCodecMPEG2 = 3;
+    kCodecMPEG4 = 4;
+    kCodecTheora = 5;
+    kCodecVP8 = 6;
+    kCodecVP9 = 7;
+    kCodecHEVC = 8;
+  }
+
+  // Should align with ::media::VideoCodecProfile
+  enum Profile {
+    VIDEO_CODEC_PROFILE_UNKNOWN = -1;
+    H264PROFILE_BASELINE = 0;
+    H264PROFILE_MAIN = 1;
+    H264PROFILE_EXTENDED = 2;
+    H264PROFILE_HIGH = 3;
+    H264PROFILE_HIGH10PROFILE = 4;
+    H264PROFILE_HIGH422PROFILE = 5;
+    H264PROFILE_HIGH444PREDICTIVEPROFILE = 6;
+    H264PROFILE_SCALABLEBASELINE = 7;
+    H264PROFILE_SCALABLEHIGH = 8;
+    H264PROFILE_STEREOHIGH = 9;
+    H264PROFILE_MULTIVIEWHIGH = 10;
+    VP8PROFILE_ANY = 11;
+    VP9PROFILE_PROFILE0 = 12;
+    VP9PROFILE_PROFILE1 = 13;
+    VP9PROFILE_PROFILE2 = 14;
+    VP9PROFILE_PROFILE3 = 15;
+    HEVCPROFILE_MAIN = 16;
+    HEVCPROFILE_MAIN10 = 17;
+    HEVCPROFILE_MAIN_STILL_PICTURE = 18;
+  };
+
+  // Should align with ::media::VideoPixelFormat
+  enum Format {
+    PIXEL_FORMAT_UNKNOWN = 0;
+    PIXEL_FORMAT_I420 = 1;
+    PIXEL_FORMAT_YV12 = 2;
+    PIXEL_FORMAT_YV16 = 3;
+    PIXEL_FORMAT_YV12A = 4;
+    PIXEL_FORMAT_YV24 = 5;
+    PIXEL_FORMAT_NV12 = 6;
+    PIXEL_FORMAT_NV21 = 7;
+    PIXEL_FORMAT_UYVY = 8;
+    PIXEL_FORMAT_YUY2 = 9;
+    PIXEL_FORMAT_ARGB = 10;
+    PIXEL_FORMAT_XRGB = 11;
+    PIXEL_FORMAT_RGB24 = 12;
+    PIXEL_FORMAT_RGB32 = 13;
+    PIXEL_FORMAT_MJPEG = 14;
+    PIXEL_FORMAT_MT21 = 15;
+    PIXEL_FORMAT_YUV420P9 = 16;
+    PIXEL_FORMAT_YUV420P10 = 17;
+    PIXEL_FORMAT_YUV422P9 = 18;
+    PIXEL_FORMAT_YUV422P10 = 19;
+    PIXEL_FORMAT_YUV444P9 = 20;
+    PIXEL_FORMAT_YUV444P10 = 21;
+  };
+
+  // Should align with ::media::ColorSpace
+  enum ColorSpace {
+    COLOR_SPACE_UNSPECIFIED = 0;
+    COLOR_SPACE_JPEG = 1;
+    COLOR_SPACE_HD_REC709 = 2;
+    COLOR_SPACE_SD_REC601 = 3;
+  };
+
+  optional Codec codec = 1;
+  optional EncryptionScheme encryption_scheme = 2;
+  optional Profile profile = 3;
+  optional Format format = 4;
+  optional ColorSpace color_space = 5;
+  optional Size coded_size = 6;
+  optional Rect visible_rect = 7;
+  optional Size natural_size = 8;
+  optional bytes extra_data = 9;
+}
+
+message DecryptConfig {
+  message SubSample {
+    optional uint32 clear_bytes = 1;
+    optional uint32 cypher_bytes = 2;
+  }
+
+  optional bytes key_id = 1;
+  optional bytes iv = 2;
+  repeated SubSample sub_samples = 3;
+}
+
+message PipelineStatistics {
+  optional uint64 audio_bytes_decoded = 1;
+  optional uint64 video_bytes_decoded = 2;
+  optional uint32 video_frames_decoded = 3;
+  optional uint32 video_frames_dropped = 4;
+  optional int64 audio_memory_usage = 5;
+  optional int64 video_memory_usage = 6;
+};
+
+message CdmKeyInformation {
+  // Align with ::media::CdmKeyInformation::KeyStatus
+  enum KeyStatus {
+    USABLE = 0;
+    INTERNAL_ERROR = 1;
+    EXPIRED = 2;
+    OUTPUT_RESTRICTED = 3;
+    OUTPUT_DOWNSCALED = 4;
+    KEY_STATUS_PENDING = 5;
+    RELEASED = 6;
+  }
+
+  optional bytes key_id = 1;
+  optional KeyStatus status = 2;
+  optional uint32 system_code = 3;
+}
+
+// Should align with ::media::MediaKeys::Exception
+enum MediaKeysException {
+  NOT_SUPPORTED_ERROR = 0;
+  INVALID_STATE_ERROR = 1;
+  INVALID_ACCESS_ERROR = 2;
+  QUOTA_EXCEEDED_ERROR = 3;
+  UNKNOWN_ERROR = 4;
+  CLIENT_ERROR = 5;
+  OUTPUT_ERROR = 6;
+}
+
+// Should align with ::media::MediaKeys::MessageType
+enum MediaKeysMessageType {
+  LICENSE_REQUEST = 0;
+  LICENSE_RENEWAL = 1;
+  LICENSE_RELEASE = 2;
+}
+
+// Should align with ::media::MediaKeys::SessionType
+enum MediaKeysSessionType {
+  TEMPORARY_SESSION = 0;
+  PERSISTENT_LICENSE_SESSION = 1;
+  PERSISTENT_RELEASE_MESSAGE_SESSION = 2;
+};
+
+// Custom proto data structure
+message RendererInitialize {
+  optional int32 client_handle = 1;
+  optional int32 audio_demuxer_handle = 2;
+  optional int32 video_demuxer_handle = 3;
+  optional int32 callback_handle = 4;
+}
+
+message RendererFlushUntil {
+  optional uint32 audio_count = 1;
+  optional uint32 video_count = 2;
+  optional int32 callback_handle = 3;
+}
+
+message RendererSetCdm {
+  optional int32 cdm_id = 1;
+  optional int32 callback_handle = 2;
+}
+
+message RendererClientOnTimeUpdate {
+  optional int64 time_usec = 1;
+  optional int64 max_time_usec = 2;
+}
+
+message DemuxerStreamReadUntil {
+  optional int32 callback_handle = 1;
+  optional uint32 count = 2;
+}
+
+message DemuxerStreamInitializeCallback {
+  optional int32 type = 1;
+  optional AudioDecoderConfig audio_decoder_config = 2;
+  optional VideoDecoderConfig video_decoder_config = 3;
+}
+
+message DemuxerStreamReadUntilCallback {
+  // Should align with ::media::DemuxerStream::Status
+  enum Status {
+    kOk = 0;
+    kAborted = 1;
+    kConfigChanged = 2;
+  };
+
+  optional Status status = 1;
+  optional uint32 count = 2;
+  optional AudioDecoderConfig audio_decoder_config = 3;
+  optional VideoDecoderConfig video_decoder_config = 4;
+}
+
+message CdmInitialize {
+  optional string key_system = 1;
+  optional string security_origin = 2;
+  optional bool allow_distinctive_identifier = 3;
+  optional bool allow_persistent_state = 4;
+  optional bool use_hw_secure_codecs = 5;
+  optional int32 callback_handle = 6;
+}
+
+message CdmSetServerCertificate {
+  optional int32 callback_handle = 1;
+  optional bytes certificate_data = 2;
+}
+
+message CdmCreateSessionAndGenerateRequest {
+  enum EmeInitDataType {
+    UNKNOWN = 0;
+    WEBM = 1;
+    CENC = 2;
+    KEYIDS = 3;
+  };
+
+  optional MediaKeysSessionType session_type = 1;
+  optional EmeInitDataType init_data_type = 2;
+  optional int32 callback_handle = 3;
+  optional bytes init_data = 4;
+}
+
+message CdmLoadSession {
+  optional MediaKeysSessionType session_type = 1;
+  optional string session_id = 2;
+  optional int32 callback_handle = 3;
+}
+
+message CdmUpdateSession {
+  optional string session_id = 1;
+  optional int32 callback_handle = 2;
+  optional bytes response = 3;
+}
+
+message CdmCloseSession {
+  optional string session_id = 1;
+  optional int32 callback_handle = 2;
+}
+
+message CdmRemoveSession {
+  optional string session_id = 1;
+  optional int32 callback_handle = 2;
+}
+
+message CdmPromise {
+  // These two fields are used only for RPC_CDM_INITIALIZE_CALLBACK
+  optional int32 cdm_id = 1;
+  optional int32 decryptor_handle = 2;
+
+  optional string session_id = 3;
+  optional bool success = 4;
+  optional MediaKeysException exception = 5;
+  optional uint32 system_code = 6;
+  optional string error_message = 7;
+}
+
+message CdmClientOnSessionMessage {
+  optional string session_id = 1;
+  optional MediaKeysMessageType message_type = 2;
+  optional bytes message = 3;
+}
+
+message CdmClientOnSessionKeysChange {
+  optional string session_id = 1;
+  optional bool has_additional_usable_key = 2;
+  repeated CdmKeyInformation key_information = 3;
+}
+
+message CdmClientOnSessionExpirationUpdate {
+  optional string session_id = 1;
+  optional double new_expiry_time_sec = 2;
+}
+
+message RpcMessage {
+  enum RpcProc {
+    // Remoting setup
+    RPC_INTERNAL = 0;
+    RPC_ACQUIRE_RENDERER = 1;
+    RPC_ACQUIRE_RENDERER_DONE = 2;
+    RPC_ACQUIRE_CDM = 3;
+    RPC_ACQUIRE_CDM_DONE = 4;
+    // Renderer message
+    RPC_R_INITIALIZE = 1000;
+    RPC_R_FLUSHUNTIL = 1001;
+    RPC_R_STARTPLAYINGFROM = 1002;
+    RPC_R_SETPLAYBACKRATE = 1003;
+    RPC_R_SETVOLUME = 1004;
+    RPC_R_SETCDM = 1005;
+    // Renderer callbacks
+    RPC_R_INITIALIZE_CALLBACK = 1100;
+    RPC_R_FLUSHUNTIL_CALLBACK = 1101;
+    RPC_R_SETCDM_CALLBACK = 1102;
+    // Renderer client message
+    RPC_RC_ONTIMEUPDATE = 2000;
+    RPC_RC_ONBUFFERINGSTATECHANGE = 2001;
+    RPC_RC_ONENDED = 2002;
+    RPC_RC_ONERROR = 2003;
+    RPC_RC_ONVIDEONATURALSIZECHANGE = 2004;
+    RPC_RC_ONVIDEOOPACITYCHANGE = 2005;
+    RPC_RC_ONSTATISTICSUPDATE = 2006;
+    RPC_RC_ONWAITINGFORDECRYPTIONKEY = 2007;
+    RPC_RC_ONDURATIONCHANGE = 2008;
+    // DemuxerStream message
+    RPC_DS_INITIALIZE = 3000;
+    RPC_DS_READUNTIL = 3001;
+    RPC_DS_ENABLEBITSTREAMCONVERTER = 3002;
+    // DemuxerStream callbacks
+    RPC_DS_INITIALIZE_CALLBACK = 3100;
+    RPC_DS_READUNTIL_CALLBACK = 3101;
+    // ContentDecryptionModule
+    RPC_CDM_SETCLIENT = 4000;
+    RPC_CDM_INITIALIZE = 4001;
+    RPC_CDM_SETSERVERCERTIFICATE = 4002;
+    RPC_CDM_CREATESESSIONANDGENERATEREQUEST = 4003;
+    RPC_CDM_LOADSESSION = 4004;
+    RPC_CDM_UPDATESESSION = 4005;
+    RPC_CDM_CLOSESESSION = 4006;
+    RPC_CDM_REMOVESESSION = 4007;
+    // ContentDecryptionModule callbacks
+    RPC_CDM_INITIALIZE_CALLBACK = 4100;
+    RPC_CDM_SETSERVERCERTIFICATE_CALLBACK = 4101;
+    RPC_CDM_CREATESESSIONANDGENERATEREQUEST_CALLBACK = 4102;
+    RPC_CDM_LOADSESSION_CALLBACK = 4103;
+    RPC_CDM_UPDATESESSION_CALLBACK = 4104;
+    RPC_CDM_CLOSESESSION_CALLBACK = 4105;
+    RPC_CDM_REMOVESESSION_CALLBACK = 4106;
+    // ContentDecryptionModule client
+    RPC_CDMC_ONSESSIONMESSAGE = 5000;
+    RPC_CDMC_ONSESSIONCLOSED = 5001;
+    RPC_CDMC_ONSESSIONKEYSCHANGE = 5002;
+    RPC_CDMC_ONSESSIONEXPIRATIONUPDATE = 5003;
+  };
+
+  // Component base of RPC message handle. This allows both sender and receiver
+  // to send or handle message in desired individual components.
+  optional int32 handle = 1;
+
+  // RpcProc of this RPC message
+  optional RpcProc proc = 2;
+
+  oneof rpc_oneof {
+    // For simple RPC which only passes one parameters can use the following
+    // various data type variables without using specific proto data structure.
+    // RPC_ACQUIRE_RENDERER
+    // RPC_ACQUIRE_RENDERER_DONE
+    // RPC_ACQUIRE_CDM
+    // RPC_ACQUIRE_CDM_DONE
+    // RPC_RC_ONBUFFERINGSTATECHANGE
+    // RPC_DS_INITIALIZE_CALLBACK
+    // RPC_DS_READ
+    // RPC_CDM_SETCLIENT
+    int32 integer_value = 3;
+
+    // RPC_R_STARTPLAYINGFROM
+    // RPC_RC_ONDURATIONCHANGE
+    int64 integer64_value = 4;
+
+    // RPC_R_SETPLAYBACKRATE
+    // RPC_R_SETVOLUME
+    double double_value = 5;
+
+    // RPC_R_INITIALIZE_CALLBACK
+    // RPC_R_SETCDM_CALLBACK
+    // RPC_RC_ONVIDEOOPACITYCHANGE
+    bool boolean_value = 6;
+
+    // string only:
+    // RPC_CDMC_ONSESSIONCLOSED
+    string string_value = 7;
+
+    // RPC_R_INITIALIZE
+    RendererInitialize renderer_initialize_rpc = 100;
+
+    // RPC_R_FLUSHUNTIL
+    RendererFlushUntil renderer_flushuntil_rpc = 101;
+
+    // RPC_R_SETCDM
+    RendererSetCdm renderer_set_cdm_rpc = 102;
+
+    // RPC_RC_ONTIMEUPDATE
+    RendererClientOnTimeUpdate rendererclient_ontimeupdate_rpc = 200;
+    // RPC_RC_ONVIDEONATURALSIZECHANGE
+    Size rendererclient_onvideonatualsizechange_rpc = 201;
+    // RPC_RC_ONSTATISTICSUPDATE
+    PipelineStatistics rendererclient_onstatisticsupdate_rpc = 202;
+
+    // RPC_DS_READUNTIL
+    DemuxerStreamReadUntil demuxerstream_readuntil_rpc = 300;
+
+    // RPC_DS_INITIALIZE_CALLBACK
+    DemuxerStreamInitializeCallback demuxerstream_initializecb_rpc = 400;
+    // RPC_DS_READUNTIL_CALLBACK
+    DemuxerStreamReadUntilCallback demuxerstream_readuntilcb_rpc = 401;
+
+    // RPC_CDM_INITIALIZE
+    CdmInitialize cdm_initialize_rpc = 500;
+    // RPC_CDM_SETSERVERCERTIFICATE
+    CdmSetServerCertificate cdm_setservercertificate_rpc = 501;
+    // RPC_CDM_CREATESESSIONANDGENERATEREQUEST
+    CdmCreateSessionAndGenerateRequest cdm_createsessionandgeneraterequest_rpc =
+        502;
+    // RPC_CDM_LOADSESSION
+    CdmLoadSession cdm_loadsession_rpc = 503;
+    // RPC_CDM_UPDATESESSION
+    CdmUpdateSession cdm_updatesession_rpc = 504;
+    // RPC_CDM_CLOSESESSION
+    CdmCloseSession cdm_closesession_rpc = 505;
+    // RPC_CDM_REMOVESESSION
+    CdmRemoveSession cdm_removesession_rpc = 506;
+
+    // CdmPromise message type used for the following procedure
+    // RPC_CDM_INITIALIZE_CALLBACK
+    // RPC_CDM_SETSERVERCERTIFICATE_CALLBACK
+    // RPC_CDM_CREATESESSIONANDGENERATEREQUEST_CALLBACK
+    // RPC_CDM_LOADSESSION_CALLBACK
+    // RPC_CDM_UPDATESESSION_CALLBACK
+    // RPC_CDM_CLOSESESSION_CALLBACK
+    // RPC_CDM_REMOVESESSION_CALLBACK
+    CdmPromise cdm_promise_rpc = 600;
+
+    // RPC_CDMC_ONSESSIONMESSAGE
+    CdmClientOnSessionMessage cdmclient_onsessionmessage_rpc = 601;
+    // RPC_CDMC_ONSESSIONKEYSCHANGE
+    CdmClientOnSessionKeysChange cdmclient_onsessionkeychange_rpc = 602;
+    // RPC_CDMC_ONSESSIONEXPIRATIONUPDATE
+    CdmClientOnSessionExpirationUpdate cdmclient_onsessionexpirationupdate_rpc =
+        603;
+  };
+}
\ No newline at end of file
diff --git a/media/remoting/rpc/proto_enum_utils.cc b/media/remoting/rpc/proto_enum_utils.cc
new file mode 100644
index 0000000..5fd0ac7
--- /dev/null
+++ b/media/remoting/rpc/proto_enum_utils.cc
@@ -0,0 +1,553 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/rpc/proto_enum_utils.h"
+
+namespace media {
+namespace remoting {
+
+#define CASE_RETURN_OTHER(x) \
+  case OriginType::x:        \
+    return OtherType::x
+
+base::Optional<::media::EncryptionScheme::CipherMode>
+ToMediaEncryptionSchemeCipherMode(pb::EncryptionScheme::CipherMode value) {
+  using OriginType = pb::EncryptionScheme;
+  using OtherType = ::media::EncryptionScheme;
+  switch (value) {
+    CASE_RETURN_OTHER(CIPHER_MODE_UNENCRYPTED);
+    CASE_RETURN_OTHER(CIPHER_MODE_AES_CTR);
+    CASE_RETURN_OTHER(CIPHER_MODE_AES_CBC);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::EncryptionScheme::CipherMode>
+ToProtoEncryptionSchemeCipherMode(::media::EncryptionScheme::CipherMode value) {
+  using OriginType = ::media::EncryptionScheme;
+  using OtherType = pb::EncryptionScheme;
+  switch (value) {
+    CASE_RETURN_OTHER(CIPHER_MODE_UNENCRYPTED);
+    CASE_RETURN_OTHER(CIPHER_MODE_AES_CTR);
+    CASE_RETURN_OTHER(CIPHER_MODE_AES_CBC);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::AudioCodec> ToMediaAudioCodec(
+    pb::AudioDecoderConfig::Codec value) {
+  using OriginType = pb::AudioDecoderConfig;
+  using OtherType = ::media::AudioCodec;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownAudioCodec);
+    CASE_RETURN_OTHER(kCodecAAC);
+    CASE_RETURN_OTHER(kCodecMP3);
+    CASE_RETURN_OTHER(kCodecPCM);
+    CASE_RETURN_OTHER(kCodecVorbis);
+    CASE_RETURN_OTHER(kCodecFLAC);
+    CASE_RETURN_OTHER(kCodecAMR_NB);
+    CASE_RETURN_OTHER(kCodecAMR_WB);
+    CASE_RETURN_OTHER(kCodecPCM_MULAW);
+    CASE_RETURN_OTHER(kCodecGSM_MS);
+    CASE_RETURN_OTHER(kCodecPCM_S16BE);
+    CASE_RETURN_OTHER(kCodecPCM_S24BE);
+    CASE_RETURN_OTHER(kCodecOpus);
+    CASE_RETURN_OTHER(kCodecEAC3);
+    CASE_RETURN_OTHER(kCodecPCM_ALAW);
+    CASE_RETURN_OTHER(kCodecALAC);
+    CASE_RETURN_OTHER(kCodecAC3);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::AudioDecoderConfig::Codec> ToProtoAudioDecoderConfigCodec(
+    ::media::AudioCodec value) {
+  using OriginType = ::media::AudioCodec;
+  using OtherType = pb::AudioDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownAudioCodec);
+    CASE_RETURN_OTHER(kCodecAAC);
+    CASE_RETURN_OTHER(kCodecMP3);
+    CASE_RETURN_OTHER(kCodecPCM);
+    CASE_RETURN_OTHER(kCodecVorbis);
+    CASE_RETURN_OTHER(kCodecFLAC);
+    CASE_RETURN_OTHER(kCodecAMR_NB);
+    CASE_RETURN_OTHER(kCodecAMR_WB);
+    CASE_RETURN_OTHER(kCodecPCM_MULAW);
+    CASE_RETURN_OTHER(kCodecGSM_MS);
+    CASE_RETURN_OTHER(kCodecPCM_S16BE);
+    CASE_RETURN_OTHER(kCodecPCM_S24BE);
+    CASE_RETURN_OTHER(kCodecOpus);
+    CASE_RETURN_OTHER(kCodecEAC3);
+    CASE_RETURN_OTHER(kCodecPCM_ALAW);
+    CASE_RETURN_OTHER(kCodecALAC);
+    CASE_RETURN_OTHER(kCodecAC3);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::SampleFormat> ToMediaSampleFormat(
+    pb::AudioDecoderConfig::SampleFormat value) {
+  using OriginType = pb::AudioDecoderConfig;
+  using OtherType = ::media::SampleFormat;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownSampleFormat);
+    CASE_RETURN_OTHER(kSampleFormatU8);
+    CASE_RETURN_OTHER(kSampleFormatS16);
+    CASE_RETURN_OTHER(kSampleFormatS32);
+    CASE_RETURN_OTHER(kSampleFormatF32);
+    CASE_RETURN_OTHER(kSampleFormatPlanarS16);
+    CASE_RETURN_OTHER(kSampleFormatPlanarF32);
+    CASE_RETURN_OTHER(kSampleFormatPlanarS32);
+    CASE_RETURN_OTHER(kSampleFormatS24);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::AudioDecoderConfig::SampleFormat>
+ToProtoAudioDecoderConfigSampleFormat(::media::SampleFormat value) {
+  using OriginType = ::media::SampleFormat;
+  using OtherType = pb::AudioDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownSampleFormat);
+    CASE_RETURN_OTHER(kSampleFormatU8);
+    CASE_RETURN_OTHER(kSampleFormatS16);
+    CASE_RETURN_OTHER(kSampleFormatS32);
+    CASE_RETURN_OTHER(kSampleFormatF32);
+    CASE_RETURN_OTHER(kSampleFormatPlanarS16);
+    CASE_RETURN_OTHER(kSampleFormatPlanarF32);
+    CASE_RETURN_OTHER(kSampleFormatPlanarS32);
+    CASE_RETURN_OTHER(kSampleFormatS24);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::ChannelLayout> ToMediaChannelLayout(
+    pb::AudioDecoderConfig::ChannelLayout value) {
+  using OriginType = pb::AudioDecoderConfig;
+  using OtherType = ::media::ChannelLayout;
+  switch (value) {
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_NONE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_UNSUPPORTED);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_MONO);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_SURROUND);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2_2);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_QUAD);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_0_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_1_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1_WIDE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO_DOWNMIX);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2POINT1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_3_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_0_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_HEXAGONAL);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_0_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1_WIDE_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_OCTAGONAL);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_DISCRETE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_1_QUAD_SIDE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::AudioDecoderConfig::ChannelLayout>
+ToProtoAudioDecoderConfigChannelLayout(::media::ChannelLayout value) {
+  using OriginType = ::media::ChannelLayout;
+  using OtherType = pb::AudioDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_NONE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_UNSUPPORTED);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_MONO);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_SURROUND);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2_2);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_QUAD);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_0_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_5_1_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1_WIDE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO_DOWNMIX);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_2POINT1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_3_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_0);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_0_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_HEXAGONAL);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_6_1_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_0_FRONT);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_7_1_WIDE_BACK);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_OCTAGONAL);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_DISCRETE);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC);
+    CASE_RETURN_OTHER(CHANNEL_LAYOUT_4_1_QUAD_SIDE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::VideoCodec> ToMediaVideoCodec(
+    pb::VideoDecoderConfig::Codec value) {
+  using OriginType = pb::VideoDecoderConfig;
+  using OtherType = ::media::VideoCodec;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownVideoCodec);
+    CASE_RETURN_OTHER(kCodecH264);
+    CASE_RETURN_OTHER(kCodecVC1);
+    CASE_RETURN_OTHER(kCodecMPEG2);
+    CASE_RETURN_OTHER(kCodecMPEG4);
+    CASE_RETURN_OTHER(kCodecTheora);
+    CASE_RETURN_OTHER(kCodecVP8);
+    CASE_RETURN_OTHER(kCodecVP9);
+    CASE_RETURN_OTHER(kCodecHEVC);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::VideoDecoderConfig::Codec> ToProtoVideoDecoderConfigCodec(
+    ::media::VideoCodec value) {
+  using OriginType = ::media::VideoCodec;
+  using OtherType = pb::VideoDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(kUnknownVideoCodec);
+    CASE_RETURN_OTHER(kCodecH264);
+    CASE_RETURN_OTHER(kCodecVC1);
+    CASE_RETURN_OTHER(kCodecMPEG2);
+    CASE_RETURN_OTHER(kCodecMPEG4);
+    CASE_RETURN_OTHER(kCodecTheora);
+    CASE_RETURN_OTHER(kCodecVP8);
+    CASE_RETURN_OTHER(kCodecVP9);
+    CASE_RETURN_OTHER(kCodecHEVC);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::VideoCodecProfile> ToMediaVideoCodecProfile(
+    pb::VideoDecoderConfig::Profile value) {
+  using OriginType = pb::VideoDecoderConfig;
+  using OtherType = ::media::VideoCodecProfile;
+  switch (value) {
+    CASE_RETURN_OTHER(VIDEO_CODEC_PROFILE_UNKNOWN);
+    CASE_RETURN_OTHER(H264PROFILE_BASELINE);
+    CASE_RETURN_OTHER(H264PROFILE_MAIN);
+    CASE_RETURN_OTHER(H264PROFILE_EXTENDED);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH10PROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH422PROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH444PREDICTIVEPROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_SCALABLEBASELINE);
+    CASE_RETURN_OTHER(H264PROFILE_SCALABLEHIGH);
+    CASE_RETURN_OTHER(H264PROFILE_STEREOHIGH);
+    CASE_RETURN_OTHER(H264PROFILE_MULTIVIEWHIGH);
+    CASE_RETURN_OTHER(VP8PROFILE_ANY);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE0);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE1);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE2);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE3);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN10);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN_STILL_PICTURE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::VideoDecoderConfig::Profile>
+ToProtoVideoDecoderConfigProfile(::media::VideoCodecProfile value) {
+  using OriginType = ::media::VideoCodecProfile;
+  using OtherType = pb::VideoDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(VIDEO_CODEC_PROFILE_UNKNOWN);
+    CASE_RETURN_OTHER(H264PROFILE_BASELINE);
+    CASE_RETURN_OTHER(H264PROFILE_MAIN);
+    CASE_RETURN_OTHER(H264PROFILE_EXTENDED);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH10PROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH422PROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_HIGH444PREDICTIVEPROFILE);
+    CASE_RETURN_OTHER(H264PROFILE_SCALABLEBASELINE);
+    CASE_RETURN_OTHER(H264PROFILE_SCALABLEHIGH);
+    CASE_RETURN_OTHER(H264PROFILE_STEREOHIGH);
+    CASE_RETURN_OTHER(H264PROFILE_MULTIVIEWHIGH);
+    CASE_RETURN_OTHER(VP8PROFILE_ANY);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE0);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE1);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE2);
+    CASE_RETURN_OTHER(VP9PROFILE_PROFILE3);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN10);
+    CASE_RETURN_OTHER(HEVCPROFILE_MAIN_STILL_PICTURE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::VideoPixelFormat> ToMediaVideoPixelFormat(
+    pb::VideoDecoderConfig::Format value) {
+  using OriginType = pb::VideoDecoderConfig;
+  using OtherType = ::media::VideoPixelFormat;
+  switch (value) {
+    CASE_RETURN_OTHER(PIXEL_FORMAT_UNKNOWN);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_I420);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV12);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV16);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV12A);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV24);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_NV12);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_NV21);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_UYVY);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUY2);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_ARGB);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_XRGB);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB24);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB32);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_MJPEG);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_MT21);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P10);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P10);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P10);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::VideoDecoderConfig::Format> ToProtoVideoDecoderConfigFormat(
+    ::media::VideoPixelFormat value) {
+  using OriginType = ::media::VideoPixelFormat;
+  using OtherType = pb::VideoDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(PIXEL_FORMAT_UNKNOWN);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_I420);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV12);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV16);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV12A);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YV24);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_NV12);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_NV21);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_UYVY);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUY2);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_ARGB);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_XRGB);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB24);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB32);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_MJPEG);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_MT21);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P10);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P10);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P9);
+    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P10);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::ColorSpace> ToMediaColorSpace(
+    pb::VideoDecoderConfig::ColorSpace value) {
+  using OriginType = pb::VideoDecoderConfig;
+  using OtherType = ::media::ColorSpace;
+  switch (value) {
+    CASE_RETURN_OTHER(COLOR_SPACE_UNSPECIFIED);
+    CASE_RETURN_OTHER(COLOR_SPACE_JPEG);
+    CASE_RETURN_OTHER(COLOR_SPACE_HD_REC709);
+    CASE_RETURN_OTHER(COLOR_SPACE_SD_REC601);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::VideoDecoderConfig::ColorSpace>
+ToProtoVideoDecoderConfigColorSpace(::media::ColorSpace value) {
+  using OriginType = ::media::ColorSpace;
+  using OtherType = pb::VideoDecoderConfig;
+  switch (value) {
+    CASE_RETURN_OTHER(COLOR_SPACE_UNSPECIFIED);
+    CASE_RETURN_OTHER(COLOR_SPACE_JPEG);
+    CASE_RETURN_OTHER(COLOR_SPACE_HD_REC709);
+    CASE_RETURN_OTHER(COLOR_SPACE_SD_REC601);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::CdmKeyInformation::KeyStatus>
+ToMediaCdmKeyInformationKeyStatus(pb::CdmKeyInformation::KeyStatus value) {
+  using OriginType = pb::CdmKeyInformation;
+  using OtherType = ::media::CdmKeyInformation;
+  switch (value) {
+    CASE_RETURN_OTHER(USABLE);
+    CASE_RETURN_OTHER(INTERNAL_ERROR);
+    CASE_RETURN_OTHER(EXPIRED);
+    CASE_RETURN_OTHER(OUTPUT_RESTRICTED);
+    CASE_RETURN_OTHER(OUTPUT_DOWNSCALED);
+    CASE_RETURN_OTHER(KEY_STATUS_PENDING);
+    CASE_RETURN_OTHER(RELEASED);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::CdmKeyInformation::KeyStatus> ToProtoCdmKeyInformation(
+    ::media::CdmKeyInformation::KeyStatus value) {
+  using OriginType = ::media::CdmKeyInformation;
+  using OtherType = pb::CdmKeyInformation;
+  switch (value) {
+    CASE_RETURN_OTHER(USABLE);
+    CASE_RETURN_OTHER(INTERNAL_ERROR);
+    CASE_RETURN_OTHER(EXPIRED);
+    CASE_RETURN_OTHER(OUTPUT_RESTRICTED);
+    CASE_RETURN_OTHER(OUTPUT_DOWNSCALED);
+    CASE_RETURN_OTHER(KEY_STATUS_PENDING);
+    CASE_RETURN_OTHER(RELEASED);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::MediaKeys::Exception> ToMediaMediaKeysException(
+    pb::MediaKeysException value) {
+  using OriginType = pb::MediaKeysException;
+  using OtherType = ::media::MediaKeys;
+  switch (value) {
+    CASE_RETURN_OTHER(NOT_SUPPORTED_ERROR);
+    CASE_RETURN_OTHER(INVALID_STATE_ERROR);
+    CASE_RETURN_OTHER(INVALID_ACCESS_ERROR);
+    CASE_RETURN_OTHER(QUOTA_EXCEEDED_ERROR);
+    CASE_RETURN_OTHER(UNKNOWN_ERROR);
+    CASE_RETURN_OTHER(CLIENT_ERROR);
+    CASE_RETURN_OTHER(OUTPUT_ERROR);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::MediaKeysException> ToProtoMediaKeysException(
+    ::media::MediaKeys::Exception value) {
+  using OriginType = ::media::MediaKeys;
+  using OtherType = pb::MediaKeysException;
+  switch (value) {
+    CASE_RETURN_OTHER(NOT_SUPPORTED_ERROR);
+    CASE_RETURN_OTHER(INVALID_STATE_ERROR);
+    CASE_RETURN_OTHER(INVALID_ACCESS_ERROR);
+    CASE_RETURN_OTHER(QUOTA_EXCEEDED_ERROR);
+    CASE_RETURN_OTHER(UNKNOWN_ERROR);
+    CASE_RETURN_OTHER(CLIENT_ERROR);
+    CASE_RETURN_OTHER(OUTPUT_ERROR);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::MediaKeys::MessageType> ToMediaMediaKeysMessageType(
+    pb::MediaKeysMessageType value) {
+  using OriginType = pb::MediaKeysMessageType;
+  using OtherType = ::media::MediaKeys;
+  switch (value) {
+    CASE_RETURN_OTHER(LICENSE_REQUEST);
+    CASE_RETURN_OTHER(LICENSE_RENEWAL);
+    CASE_RETURN_OTHER(LICENSE_RELEASE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::MediaKeysMessageType> ToProtoMediaKeysMessageType(
+    ::media::MediaKeys::MessageType value) {
+  using OriginType = ::media::MediaKeys;
+  using OtherType = pb::MediaKeysMessageType;
+  switch (value) {
+    CASE_RETURN_OTHER(LICENSE_REQUEST);
+    CASE_RETURN_OTHER(LICENSE_RENEWAL);
+    CASE_RETURN_OTHER(LICENSE_RELEASE);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::MediaKeys::SessionType> ToMediaKeysSessionType(
+    pb::MediaKeysSessionType value) {
+  using OriginType = pb::MediaKeysSessionType;
+  using OtherType = ::media::MediaKeys;
+  switch (value) {
+    CASE_RETURN_OTHER(TEMPORARY_SESSION);
+    CASE_RETURN_OTHER(PERSISTENT_LICENSE_SESSION);
+    CASE_RETURN_OTHER(PERSISTENT_RELEASE_MESSAGE_SESSION);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::MediaKeysSessionType> ToProtoMediaKeysSessionType(
+    ::media::MediaKeys::SessionType value) {
+  using OriginType = ::media::MediaKeys;
+  using OtherType = pb::MediaKeysSessionType;
+  switch (value) {
+    CASE_RETURN_OTHER(TEMPORARY_SESSION);
+    CASE_RETURN_OTHER(PERSISTENT_LICENSE_SESSION);
+    CASE_RETURN_OTHER(PERSISTENT_RELEASE_MESSAGE_SESSION);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::EmeInitDataType> ToMediaEmeInitDataType(
+    pb::CdmCreateSessionAndGenerateRequest::EmeInitDataType value) {
+  using OriginType = pb::CdmCreateSessionAndGenerateRequest;
+  using OtherType = ::media::EmeInitDataType;
+  switch (value) {
+    CASE_RETURN_OTHER(UNKNOWN);
+    CASE_RETURN_OTHER(WEBM);
+    CASE_RETURN_OTHER(CENC);
+    CASE_RETURN_OTHER(KEYIDS);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::CdmCreateSessionAndGenerateRequest::EmeInitDataType>
+ToProtoMediaEmeInitDataType(::media::EmeInitDataType value) {
+  using OriginType = ::media::EmeInitDataType;
+  using OtherType = pb::CdmCreateSessionAndGenerateRequest;
+  switch (value) {
+    CASE_RETURN_OTHER(UNKNOWN);
+    CASE_RETURN_OTHER(WEBM);
+    CASE_RETURN_OTHER(CENC);
+    CASE_RETURN_OTHER(KEYIDS);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<::media::DemuxerStream::Status> ToDemuxerStreamStatus(
+    pb::DemuxerStreamReadUntilCallback::Status value) {
+  using OriginType = pb::DemuxerStreamReadUntilCallback;
+  using OtherType = ::media::DemuxerStream;
+  switch (value) {
+    CASE_RETURN_OTHER(kOk);
+    CASE_RETURN_OTHER(kAborted);
+    CASE_RETURN_OTHER(kConfigChanged);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+base::Optional<pb::DemuxerStreamReadUntilCallback::Status>
+ToProtoToDemuxerStreamStatus(::media::DemuxerStream::Status value) {
+  using OriginType = ::media::DemuxerStream;
+  using OtherType = pb::DemuxerStreamReadUntilCallback;
+  switch (value) {
+    CASE_RETURN_OTHER(kOk);
+    CASE_RETURN_OTHER(kAborted);
+    CASE_RETURN_OTHER(kConfigChanged);
+  }
+  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
+}
+
+}  // namespace remoting
+}  // namespace media
diff --git a/media/remoting/rpc/proto_enum_utils.h b/media/remoting/rpc/proto_enum_utils.h
new file mode 100644
index 0000000..62af79b
--- /dev/null
+++ b/media/remoting/rpc/proto_enum_utils.h
@@ -0,0 +1,105 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_REMOTING_RPC_PROTO_ENUM_UTILS_H_
+#define MEDIA_REMOTING_RPC_PROTO_ENUM_UTILS_H_
+
+#include "base/optional.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/cdm_key_information.h"
+#include "media/base/channel_layout.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/encryption_scheme.h"
+#include "media/base/media_keys.h"
+#include "media/base/sample_format.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_types.h"
+#include "media/remoting/remoting_rpc_message.pb.h"
+
+namespace media {
+namespace remoting {
+
+// The following functions map between the enum values in media/base modules and
+// the equivalents in the media/remoting protobuf classes. The purpose of these
+// converters is to decouple the media/base modules from the media/remoting
+// modules while maintaining compile-time checks to ensure that there are always
+// valid, backwards-compatible mappings between the two.
+//
+// Each returns a base::Optional value. If it is not set, that indicates the
+// conversion failed.
+
+base::Optional<::media::EncryptionScheme::CipherMode>
+ToMediaEncryptionSchemeCipherMode(pb::EncryptionScheme::CipherMode value);
+base::Optional<pb::EncryptionScheme::CipherMode>
+ToProtoEncryptionSchemeCipherMode(::media::EncryptionScheme::CipherMode value);
+
+base::Optional<::media::AudioCodec> ToMediaAudioCodec(
+    pb::AudioDecoderConfig::Codec value);
+base::Optional<pb::AudioDecoderConfig::Codec> ToProtoAudioDecoderConfigCodec(
+    ::media::AudioCodec value);
+
+base::Optional<::media::SampleFormat> ToMediaSampleFormat(
+    pb::AudioDecoderConfig::SampleFormat value);
+base::Optional<pb::AudioDecoderConfig::SampleFormat>
+ToProtoAudioDecoderConfigSampleFormat(::media::SampleFormat value);
+
+base::Optional<::media::ChannelLayout> ToMediaChannelLayout(
+    pb::AudioDecoderConfig::ChannelLayout value);
+base::Optional<pb::AudioDecoderConfig::ChannelLayout>
+ToProtoAudioDecoderConfigChannelLayout(::media::ChannelLayout value);
+
+base::Optional<::media::VideoCodec> ToMediaVideoCodec(
+    pb::VideoDecoderConfig::Codec value);
+base::Optional<pb::VideoDecoderConfig::Codec> ToProtoVideoDecoderConfigCodec(
+    ::media::VideoCodec value);
+
+base::Optional<::media::VideoCodecProfile> ToMediaVideoCodecProfile(
+    pb::VideoDecoderConfig::Profile value);
+base::Optional<pb::VideoDecoderConfig::Profile>
+ToProtoVideoDecoderConfigProfile(::media::VideoCodecProfile value);
+
+base::Optional<::media::VideoPixelFormat> ToMediaVideoPixelFormat(
+    pb::VideoDecoderConfig::Format value);
+base::Optional<pb::VideoDecoderConfig::Format> ToProtoVideoDecoderConfigFormat(
+    ::media::VideoPixelFormat value);
+
+base::Optional<::media::ColorSpace> ToMediaColorSpace(
+    pb::VideoDecoderConfig::ColorSpace value);
+base::Optional<pb::VideoDecoderConfig::ColorSpace>
+ToProtoVideoDecoderConfigColorSpace(::media::ColorSpace value);
+
+base::Optional<::media::CdmKeyInformation::KeyStatus>
+ToMediaCdmKeyInformationKeyStatus(pb::CdmKeyInformation::KeyStatus value);
+base::Optional<pb::CdmKeyInformation::KeyStatus> ToProtoCdmKeyInformation(
+    ::media::CdmKeyInformation::KeyStatus value);
+
+base::Optional<::media::MediaKeys::Exception> ToMediaMediaKeysException(
+    pb::MediaKeysException value);
+base::Optional<pb::MediaKeysException> ToProtoMediaKeysException(
+    ::media::MediaKeys::Exception value);
+
+base::Optional<::media::MediaKeys::MessageType> ToMediaMediaKeysMessageType(
+    pb::MediaKeysMessageType value);
+base::Optional<pb::MediaKeysMessageType> ToProtoMediaKeysMessageType(
+    ::media::MediaKeys::MessageType value);
+
+base::Optional<::media::MediaKeys::SessionType> ToMediaKeysSessionType(
+    pb::MediaKeysSessionType value);
+base::Optional<pb::MediaKeysSessionType> ToProtoMediaKeysSessionType(
+    ::media::MediaKeys::SessionType value);
+
+base::Optional<::media::EmeInitDataType> ToMediaEmeInitDataType(
+    pb::CdmCreateSessionAndGenerateRequest::EmeInitDataType value);
+base::Optional<pb::CdmCreateSessionAndGenerateRequest::EmeInitDataType>
+ToProtoMediaEmeInitDataType(::media::EmeInitDataType value);
+
+base::Optional<::media::DemuxerStream::Status> ToDemuxerStreamStatus(
+    pb::DemuxerStreamReadUntilCallback::Status value);
+base::Optional<pb::DemuxerStreamReadUntilCallback::Status>
+ToProtoToDemuxerStreamStatus(::media::DemuxerStream::Status value);
+
+}  // namespace remoting
+}  // namespace media
+
+#endif  // MEDIA_REMOTING_RPC_PROTO_ENUM_UTILS_H_
diff --git a/media/remoting/rpc/proto_utils.cc b/media/remoting/rpc/proto_utils.cc
new file mode 100644
index 0000000..d8fbd06
--- /dev/null
+++ b/media/remoting/rpc/proto_utils.cc
@@ -0,0 +1,472 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/rpc/proto_utils.h"
+
+#include <algorithm>
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "media/base/encryption_scheme.h"
+#include "media/remoting/rpc/proto_enum_utils.h"
+
+namespace media {
+namespace remoting {
+
+namespace {
+
+constexpr size_t kPayloadVersionFieldSize = sizeof(uint8_t);
+constexpr size_t kProtoBufferHeaderSize = sizeof(uint16_t);
+constexpr size_t kDataBufferHeaderSize = sizeof(uint32_t);
+
+std::unique_ptr<::media::DecryptConfig> ConvertProtoToDecryptConfig(
+    const pb::DecryptConfig& config_message) {
+  if (!config_message.has_key_id())
+    return nullptr;
+  if (!config_message.has_iv())
+    return nullptr;
+
+  std::vector<::media::SubsampleEntry> entries(
+      config_message.sub_samples_size());
+  for (int i = 0; i < config_message.sub_samples_size(); ++i) {
+    entries.push_back(
+        ::media::SubsampleEntry(config_message.sub_samples(i).clear_bytes(),
+                                config_message.sub_samples(i).cypher_bytes()));
+  }
+
+  std::unique_ptr<::media::DecryptConfig> decrypt_config(
+      new ::media::DecryptConfig(config_message.key_id(), config_message.iv(),
+                                 entries));
+  return decrypt_config;
+}
+
+scoped_refptr<::media::DecoderBuffer> ConvertProtoToDecoderBuffer(
+    const pb::DecoderBuffer& buffer_message,
+    scoped_refptr<::media::DecoderBuffer> buffer) {
+  if (buffer_message.is_eos()) {
+    VLOG(1) << "EOS data";
+    return ::media::DecoderBuffer::CreateEOSBuffer();
+  }
+
+  if (buffer_message.has_timestamp_usec()) {
+    buffer->set_timestamp(
+        base::TimeDelta::FromMicroseconds(buffer_message.timestamp_usec()));
+  }
+
+  if (buffer_message.has_duration_usec()) {
+    buffer->set_duration(
+        base::TimeDelta::FromMicroseconds(buffer_message.duration_usec()));
+  }
+  VLOG(3) << "timestamp:" << buffer_message.timestamp_usec()
+          << " duration:" << buffer_message.duration_usec();
+
+  if (buffer_message.has_is_key_frame())
+    buffer->set_is_key_frame(buffer_message.is_key_frame());
+
+  if (buffer_message.has_decrypt_config()) {
+    buffer->set_decrypt_config(
+        ConvertProtoToDecryptConfig(buffer_message.decrypt_config()));
+  }
+
+  bool has_discard = false;
+  base::TimeDelta front_discard;
+  if (buffer_message.has_front_discard_usec()) {
+    has_discard = true;
+    front_discard =
+        base::TimeDelta::FromMicroseconds(buffer_message.front_discard_usec());
+  }
+  base::TimeDelta back_discard;
+  if (buffer_message.has_back_discard_usec()) {
+    has_discard = true;
+    back_discard =
+        base::TimeDelta::FromMicroseconds(buffer_message.back_discard_usec());
+  }
+
+  if (has_discard) {
+    buffer->set_discard_padding(
+        ::media::DecoderBuffer::DiscardPadding(front_discard, back_discard));
+  }
+
+  if (buffer_message.has_splice_timestamp_usec()) {
+    buffer->set_splice_timestamp(base::TimeDelta::FromMicroseconds(
+        buffer_message.splice_timestamp_usec()));
+  }
+
+  if (buffer_message.has_side_data()) {
+    buffer->CopySideDataFrom(
+        reinterpret_cast<const uint8_t*>(buffer_message.side_data().data()),
+        buffer_message.side_data().size());
+  }
+
+  return buffer;
+}
+
+void ConvertDecryptConfigToProto(const ::media::DecryptConfig& decrypt_config,
+                                 pb::DecryptConfig* config_message) {
+  DCHECK(config_message);
+
+  config_message->set_key_id(decrypt_config.key_id());
+  config_message->set_iv(decrypt_config.iv());
+
+  for (const auto& entry : decrypt_config.subsamples()) {
+    pb::DecryptConfig::SubSample* sub_sample =
+        config_message->add_sub_samples();
+    sub_sample->set_clear_bytes(entry.clear_bytes);
+    sub_sample->set_cypher_bytes(entry.cypher_bytes);
+  }
+}
+
+void ConvertDecoderBufferToProto(
+    const scoped_refptr<::media::DecoderBuffer>& decoder_buffer,
+    pb::DecoderBuffer* buffer_message) {
+  if (decoder_buffer->end_of_stream()) {
+    buffer_message->set_is_eos(true);
+    return;
+  }
+
+  VLOG(3) << "timestamp:" << decoder_buffer->timestamp().InMicroseconds()
+          << " duration:" << decoder_buffer->duration().InMicroseconds();
+  buffer_message->set_timestamp_usec(
+      decoder_buffer->timestamp().InMicroseconds());
+  buffer_message->set_duration_usec(
+      decoder_buffer->duration().InMicroseconds());
+  buffer_message->set_is_key_frame(decoder_buffer->is_key_frame());
+
+  if (decoder_buffer->decrypt_config()) {
+    ConvertDecryptConfigToProto(*decoder_buffer->decrypt_config(),
+                                buffer_message->mutable_decrypt_config());
+  }
+
+  buffer_message->set_front_discard_usec(
+      decoder_buffer->discard_padding().first.InMicroseconds());
+  buffer_message->set_back_discard_usec(
+      decoder_buffer->discard_padding().second.InMicroseconds());
+  buffer_message->set_splice_timestamp_usec(
+      decoder_buffer->splice_timestamp().InMicroseconds());
+
+  if (decoder_buffer->side_data_size()) {
+    buffer_message->set_side_data(decoder_buffer->side_data(),
+                                  decoder_buffer->side_data_size());
+  }
+}
+
+}  // namespace
+
+scoped_refptr<::media::DecoderBuffer> ByteArrayToDecoderBuffer(
+    const uint8_t* data,
+    uint32_t size) {
+  base::BigEndianReader reader(reinterpret_cast<const char*>(data), size);
+  uint8_t payload_version = 0;
+  uint16_t proto_size = 0;
+  pb::DecoderBuffer segment;
+  uint32_t buffer_size = 0;
+  if (reader.ReadU8(&payload_version) && payload_version == 0 &&
+      reader.ReadU16(&proto_size) &&
+      static_cast<int>(proto_size) < reader.remaining() &&
+      segment.ParseFromArray(reader.ptr(), proto_size) &&
+      reader.Skip(proto_size) && reader.ReadU32(&buffer_size) &&
+      static_cast<int64_t>(buffer_size) <= reader.remaining()) {
+    // Deserialize proto buffer. It passes the pre allocated DecoderBuffer into
+    // the function because the proto buffer may overwrite DecoderBuffer since
+    // it may be EOS buffer.
+    scoped_refptr<media::DecoderBuffer> decoder_buffer =
+        ConvertProtoToDecoderBuffer(
+            segment,
+            DecoderBuffer::CopyFrom(
+                reinterpret_cast<const uint8_t*>(reader.ptr()), buffer_size));
+    return decoder_buffer;
+  }
+
+  LOG(ERROR) << "Not able to convert byte array to ::media::DecoderBuffer";
+  return nullptr;
+}
+
+std::vector<uint8_t> DecoderBufferToByteArray(
+    const scoped_refptr<::media::DecoderBuffer>& decoder_buffer) {
+  pb::DecoderBuffer decoder_buffer_message;
+  ConvertDecoderBufferToProto(decoder_buffer, &decoder_buffer_message);
+
+  size_t decoder_buffer_size =
+      decoder_buffer->end_of_stream() ? 0 : decoder_buffer->data_size();
+  size_t size = kPayloadVersionFieldSize + kProtoBufferHeaderSize +
+                decoder_buffer_message.ByteSize() + kDataBufferHeaderSize +
+                decoder_buffer_size;
+  std::vector<uint8_t> buffer(size);
+  base::BigEndianWriter writer(reinterpret_cast<char*>(buffer.data()),
+                               buffer.size());
+  if (writer.WriteU8(0) &&
+      writer.WriteU16(
+          static_cast<uint16_t>(decoder_buffer_message.GetCachedSize())) &&
+      decoder_buffer_message.SerializeToArray(
+          writer.ptr(), decoder_buffer_message.GetCachedSize()) &&
+      writer.Skip(decoder_buffer_message.GetCachedSize()) &&
+      writer.WriteU32(decoder_buffer_size)) {
+    if (decoder_buffer_size) {
+      // DecoderBuffer frame data.
+      writer.WriteBytes(reinterpret_cast<const void*>(decoder_buffer->data()),
+                        decoder_buffer->data_size());
+    }
+    return buffer;
+  }
+
+  // Reset buffer since serialization of the data failed.
+  LOG(ERROR) << "Not able to convert ::media::DecoderBuffer to byte array";
+  buffer.clear();
+  return buffer;
+}
+
+void ConvertEncryptionSchemeToProto(
+    const ::media::EncryptionScheme& encryption_scheme,
+    pb::EncryptionScheme* message) {
+  DCHECK(message);
+  message->set_mode(
+      ToProtoEncryptionSchemeCipherMode(encryption_scheme.mode()).value());
+  message->set_encrypt_blocks(encryption_scheme.pattern().encrypt_blocks());
+  message->set_skip_blocks(encryption_scheme.pattern().skip_blocks());
+}
+
+::media::EncryptionScheme ConvertProtoToEncryptionScheme(
+    const pb::EncryptionScheme& message) {
+  return ::media::EncryptionScheme(
+      ToMediaEncryptionSchemeCipherMode(message.mode()).value(),
+      ::media::EncryptionScheme::Pattern(message.encrypt_blocks(),
+                                         message.skip_blocks()));
+}
+
+void ConvertAudioDecoderConfigToProto(
+    const ::media::AudioDecoderConfig& audio_config,
+    pb::AudioDecoderConfig* audio_message) {
+  DCHECK(audio_config.IsValidConfig());
+  DCHECK(audio_message);
+
+  audio_message->set_codec(
+      ToProtoAudioDecoderConfigCodec(audio_config.codec()).value());
+  audio_message->set_sample_format(
+      ToProtoAudioDecoderConfigSampleFormat(audio_config.sample_format())
+          .value());
+  audio_message->set_channel_layout(
+      ToProtoAudioDecoderConfigChannelLayout(audio_config.channel_layout())
+          .value());
+  audio_message->set_samples_per_second(audio_config.samples_per_second());
+  audio_message->set_seek_preroll_usec(
+      audio_config.seek_preroll().InMicroseconds());
+  audio_message->set_codec_delay(audio_config.codec_delay());
+
+  if (!audio_config.extra_data().empty()) {
+    audio_message->set_extra_data(audio_config.extra_data().data(),
+                                  audio_config.extra_data().size());
+  }
+
+  if (audio_config.is_encrypted()) {
+    pb::EncryptionScheme* encryption_scheme_message =
+        audio_message->mutable_encryption_scheme();
+    ConvertEncryptionSchemeToProto(audio_config.encryption_scheme(),
+                                   encryption_scheme_message);
+  }
+}
+
+bool ConvertProtoToAudioDecoderConfig(
+    const pb::AudioDecoderConfig& audio_message,
+    ::media::AudioDecoderConfig* audio_config) {
+  DCHECK(audio_config);
+  audio_config->Initialize(
+      ToMediaAudioCodec(audio_message.codec()).value(),
+      ToMediaSampleFormat(audio_message.sample_format()).value(),
+      ToMediaChannelLayout(audio_message.channel_layout()).value(),
+      audio_message.samples_per_second(),
+      std::vector<uint8_t>(audio_message.extra_data().begin(),
+                           audio_message.extra_data().end()),
+      ConvertProtoToEncryptionScheme(audio_message.encryption_scheme()),
+      base::TimeDelta::FromMicroseconds(audio_message.seek_preroll_usec()),
+      audio_message.codec_delay());
+  return audio_config->IsValidConfig();
+}
+
+void ConvertVideoDecoderConfigToProto(
+    const ::media::VideoDecoderConfig& video_config,
+    pb::VideoDecoderConfig* video_message) {
+  DCHECK(video_config.IsValidConfig());
+  DCHECK(video_message);
+
+  video_message->set_codec(
+      ToProtoVideoDecoderConfigCodec(video_config.codec()).value());
+  video_message->set_profile(
+      ToProtoVideoDecoderConfigProfile(video_config.profile()).value());
+  video_message->set_format(
+      ToProtoVideoDecoderConfigFormat(video_config.format()).value());
+  video_message->set_color_space(
+      ToProtoVideoDecoderConfigColorSpace(video_config.color_space()).value());
+
+  pb::Size* coded_size_message = video_message->mutable_coded_size();
+  coded_size_message->set_width(video_config.coded_size().width());
+  coded_size_message->set_height(video_config.coded_size().height());
+
+  pb::Rect* visible_rect_message = video_message->mutable_visible_rect();
+  visible_rect_message->set_x(video_config.visible_rect().x());
+  visible_rect_message->set_y(video_config.visible_rect().y());
+  visible_rect_message->set_width(video_config.visible_rect().width());
+  visible_rect_message->set_height(video_config.visible_rect().height());
+
+  pb::Size* natural_size_message = video_message->mutable_natural_size();
+  natural_size_message->set_width(video_config.natural_size().width());
+  natural_size_message->set_height(video_config.natural_size().height());
+
+  if (!video_config.extra_data().empty()) {
+    video_message->set_extra_data(video_config.extra_data().data(),
+                                  video_config.extra_data().size());
+  }
+
+  if (video_config.is_encrypted()) {
+    pb::EncryptionScheme* encryption_scheme_message =
+        video_message->mutable_encryption_scheme();
+    ConvertEncryptionSchemeToProto(video_config.encryption_scheme(),
+                                   encryption_scheme_message);
+  }
+}
+
+bool ConvertProtoToVideoDecoderConfig(
+    const pb::VideoDecoderConfig& video_message,
+    ::media::VideoDecoderConfig* video_config) {
+  DCHECK(video_config);
+  ::media::EncryptionScheme encryption_scheme;
+  video_config->Initialize(
+      ToMediaVideoCodec(video_message.codec()).value(),
+      ToMediaVideoCodecProfile(video_message.profile()).value(),
+      ToMediaVideoPixelFormat(video_message.format()).value(),
+      ToMediaColorSpace(video_message.color_space()).value(),
+      gfx::Size(video_message.coded_size().width(),
+                video_message.coded_size().height()),
+      gfx::Rect(video_message.visible_rect().x(),
+                video_message.visible_rect().y(),
+                video_message.visible_rect().width(),
+                video_message.visible_rect().height()),
+      gfx::Size(video_message.natural_size().width(),
+                video_message.natural_size().height()),
+      std::vector<uint8_t>(video_message.extra_data().begin(),
+                           video_message.extra_data().end()),
+      ConvertProtoToEncryptionScheme(video_message.encryption_scheme()));
+  return video_config->IsValidConfig();
+}
+
+void ConvertCdmKeyInfoToProto(
+    const ::media::CdmKeysInfo& keys_information,
+    pb::CdmClientOnSessionKeysChange* key_change_message) {
+  for (const auto& info : keys_information) {
+    pb::CdmKeyInformation* key = key_change_message->add_key_information();
+    key->set_key_id(info->key_id.data(), info->key_id.size());
+    key->set_status(ToProtoCdmKeyInformation(info->status).value());
+    key->set_system_code(info->system_code);
+  }
+}
+
+void ConvertProtoToCdmKeyInfo(
+    const pb::CdmClientOnSessionKeysChange keychange_message,
+    CdmKeysInfo* key_information) {
+  DCHECK(key_information);
+  key_information->reserve(keychange_message.key_information_size());
+  for (int i = 0; i < keychange_message.key_information_size(); ++i) {
+    const pb::CdmKeyInformation key_info_msg =
+        keychange_message.key_information(i);
+
+    std::unique_ptr<::media::CdmKeyInformation> key(
+        new ::media::CdmKeyInformation(
+            key_info_msg.key_id(),
+            ToMediaCdmKeyInformationKeyStatus(key_info_msg.status()).value(),
+            key_info_msg.system_code()));
+    key_information->push_back(std::move(key));
+  }
+}
+
+void ConvertCdmPromiseToProto(const CdmPromiseResult& result,
+                              pb::CdmPromise* promise_message) {
+  promise_message->set_success(result.success());
+  if (!result.success()) {
+    promise_message->set_exception(
+        ToProtoMediaKeysException(result.exception()).value());
+    promise_message->set_system_code(result.system_code());
+    promise_message->set_error_message(result.error_message());
+  }
+}
+
+void ConvertCdmPromiseWithSessionIdToProto(const CdmPromiseResult& result,
+                                           const std::string& session_id,
+                                           pb::CdmPromise* promise_message) {
+  ConvertCdmPromiseToProto(result, promise_message);
+  promise_message->set_session_id(session_id);
+}
+
+void ConvertCdmPromiseWithCdmIdToProto(const CdmPromiseResult& result,
+                                       int cdm_id,
+                                       pb::CdmPromise* promise_message) {
+  ConvertCdmPromiseToProto(result, promise_message);
+  promise_message->set_cdm_id(cdm_id);
+}
+
+bool ConvertProtoToCdmPromise(const pb::CdmPromise& promise_message,
+                              CdmPromiseResult* result) {
+  if (!promise_message.has_success())
+    return false;
+
+  bool success = promise_message.success();
+  if (success) {
+    *result = CdmPromiseResult::SuccessResult();
+    return true;
+  }
+
+  ::media::MediaKeys::Exception exception = ::media::MediaKeys::UNKNOWN_ERROR;
+  uint32_t system_code = 0;
+  std::string error_message;
+
+  exception = ToMediaMediaKeysException(promise_message.exception()).value();
+  system_code = promise_message.system_code();
+  error_message = promise_message.error_message();
+  *result = CdmPromiseResult(exception, system_code, error_message);
+  return true;
+}
+
+bool ConvertProtoToCdmPromiseWithCdmIdSessionId(const pb::RpcMessage& message,
+                                                CdmPromiseResult* result,
+                                                int* cdm_id,
+                                                std::string* session_id) {
+  if (!message.has_cdm_promise_rpc())
+    return false;
+
+  const auto& promise_message = message.cdm_promise_rpc();
+  if (!ConvertProtoToCdmPromise(promise_message, result))
+    return false;
+
+  if (cdm_id)
+    *cdm_id = promise_message.cdm_id();
+  if (session_id)
+    *session_id = promise_message.session_id();
+
+  return true;
+}
+
+//==============================================================================
+CdmPromiseResult::CdmPromiseResult()
+    : CdmPromiseResult(::media::MediaKeys::UNKNOWN_ERROR, 0, "") {}
+
+CdmPromiseResult::CdmPromiseResult(::media::MediaKeys::Exception exception,
+                                   uint32_t system_code,
+                                   std::string error_message)
+    : success_(false),
+      exception_(exception),
+      system_code_(system_code),
+      error_message_(error_message) {}
+
+CdmPromiseResult::CdmPromiseResult(const CdmPromiseResult& other) = default;
+
+CdmPromiseResult::~CdmPromiseResult() = default;
+
+CdmPromiseResult CdmPromiseResult::SuccessResult() {
+  CdmPromiseResult result(static_cast<::media::MediaKeys::Exception>(0), 0, "");
+  result.success_ = true;
+  return result;
+}
+
+}  // namespace remoting
+}  // namespace media
diff --git a/media/remoting/rpc/proto_utils.h b/media/remoting/rpc/proto_utils.h
new file mode 100644
index 0000000..6bba860a
--- /dev/null
+++ b/media/remoting/rpc/proto_utils.h
@@ -0,0 +1,137 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_REMOTING_RPC_PROTO_UTILS_H_
+#define MEDIA_REMOTING_RPC_PROTO_UTILS_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/cdm_config.h"
+#include "media/base/cdm_key_information.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/eme_constants.h"
+#include "media/base/media_keys.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/video_decoder_config.h"
+#include "media/remoting/remoting_rpc_message.pb.h"
+
+namespace media {
+namespace remoting {
+
+class CdmPromiseResult;
+
+// Predefined invalid handle value RPC message.
+constexpr int kInvalidHandle = -1;
+
+// Predefined handle value for RPC messages related to initialization.
+constexpr int kReceiverHandle = 0;
+
+// Utility class to convert data between ::media::DecoderBuffer and byte array.
+// It is to serialize ::media::DecoderBuffer structure except for actual data
+// into pb::DecoderBuffer followed by byte array of decoder buffer. The reason
+// data is not part of proto buffer because it would cost unnecessary time to
+// wait for whole proto received before conversion given the fact that decoder
+// buffer data can vary from hundred bytes to 3~5MB. Also, it would costs extra
+// CPU to sirealize/de-serialize decoder buffer which is encoded and encrypted
+// as wire format for data transmission.
+//
+// DecoderBufferSegment {
+//  // Payload version. Default value is 0.
+//  u8 payload_version;
+//
+//  // Length of pb::DecoderBuffer (protobuf-encoded of ::media::DecoderBuffer
+//                   except for data).
+//  u16 buffer_segment_size;
+//  // pb::DecoderBuffer.
+//  u8[buffer_segment_size] buffer_segment;
+//
+//  // Length of data in media::DecoderBuffer.
+//  u32 data_buffer_size;
+//  // media::DecoderBuffer data.
+//  u8[data_buffer_size] data_buffer;
+//};
+
+// Converts DecoderBufferSegment into byte array.
+std::vector<uint8_t> DecoderBufferToByteArray(
+    const scoped_refptr<::media::DecoderBuffer>& decoder_buffer);
+
+// Converts byte array into DecoderBufferSegment.
+scoped_refptr<::media::DecoderBuffer> ByteArrayToDecoderBuffer(
+    const uint8_t* data,
+    uint32_t size);
+
+// Data type conversion between ::media::AudioDecoderConfig and proto buffer.
+void ConvertAudioDecoderConfigToProto(
+    const ::media::AudioDecoderConfig& audio_config,
+    pb::AudioDecoderConfig* audio_message);
+bool ConvertProtoToAudioDecoderConfig(
+    const pb::AudioDecoderConfig& audio_message,
+    ::media::AudioDecoderConfig* audio_config);
+
+// Data type conversion between ::media::VideoDecoderConfig and proto buffer.
+void ConvertVideoDecoderConfigToProto(
+    const ::media::VideoDecoderConfig& video_config,
+    pb::VideoDecoderConfig* video_message);
+bool ConvertProtoToVideoDecoderConfig(
+    const pb::VideoDecoderConfig& video_message,
+    ::media::VideoDecoderConfig* video_config);
+
+// Data type conversion between ::media::CdmKeysInfo and proto buffer.
+void ConvertCdmKeyInfoToProto(
+    const ::media::CdmKeysInfo& keys_information,
+    pb::CdmClientOnSessionKeysChange* key_change_message);
+void ConvertProtoToCdmKeyInfo(
+    const pb::CdmClientOnSessionKeysChange keychange_message,
+    CdmKeysInfo* key_information);
+
+// Data type conversion between CdmPromiseResult and proto buffer.
+void ConvertCdmPromiseToProto(const CdmPromiseResult& result,
+                              pb::CdmPromise* promise_message);
+void ConvertCdmPromiseWithSessionIdToProto(const CdmPromiseResult& result,
+                                           const std::string& session_id,
+                                           pb::CdmPromise* promise_message);
+void ConvertCdmPromiseWithCdmIdToProto(const CdmPromiseResult& result,
+                                       int cdm_id,
+                                       pb::CdmPromise* promise_message);
+bool ConvertProtoToCdmPromise(const pb::CdmPromise& promise_message,
+                              CdmPromiseResult* result);
+bool ConvertProtoToCdmPromiseWithCdmIdSessionId(const pb::RpcMessage& message,
+                                                CdmPromiseResult* result,
+                                                int* cdm_id,
+                                                std::string* session_id);
+
+//==================================================================
+class CdmPromiseResult {
+ public:
+  CdmPromiseResult();
+  CdmPromiseResult(::media::MediaKeys::Exception exception,
+                   uint32_t system_code,
+                   std::string error_message);
+  CdmPromiseResult(const CdmPromiseResult& other);
+  ~CdmPromiseResult();
+
+  static CdmPromiseResult SuccessResult();
+
+  bool success() const { return success_; }
+  ::media::MediaKeys::Exception exception() const { return exception_; }
+  uint32_t system_code() const { return system_code_; }
+  const std::string& error_message() const { return error_message_; }
+
+ private:
+  bool success_;
+  ::media::MediaKeys::Exception exception_;
+  uint32_t system_code_;
+  std::string error_message_;
+};
+
+}  // namespace remoting
+}  // namespace media
+
+#endif  // MEDIA_REMOTING_RPC_PROTO_UTILS_H_
diff --git a/media/remoting/rpc/proto_utils_unittest.cc b/media/remoting/rpc/proto_utils_unittest.cc
new file mode 100644
index 0000000..4f78c96
--- /dev/null
+++ b/media/remoting/rpc/proto_utils_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/rpc/proto_utils.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/cdm_config.h"
+#include "media/base/cdm_key_information.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/eme_constants.h"
+#include "media/base/media_keys.h"
+#include "media/base/video_decoder_config.h"
+#include "media/remoting/remoting_rpc_message.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+
+namespace media {
+namespace remoting {
+
+class ProtoUtilsTest : public testing::Test {
+ protected:
+  void SetUp() override {}
+};
+
+TEST_F(ProtoUtilsTest, PassEOSDecoderBuffer) {
+  // 1. To DecoderBuffer
+  scoped_refptr<::media::DecoderBuffer> input_buffer =
+      ::media::DecoderBuffer::CreateEOSBuffer();
+
+  // 2. To Byte Array
+  std::vector<uint8_t> data = DecoderBufferToByteArray(input_buffer);
+
+  // 3. To DecoderBuffer
+  scoped_refptr<::media::DecoderBuffer> output_buffer =
+      ByteArrayToDecoderBuffer(data.data(), data.size());
+  DCHECK(output_buffer);
+
+  ASSERT_TRUE(output_buffer->end_of_stream());
+}
+
+TEST_F(ProtoUtilsTest, PassValidDecoderBuffer) {
+  const uint8_t buffer[] = {
+      0,   0,   0,   1,   9,   224, 0,   0,   0,   1,   103, 77,  64,  21,  217,
+      1,   177, 254, 78,  16,  0,   0,   62,  144, 0,   11,  184, 0,   241, 98,
+      228, 128, 0,   0,   0,   1,   104, 235, 143, 32,  0,   0,   0,   1,   103,
+      77,  64,  21,  217, 1,   177, 254, 78,  16,  0,   0,   62,  144, 0,   11,
+      184, 0,   241, 98,  228, 128, 0,   0,   0,   1,   104, 235, 143, 32,  0,
+      0,   0,   1,   101, 136, 132, 25,  255, 0,   191, 98,  0,   6,   29,  63,
+      252, 65,  246, 207, 255, 235, 63,  172, 35,  112, 198, 115, 222, 243, 159,
+      232, 208, 32,  0,   0,   3,   0,   0,   203, 255, 149, 20,  71,  203, 213,
+      40,  0,   0,   139, 0,   24,  117, 166, 249, 227, 68,  230, 177, 134, 161,
+      162, 1,   22,  105, 78,  66,  183, 130, 158, 108, 252, 112, 113, 58,  159,
+      72,  116, 78,  141, 133, 76,  225, 209, 13,  221, 49,  187, 83,  123, 193,
+      112, 123, 112, 74,  121, 133};
+  size_t buffer_size = sizeof(buffer) / sizeof(uint8_t);
+  const uint8_t side_buffer[] = "XX";
+  size_t side_buffer_size = sizeof(side_buffer) / sizeof(uint8_t);
+  base::TimeDelta pts = base::TimeDelta::FromMilliseconds(5);
+
+  // 1. To DecoderBuffer
+  scoped_refptr<::media::DecoderBuffer> input_buffer =
+      ::media::DecoderBuffer::CopyFrom(buffer, buffer_size, side_buffer,
+                                       side_buffer_size);
+  input_buffer->set_timestamp(pts);
+  input_buffer->set_is_key_frame(true);
+
+  // 2. To Byte Array
+  std::vector<uint8_t> data = DecoderBufferToByteArray(input_buffer);
+
+  // 3. To DecoderBuffer
+  scoped_refptr<::media::DecoderBuffer> output_buffer =
+      ByteArrayToDecoderBuffer(data.data(), data.size());
+  DCHECK(output_buffer);
+
+  ASSERT_FALSE(output_buffer->end_of_stream());
+  ASSERT_TRUE(output_buffer->is_key_frame());
+  ASSERT_EQ(output_buffer->timestamp(), pts);
+  ASSERT_EQ(output_buffer->data_size(), buffer_size);
+  const uint8_t* output_data = output_buffer->data();
+  for (size_t i = 0; i < buffer_size; i++) {
+    ASSERT_EQ(output_data[i], buffer[i]);
+  }
+  ASSERT_EQ(output_buffer->side_data_size(), side_buffer_size);
+  const uint8_t* output_side_data = output_buffer->side_data();
+  for (size_t i = 0; i < side_buffer_size; i++) {
+    ASSERT_EQ(output_side_data[i], side_buffer[i]);
+  }
+}
+
+TEST_F(ProtoUtilsTest, AudioDecoderConfigConversionTest) {
+  const std::string extra_data = "ACEG";
+  const EncryptionScheme encryption_scheme(
+      EncryptionScheme::CIPHER_MODE_AES_CTR, EncryptionScheme::Pattern(20, 40));
+  AudioDecoderConfig audio_config(
+      kCodecAAC, kSampleFormatF32, CHANNEL_LAYOUT_MONO, 48000,
+      std::vector<uint8_t>(extra_data.begin(), extra_data.end()),
+      encryption_scheme);
+  ASSERT_TRUE(audio_config.IsValidConfig());
+
+  pb::AudioDecoderConfig audio_message;
+  ConvertAudioDecoderConfigToProto(audio_config, &audio_message);
+
+  AudioDecoderConfig audio_output_config;
+  ASSERT_TRUE(
+      ConvertProtoToAudioDecoderConfig(audio_message, &audio_output_config));
+
+  ASSERT_TRUE(audio_config.Matches(audio_output_config));
+}
+
+TEST_F(ProtoUtilsTest, CdmPromiseResultConversion) {
+  CdmPromiseResult success_result = CdmPromiseResult::SuccessResult();
+
+  pb::CdmPromise promise_message;
+  ConvertCdmPromiseToProto(success_result, &promise_message);
+
+  CdmPromiseResult output_result;
+  ASSERT_TRUE(ConvertProtoToCdmPromise(promise_message, &output_result));
+
+  ASSERT_EQ(success_result.success(), output_result.success());
+  ASSERT_EQ(success_result.exception(), output_result.exception());
+  ASSERT_EQ(success_result.system_code(), output_result.system_code());
+  ASSERT_EQ(success_result.error_message(), output_result.error_message());
+}
+
+TEST_F(ProtoUtilsTest, CdmKeyInformationConversion) {
+  std::unique_ptr<CdmKeyInformation> cdm_key_info_1(new CdmKeyInformation(
+      "key_1", CdmKeyInformation::OUTPUT_RESTRICTED, 100));
+  std::unique_ptr<CdmKeyInformation> cdm_key_info_2(
+      new CdmKeyInformation("key_2", CdmKeyInformation::EXPIRED, 11));
+  std::unique_ptr<CdmKeyInformation> cdm_key_info_3(
+      new CdmKeyInformation("key_3", CdmKeyInformation::RELEASED, 22));
+  CdmKeysInfo keys_information;
+  keys_information.push_back(std::move(cdm_key_info_1));
+  keys_information.push_back(std::move(cdm_key_info_2));
+  keys_information.push_back(std::move(cdm_key_info_3));
+
+  pb::CdmClientOnSessionKeysChange key_message;
+  ConvertCdmKeyInfoToProto(keys_information, &key_message);
+
+  CdmKeysInfo key_output_information;
+  ConvertProtoToCdmKeyInfo(key_message, &key_output_information);
+
+  ASSERT_EQ(keys_information.size(), key_output_information.size());
+  for (uint32_t i = 0; i < 3; i++) {
+    ASSERT_EQ(keys_information[i]->key_id, key_output_information[i]->key_id);
+    ASSERT_EQ(keys_information[i]->status, key_output_information[i]->status);
+    ASSERT_EQ(keys_information[i]->system_code,
+              key_output_information[i]->system_code);
+  }
+}
+
+}  // namespace remoting
+}  // namespace media