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/transport/cast_transport_config.h"
     22 #include "media/cast/transport/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_);
     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()));
     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()),
     81       create_vea_cb,
     82       create_video_encode_mem_cb);
     83 }
     84 
     85 void CastSessionDelegate::StartUDP(const net::IPEndPoint& remote_endpoint) {
     86   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
     87 
     88   // CastSender uses the renderer's IO thread as the main thread. This reduces
     89   // thread hopping for incoming video frames and outgoing network packets.
     90   cast_environment_ = new CastEnvironment(
     91       scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
     92       base::MessageLoopProxy::current(),
     93       g_cast_threads.Get().GetAudioEncodeMessageLoopProxy(),
     94       g_cast_threads.Get().GetVideoEncodeMessageLoopProxy());
     95 
     96   event_subscribers_.reset(
     97       new media::cast::RawEventSubscriberBundle(cast_environment_));
     98 
     99   // Rationale for using unretained: The callback cannot be called after the
    100   // destruction of CastTransportSenderIPC, and they both share the same thread.
    101   cast_transport_.reset(new CastTransportSenderIPC(
    102       remote_endpoint,
    103       base::Bind(&CastSessionDelegate::StatusNotificationCB,
    104                  base::Unretained(this)),
    105       base::Bind(&CastSessionDelegate::LogRawEvents, base::Unretained(this))));
    106 
    107   cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get());
    108   cast_transport_->SetPacketReceiver(cast_sender_->packet_receiver());
    109 }
    110 
    111 void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) {
    112   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    113   if (!event_subscribers_.get())
    114     return;
    115 
    116   if (enable)
    117     event_subscribers_->AddEventSubscribers(is_audio);
    118   else
    119     event_subscribers_->RemoveEventSubscribers(is_audio);
    120 }
    121 
    122 void CastSessionDelegate::GetEventLogsAndReset(
    123     bool is_audio,
    124     const std::string& extra_data,
    125     const EventLogsCallback& callback) {
    126   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    127 
    128   if (!event_subscribers_.get()) {
    129     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    130     return;
    131   }
    132 
    133   media::cast::EncodingEventSubscriber* subscriber =
    134       event_subscribers_->GetEncodingEventSubscriber(is_audio);
    135   if (!subscriber) {
    136     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    137     return;
    138   }
    139 
    140   media::cast::proto::LogMetadata metadata;
    141   media::cast::FrameEventList frame_events;
    142   media::cast::PacketEventList packet_events;
    143 
    144   subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events);
    145 
    146   if (!extra_data.empty())
    147     metadata.set_extra_data(extra_data);
    148   media::cast::proto::GeneralDescription* gen_desc =
    149       metadata.mutable_general_description();
    150   chrome::VersionInfo version_info;
    151   gen_desc->set_product(version_info.Name());
    152   gen_desc->set_product_version(version_info.Version());
    153   gen_desc->set_os(version_info.OSType());
    154 
    155   scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
    156   int output_bytes;
    157   bool success = media::cast::SerializeEvents(metadata,
    158                                               frame_events,
    159                                               packet_events,
    160                                               true,
    161                                               media::cast::kMaxSerializedBytes,
    162                                               serialized_log.get(),
    163                                               &output_bytes);
    164 
    165   if (!success) {
    166     VLOG(2) << "Failed to serialize event log.";
    167     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
    168     return;
    169   }
    170 
    171   DVLOG(2) << "Serialized log length: " << output_bytes;
    172 
    173   scoped_ptr<base::BinaryValue> blob(
    174       new base::BinaryValue(serialized_log.Pass(), output_bytes));
    175   callback.Run(blob.Pass());
    176 }
    177 
    178 void CastSessionDelegate::GetStatsAndReset(bool is_audio,
    179                                            const StatsCallback& callback) {
    180   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    181 
    182   if (!event_subscribers_.get()) {
    183     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
    184     return;
    185   }
    186 
    187   media::cast::StatsEventSubscriber* subscriber =
    188       event_subscribers_->GetStatsEventSubscriber(is_audio);
    189   if (!subscriber) {
    190     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
    191     return;
    192   }
    193 
    194   scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
    195   subscriber->Reset();
    196 
    197   callback.Run(stats.Pass());
    198 }
    199 
    200 void CastSessionDelegate::StatusNotificationCB(
    201     media::cast::transport::CastTransportStatus unused_status) {
    202   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    203   // TODO(hubbe): Call javascript UDPTransport error function.
    204 }
    205 
    206 void CastSessionDelegate::InitializationResultCB(
    207     media::cast::CastInitializationStatus result) const {
    208   DCHECK(cast_sender_);
    209 
    210   // TODO(pwestin): handle the error codes.
    211   if (result == media::cast::STATUS_AUDIO_INITIALIZED) {
    212     audio_frame_input_available_callback_.Run(
    213         cast_sender_->audio_frame_input());
    214   } else if (result == media::cast::STATUS_VIDEO_INITIALIZED) {
    215     video_frame_input_available_callback_.Run(
    216         cast_sender_->video_frame_input());
    217   }
    218 }
    219 
    220 void CastSessionDelegate::LogRawEvents(
    221     const std::vector<media::cast::PacketEvent>& packet_events) {
    222   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    223 
    224   for (std::vector<media::cast::PacketEvent>::const_iterator it =
    225            packet_events.begin();
    226        it != packet_events.end();
    227        ++it) {
    228     cast_environment_->Logging()->InsertPacketEvent(it->timestamp,
    229                                                     it->type,
    230                                                     it->media_type,
    231                                                     it->rtp_timestamp,
    232                                                     it->frame_id,
    233                                                     it->packet_id,
    234                                                     it->max_packet_id,
    235                                                     it->size);
    236   }
    237 }
    238