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