Home | History | Annotate | Download | only in media
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/renderer/media/cast_session_delegate.h"
      6 
      7 #include "base/lazy_instance.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "chrome/common/chrome_version_info.h"
     11 #include "chrome/renderer/media/cast_threads.h"
     12 #include "chrome/renderer/media/cast_transport_sender_ipc.h"
     13 #include "content/public/renderer/render_thread.h"
     14 #include "media/cast/cast_config.h"
     15 #include "media/cast/cast_environment.h"
     16 #include "media/cast/cast_sender.h"
     17 #include "media/cast/logging/log_serializer.h"
     18 #include "media/cast/logging/logging_defines.h"
     19 #include "media/cast/logging/proto/raw_events.pb.h"
     20 #include "media/cast/logging/raw_event_subscriber_bundle.h"
     21 #include "media/cast/net/cast_transport_config.h"
     22 #include "media/cast/net/cast_transport_sender.h"
     23 
     24 using media::cast::AudioSenderConfig;
     25 using media::cast::CastEnvironment;
     26 using media::cast::CastSender;
     27 using media::cast::VideoSenderConfig;
     28 
     29 static base::LazyInstance<CastThreads> g_cast_threads =
     30     LAZY_INSTANCE_INITIALIZER;
     31 
     32 CastSessionDelegate::CastSessionDelegate()
     33     : io_message_loop_proxy_(
     34           content::RenderThread::Get()->GetIOMessageLoopProxy()),
     35       weak_factory_(this) {
     36   DCHECK(io_message_loop_proxy_.get());
     37 }
     38 
     39 CastSessionDelegate::~CastSessionDelegate() {
     40   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
     41 }
     42 
     43 void CastSessionDelegate::StartAudio(
     44     const AudioSenderConfig& config,
     45     const AudioFrameInputAvailableCallback& callback,
     46     const ErrorCallback& error_callback) {
     47   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
     48 
     49   if (!cast_transport_ || !cast_sender_) {
     50     error_callback.Run("Destination not set.");
     51     return;
     52   }
     53 
     54   audio_frame_input_available_callback_ = callback;
     55   cast_sender_->InitializeAudio(
     56       config,
     57       base::Bind(&CastSessionDelegate::InitializationResultCB,
     58                  weak_factory_.GetWeakPtr(), error_callback));
     59 }
     60 
     61 void CastSessionDelegate::StartVideo(
     62     const VideoSenderConfig& config,
     63     const VideoFrameInputAvailableCallback& callback,
     64     const ErrorCallback& error_callback,
     65     const media::cast::CreateVideoEncodeAcceleratorCallback& create_vea_cb,
     66     const media::cast::CreateVideoEncodeMemoryCallback&
     67         create_video_encode_mem_cb) {
     68   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
     69 
     70   if (!cast_transport_ || !cast_sender_) {
     71     error_callback.Run("Destination not set.");
     72     return;
     73   }
     74 
     75   video_frame_input_available_callback_ = callback;
     76 
     77   cast_sender_->InitializeVideo(
     78       config,
     79       base::Bind(&CastSessionDelegate::InitializationResultCB,
     80                  weak_factory_.GetWeakPtr(), error_callback),
     81       create_vea_cb,
     82       create_video_encode_mem_cb);
     83 }
     84 
     85 void CastSessionDelegate::StartUDP(const net::IPEndPoint& remote_endpoint,
     86                                    scoped_ptr<base::DictionaryValue> options) {
     87   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
     88 
     89   // CastSender uses the renderer's IO thread as the main thread. This reduces
     90   // thread hopping for incoming video frames and outgoing network packets.
     91   cast_environment_ = new CastEnvironment(
     92       scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
     93       base::MessageLoopProxy::current(),
     94       g_cast_threads.Get().GetAudioEncodeMessageLoopProxy(),
     95       g_cast_threads.Get().GetVideoEncodeMessageLoopProxy());
     96 
     97   event_subscribers_.reset(
     98       new media::cast::RawEventSubscriberBundle(cast_environment_));
     99 
    100   // Rationale for using unretained: The callback cannot be called after the
    101   // destruction of CastTransportSenderIPC, and they both share the same thread.
    102   cast_transport_.reset(new CastTransportSenderIPC(
    103       remote_endpoint,
    104       options.Pass(),
    105       base::Bind(&CastSessionDelegate::StatusNotificationCB,
    106                  base::Unretained(this)),
    107       base::Bind(&CastSessionDelegate::LogRawEvents, base::Unretained(this))));
    108 
    109   cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get());
    110 }
    111 
    112 void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) {
    113   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    114   if (!event_subscribers_.get())
    115     return;
    116 
    117   if (enable)
    118     event_subscribers_->AddEventSubscribers(is_audio);
    119   else
    120     event_subscribers_->RemoveEventSubscribers(is_audio);
    121 }
    122 
    123 void CastSessionDelegate::GetEventLogsAndReset(
    124     bool is_audio,
    125     const std::string& extra_data,
    126     const EventLogsCallback& callback) {
    127   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    128 
    129   if (!event_subscribers_.get()) {
    130     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    131     return;
    132   }
    133 
    134   media::cast::EncodingEventSubscriber* subscriber =
    135       event_subscribers_->GetEncodingEventSubscriber(is_audio);
    136   if (!subscriber) {
    137     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    138     return;
    139   }
    140 
    141   media::cast::proto::LogMetadata metadata;
    142   media::cast::FrameEventList frame_events;
    143   media::cast::PacketEventList packet_events;
    144 
    145   subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events);
    146 
    147   if (!extra_data.empty())
    148     metadata.set_extra_data(extra_data);
    149   media::cast::proto::GeneralDescription* gen_desc =
    150       metadata.mutable_general_description();
    151   chrome::VersionInfo version_info;
    152   gen_desc->set_product(version_info.Name());
    153   gen_desc->set_product_version(version_info.Version());
    154   gen_desc->set_os(version_info.OSType());
    155 
    156   scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
    157   int output_bytes;
    158   bool success = media::cast::SerializeEvents(metadata,
    159                                               frame_events,
    160                                               packet_events,
    161                                               true,
    162                                               media::cast::kMaxSerializedBytes,
    163                                               serialized_log.get(),
    164                                               &output_bytes);
    165 
    166   if (!success) {
    167     VLOG(2) << "Failed to serialize event log.";
    168     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    169     return;
    170   }
    171 
    172   DVLOG(2) << "Serialized log length: " << output_bytes;
    173 
    174   scoped_ptr<base::BinaryValue> blob(
    175       new base::BinaryValue(serialized_log.Pass(), output_bytes));
    176   callback.Run(blob.Pass());
    177 }
    178 
    179 void CastSessionDelegate::GetStatsAndReset(bool is_audio,
    180                                            const StatsCallback& callback) {
    181   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    182 
    183   if (!event_subscribers_.get()) {
    184     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
    185     return;
    186   }
    187 
    188   media::cast::StatsEventSubscriber* subscriber =
    189       event_subscribers_->GetStatsEventSubscriber(is_audio);
    190   if (!subscriber) {
    191     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
    192     return;
    193   }
    194 
    195   scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
    196   subscriber->Reset();
    197 
    198   callback.Run(stats.Pass());
    199 }
    200 
    201 void CastSessionDelegate::StatusNotificationCB(
    202     media::cast::CastTransportStatus unused_status) {
    203   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    204   // TODO(hubbe): Call javascript UDPTransport error function.
    205 }
    206 
    207 void CastSessionDelegate::InitializationResultCB(
    208     const ErrorCallback& error_callback,
    209     media::cast::CastInitializationStatus result) const {
    210   DCHECK(cast_sender_);
    211 
    212   switch (result) {
    213     case media::cast::STATUS_AUDIO_INITIALIZED:
    214       audio_frame_input_available_callback_.Run(
    215           cast_sender_->audio_frame_input());
    216       break;
    217     case media::cast::STATUS_VIDEO_INITIALIZED:
    218       video_frame_input_available_callback_.Run(
    219           cast_sender_->video_frame_input());
    220       break;
    221     case media::cast::STATUS_INVALID_CAST_ENVIRONMENT:
    222       error_callback.Run("Invalid cast environment.");
    223       break;
    224     case media::cast::STATUS_INVALID_CRYPTO_CONFIGURATION:
    225       error_callback.Run("Invalid encryption keys.");
    226       break;
    227     case media::cast::STATUS_UNSUPPORTED_AUDIO_CODEC:
    228       error_callback.Run("Audio codec not supported.");
    229       break;
    230     case media::cast::STATUS_UNSUPPORTED_VIDEO_CODEC:
    231       error_callback.Run("Video codec not supported.");
    232       break;
    233     case media::cast::STATUS_INVALID_AUDIO_CONFIGURATION:
    234       error_callback.Run("Invalid audio configuration.");
    235       break;
    236     case media::cast::STATUS_INVALID_VIDEO_CONFIGURATION:
    237       error_callback.Run("Invalid video configuration.");
    238       break;
    239     case media::cast::STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED:
    240       error_callback.Run("Hardware video encoder not supported.");
    241       break;
    242     case media::cast::STATUS_AUDIO_UNINITIALIZED:
    243     case media::cast::STATUS_VIDEO_UNINITIALIZED:
    244       NOTREACHED() << "Not an error.";
    245       break;
    246   }
    247 }
    248 
    249 void CastSessionDelegate::LogRawEvents(
    250     const std::vector<media::cast::PacketEvent>& packet_events,
    251     const std::vector<media::cast::FrameEvent>& frame_events) {
    252   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    253 
    254   for (std::vector<media::cast::PacketEvent>::const_iterator it =
    255            packet_events.begin();
    256        it != packet_events.end();
    257        ++it) {
    258     cast_environment_->Logging()->InsertPacketEvent(it->timestamp,
    259                                                     it->type,
    260                                                     it->media_type,
    261                                                     it->rtp_timestamp,
    262                                                     it->frame_id,
    263                                                     it->packet_id,
    264                                                     it->max_packet_id,
    265                                                     it->size);
    266   }
    267   for (std::vector<media::cast::FrameEvent>::const_iterator it =
    268            frame_events.begin();
    269        it != frame_events.end();
    270        ++it) {
    271     if (it->type == media::cast::FRAME_PLAYOUT) {
    272       cast_environment_->Logging()->InsertFrameEventWithDelay(
    273           it->timestamp,
    274           it->type,
    275           it->media_type,
    276           it->rtp_timestamp,
    277           it->frame_id,
    278           it->delay_delta);
    279     } else {
    280       cast_environment_->Logging()->InsertFrameEvent(
    281           it->timestamp,
    282           it->type,
    283           it->media_type,
    284           it->rtp_timestamp,
    285           it->frame_id);
    286     }
    287   }
    288 }
    289