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 // Test application that simulates a cast sender - Data can be either generated 6 // or read from a file. 7 8 #include <queue> 9 10 #include "base/at_exit.h" 11 #include "base/base_paths.h" 12 #include "base/command_line.h" 13 #include "base/files/file_path.h" 14 #include "base/json/json_writer.h" 15 #include "base/logging.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/path_service.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/threading/thread.h" 20 #include "base/time/default_tick_clock.h" 21 #include "base/values.h" 22 #include "media/base/media.h" 23 #include "media/base/video_frame.h" 24 #include "media/cast/cast_config.h" 25 #include "media/cast/cast_environment.h" 26 #include "media/cast/cast_sender.h" 27 #include "media/cast/logging/encoding_event_subscriber.h" 28 #include "media/cast/logging/log_serializer.h" 29 #include "media/cast/logging/logging_defines.h" 30 #include "media/cast/logging/proto/raw_events.pb.h" 31 #include "media/cast/logging/receiver_time_offset_estimator_impl.h" 32 #include "media/cast/logging/stats_event_subscriber.h" 33 #include "media/cast/net/cast_transport_defines.h" 34 #include "media/cast/net/cast_transport_sender.h" 35 #include "media/cast/net/udp_transport.h" 36 #include "media/cast/test/fake_media_source.h" 37 #include "media/cast/test/utility/default_config.h" 38 #include "media/cast/test/utility/input_builder.h" 39 40 namespace { 41 static const int kAudioChannels = 2; 42 static const int kAudioSamplingFrequency = 48000; 43 44 // The max allowed size of serialized log. 45 const int kMaxSerializedLogBytes = 10 * 1000 * 1000; 46 47 // Flags for this program: 48 // 49 // --address=xx.xx.xx.xx 50 // IP address of receiver. 51 // 52 // --port=xxxx 53 // Port number of receiver. 54 // 55 // --source-file=xxx.webm 56 // WebM file as source of video frames. 57 // 58 // --fps=xx 59 // Override framerate of the video stream. 60 const char kSwitchAddress[] = "address"; 61 const char kSwitchPort[] = "port"; 62 const char kSwitchSourceFile[] = "source-file"; 63 const char kSwitchFps[] = "fps"; 64 65 media::cast::AudioSenderConfig GetAudioSenderConfig() { 66 media::cast::AudioSenderConfig audio_config; 67 68 audio_config.use_external_encoder = false; 69 audio_config.frequency = kAudioSamplingFrequency; 70 audio_config.channels = kAudioChannels; 71 audio_config.bitrate = 0; // Use Opus auto-VBR mode. 72 audio_config.codec = media::cast::CODEC_AUDIO_OPUS; 73 audio_config.ssrc = 1; 74 audio_config.incoming_feedback_ssrc = 2; 75 audio_config.rtp_payload_type = 127; 76 // TODO(miu): The default in cast_defines.h is 100. Should this be 100, and 77 // should receiver.cc's config also be 100? 78 audio_config.max_playout_delay = base::TimeDelta::FromMilliseconds(300); 79 return audio_config; 80 } 81 82 media::cast::VideoSenderConfig GetVideoSenderConfig() { 83 media::cast::VideoSenderConfig video_config; 84 85 video_config.use_external_encoder = false; 86 87 // Resolution. 88 video_config.width = 1280; 89 video_config.height = 720; 90 video_config.max_frame_rate = 30; 91 92 // Bitrates. 93 video_config.max_bitrate = 2500000; 94 video_config.min_bitrate = 100000; 95 video_config.start_bitrate = video_config.min_bitrate; 96 97 // Codec. 98 video_config.codec = media::cast::CODEC_VIDEO_VP8; 99 video_config.max_number_of_video_buffers_used = 1; 100 video_config.number_of_encode_threads = 2; 101 102 // Quality options. 103 video_config.min_qp = 4; 104 video_config.max_qp = 40; 105 106 // SSRCs and payload type. Don't change them. 107 video_config.ssrc = 11; 108 video_config.incoming_feedback_ssrc = 12; 109 video_config.rtp_payload_type = 96; 110 // TODO(miu): The default in cast_defines.h is 100. Should this be 100, and 111 // should receiver.cc's config also be 100? 112 video_config.max_playout_delay = base::TimeDelta::FromMilliseconds(300); 113 return video_config; 114 } 115 116 void UpdateCastTransportStatus( 117 media::cast::CastTransportStatus status) { 118 VLOG(1) << "Transport status: " << status; 119 } 120 121 void LogRawEvents( 122 const scoped_refptr<media::cast::CastEnvironment>& cast_environment, 123 const std::vector<media::cast::PacketEvent>& packet_events, 124 const std::vector<media::cast::FrameEvent>& frame_events) { 125 VLOG(1) << "Got packet events from transport, size: " << packet_events.size(); 126 for (std::vector<media::cast::PacketEvent>::const_iterator it = 127 packet_events.begin(); 128 it != packet_events.end(); 129 ++it) { 130 cast_environment->Logging()->InsertPacketEvent(it->timestamp, 131 it->type, 132 it->media_type, 133 it->rtp_timestamp, 134 it->frame_id, 135 it->packet_id, 136 it->max_packet_id, 137 it->size); 138 } 139 VLOG(1) << "Got frame events from transport, size: " << frame_events.size(); 140 for (std::vector<media::cast::FrameEvent>::const_iterator it = 141 frame_events.begin(); 142 it != frame_events.end(); 143 ++it) { 144 cast_environment->Logging()->InsertFrameEvent(it->timestamp, 145 it->type, 146 it->media_type, 147 it->rtp_timestamp, 148 it->frame_id); 149 } 150 } 151 152 void InitializationResult(media::cast::CastInitializationStatus result) { 153 bool end_result = result == media::cast::STATUS_AUDIO_INITIALIZED || 154 result == media::cast::STATUS_VIDEO_INITIALIZED; 155 CHECK(end_result) << "Cast sender uninitialized"; 156 } 157 158 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { 159 net::IPAddressNumber ip_number; 160 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number)); 161 return net::IPEndPoint(ip_number, port); 162 } 163 164 void DumpLoggingData(const media::cast::proto::LogMetadata& log_metadata, 165 const media::cast::FrameEventList& frame_events, 166 const media::cast::PacketEventList& packet_events, 167 base::ScopedFILE log_file) { 168 VLOG(0) << "Frame map size: " << frame_events.size(); 169 VLOG(0) << "Packet map size: " << packet_events.size(); 170 171 scoped_ptr<char[]> event_log(new char[kMaxSerializedLogBytes]); 172 int event_log_bytes; 173 if (!media::cast::SerializeEvents(log_metadata, 174 frame_events, 175 packet_events, 176 true, 177 kMaxSerializedLogBytes, 178 event_log.get(), 179 &event_log_bytes)) { 180 VLOG(0) << "Failed to serialize events."; 181 return; 182 } 183 184 VLOG(0) << "Events serialized length: " << event_log_bytes; 185 186 int ret = fwrite(event_log.get(), 1, event_log_bytes, log_file.get()); 187 if (ret != event_log_bytes) 188 VLOG(0) << "Failed to write logs to file."; 189 } 190 191 void WriteLogsToFileAndDestroySubscribers( 192 const scoped_refptr<media::cast::CastEnvironment>& cast_environment, 193 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber, 194 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber, 195 base::ScopedFILE video_log_file, 196 base::ScopedFILE audio_log_file) { 197 cast_environment->Logging()->RemoveRawEventSubscriber( 198 video_event_subscriber.get()); 199 cast_environment->Logging()->RemoveRawEventSubscriber( 200 audio_event_subscriber.get()); 201 202 VLOG(0) << "Dumping logging data for video stream."; 203 media::cast::proto::LogMetadata log_metadata; 204 media::cast::FrameEventList frame_events; 205 media::cast::PacketEventList packet_events; 206 video_event_subscriber->GetEventsAndReset( 207 &log_metadata, &frame_events, &packet_events); 208 209 DumpLoggingData(log_metadata, 210 frame_events, 211 packet_events, 212 video_log_file.Pass()); 213 214 VLOG(0) << "Dumping logging data for audio stream."; 215 audio_event_subscriber->GetEventsAndReset( 216 &log_metadata, &frame_events, &packet_events); 217 218 DumpLoggingData(log_metadata, 219 frame_events, 220 packet_events, 221 audio_log_file.Pass()); 222 } 223 224 void WriteStatsAndDestroySubscribers( 225 const scoped_refptr<media::cast::CastEnvironment>& cast_environment, 226 scoped_ptr<media::cast::StatsEventSubscriber> video_event_subscriber, 227 scoped_ptr<media::cast::StatsEventSubscriber> audio_event_subscriber, 228 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> estimator) { 229 cast_environment->Logging()->RemoveRawEventSubscriber( 230 video_event_subscriber.get()); 231 cast_environment->Logging()->RemoveRawEventSubscriber( 232 audio_event_subscriber.get()); 233 cast_environment->Logging()->RemoveRawEventSubscriber(estimator.get()); 234 235 scoped_ptr<base::DictionaryValue> stats = video_event_subscriber->GetStats(); 236 std::string json; 237 base::JSONWriter::WriteWithOptions( 238 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); 239 VLOG(0) << "Video stats: " << json; 240 241 stats = audio_event_subscriber->GetStats(); 242 json.clear(); 243 base::JSONWriter::WriteWithOptions( 244 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); 245 VLOG(0) << "Audio stats: " << json; 246 } 247 248 } // namespace 249 250 int main(int argc, char** argv) { 251 base::AtExitManager at_exit; 252 CommandLine::Init(argc, argv); 253 InitLogging(logging::LoggingSettings()); 254 255 // Load the media module for FFmpeg decoding. 256 base::FilePath path; 257 PathService::Get(base::DIR_MODULE, &path); 258 if (!media::InitializeMediaLibrary(path)) { 259 LOG(ERROR) << "Could not initialize media library."; 260 return 1; 261 } 262 263 base::Thread test_thread("Cast sender test app thread"); 264 base::Thread audio_thread("Cast audio encoder thread"); 265 base::Thread video_thread("Cast video encoder thread"); 266 test_thread.Start(); 267 audio_thread.Start(); 268 video_thread.Start(); 269 270 base::MessageLoopForIO io_message_loop; 271 272 // Default parameters. 273 CommandLine* cmd = CommandLine::ForCurrentProcess(); 274 std::string remote_ip_address = cmd->GetSwitchValueASCII(kSwitchAddress); 275 if (remote_ip_address.empty()) 276 remote_ip_address = "127.0.0.1"; 277 int remote_port = 0; 278 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchPort), 279 &remote_port)) { 280 remote_port = 2344; 281 } 282 LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port 283 << "."; 284 285 media::cast::AudioSenderConfig audio_config = GetAudioSenderConfig(); 286 media::cast::VideoSenderConfig video_config = GetVideoSenderConfig(); 287 288 // Running transport on the main thread. 289 // Setting up transport config. 290 net::IPEndPoint remote_endpoint = 291 CreateUDPAddress(remote_ip_address, remote_port); 292 293 // Enable raw event and stats logging. 294 // Running transport on the main thread. 295 scoped_refptr<media::cast::CastEnvironment> cast_environment( 296 new media::cast::CastEnvironment( 297 make_scoped_ptr<base::TickClock>(new base::DefaultTickClock()), 298 io_message_loop.message_loop_proxy(), 299 audio_thread.message_loop_proxy(), 300 video_thread.message_loop_proxy())); 301 302 // SendProcess initialization. 303 scoped_ptr<media::cast::FakeMediaSource> fake_media_source( 304 new media::cast::FakeMediaSource(test_thread.message_loop_proxy(), 305 cast_environment->Clock(), 306 video_config)); 307 308 int override_fps = 0; 309 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps), 310 &override_fps)){ 311 override_fps = 0; 312 } 313 base::FilePath source_path = cmd->GetSwitchValuePath(kSwitchSourceFile); 314 if (!source_path.empty()) { 315 LOG(INFO) << "Source: " << source_path.value(); 316 fake_media_source->SetSourceFile(source_path, override_fps); 317 } 318 319 // CastTransportSender initialization. 320 scoped_ptr<media::cast::CastTransportSender> transport_sender = 321 media::cast::CastTransportSender::Create( 322 NULL, // net log. 323 cast_environment->Clock(), 324 remote_endpoint, 325 make_scoped_ptr(new base::DictionaryValue), // options 326 base::Bind(&UpdateCastTransportStatus), 327 base::Bind(&LogRawEvents, cast_environment), 328 base::TimeDelta::FromSeconds(1), 329 io_message_loop.message_loop_proxy()); 330 331 // CastSender initialization. 332 scoped_ptr<media::cast::CastSender> cast_sender = 333 media::cast::CastSender::Create(cast_environment, transport_sender.get()); 334 cast_sender->InitializeVideo( 335 fake_media_source->get_video_config(), 336 base::Bind(&InitializationResult), 337 media::cast::CreateDefaultVideoEncodeAcceleratorCallback(), 338 media::cast::CreateDefaultVideoEncodeMemoryCallback()); 339 cast_sender->InitializeAudio(audio_config, base::Bind(&InitializationResult)); 340 341 // Set up event subscribers. 342 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber; 343 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber; 344 std::string video_log_file_name("/tmp/video_events.log.gz"); 345 std::string audio_log_file_name("/tmp/audio_events.log.gz"); 346 LOG(INFO) << "Logging audio events to: " << audio_log_file_name; 347 LOG(INFO) << "Logging video events to: " << video_log_file_name; 348 video_event_subscriber.reset(new media::cast::EncodingEventSubscriber( 349 media::cast::VIDEO_EVENT, 10000)); 350 audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber( 351 media::cast::AUDIO_EVENT, 10000)); 352 cast_environment->Logging()->AddRawEventSubscriber( 353 video_event_subscriber.get()); 354 cast_environment->Logging()->AddRawEventSubscriber( 355 audio_event_subscriber.get()); 356 357 // Subscribers for stats. 358 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> offset_estimator( 359 new media::cast::ReceiverTimeOffsetEstimatorImpl()); 360 cast_environment->Logging()->AddRawEventSubscriber(offset_estimator.get()); 361 scoped_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber( 362 new media::cast::StatsEventSubscriber(media::cast::VIDEO_EVENT, 363 cast_environment->Clock(), 364 offset_estimator.get())); 365 scoped_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber( 366 new media::cast::StatsEventSubscriber(media::cast::AUDIO_EVENT, 367 cast_environment->Clock(), 368 offset_estimator.get())); 369 cast_environment->Logging()->AddRawEventSubscriber( 370 video_stats_subscriber.get()); 371 cast_environment->Logging()->AddRawEventSubscriber( 372 audio_stats_subscriber.get()); 373 374 base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w")); 375 if (!video_log_file) { 376 VLOG(1) << "Failed to open video log file for writing."; 377 exit(-1); 378 } 379 380 base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w")); 381 if (!audio_log_file) { 382 VLOG(1) << "Failed to open audio log file for writing."; 383 exit(-1); 384 } 385 386 const int logging_duration_seconds = 10; 387 io_message_loop.message_loop_proxy()->PostDelayedTask( 388 FROM_HERE, 389 base::Bind(&WriteLogsToFileAndDestroySubscribers, 390 cast_environment, 391 base::Passed(&video_event_subscriber), 392 base::Passed(&audio_event_subscriber), 393 base::Passed(&video_log_file), 394 base::Passed(&audio_log_file)), 395 base::TimeDelta::FromSeconds(logging_duration_seconds)); 396 397 io_message_loop.message_loop_proxy()->PostDelayedTask( 398 FROM_HERE, 399 base::Bind(&WriteStatsAndDestroySubscribers, 400 cast_environment, 401 base::Passed(&video_stats_subscriber), 402 base::Passed(&audio_stats_subscriber), 403 base::Passed(&offset_estimator)), 404 base::TimeDelta::FromSeconds(logging_duration_seconds)); 405 406 fake_media_source->Start(cast_sender->audio_frame_input(), 407 cast_sender->video_frame_input()); 408 409 io_message_loop.Run(); 410 return 0; 411 } 412