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