sender.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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 "base/at_exit.h"
9#include "base/command_line.h"
10#include "base/file_util.h"
11#include "base/files/scoped_file.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/threading/thread.h"
15#include "base/time/default_tick_clock.h"
16#include "media/base/video_frame.h"
17#include "media/cast/cast_config.h"
18#include "media/cast/cast_environment.h"
19#include "media/cast/cast_sender.h"
20#include "media/cast/logging/encoding_event_subscriber.h"
21#include "media/cast/logging/log_serializer.h"
22#include "media/cast/logging/logging_defines.h"
23#include "media/cast/logging/proto/raw_events.pb.h"
24#include "media/cast/test/utility/audio_utility.h"
25#include "media/cast/test/utility/default_config.h"
26#include "media/cast/test/utility/input_builder.h"
27#include "media/cast/test/utility/video_utility.h"
28#include "media/cast/transport/cast_transport_defines.h"
29#include "media/cast/transport/cast_transport_sender.h"
30#include "media/cast/transport/transport/udp_transport.h"
31#include "ui/gfx/size.h"
32
33namespace media {
34namespace cast {
35// Settings chosen to match default receiver settings.
36#define DEFAULT_RECEIVER_PORT "2344"
37#define DEFAULT_RECEIVER_IP "127.0.0.1"
38#define DEFAULT_READ_FROM_FILE "0"
39#define DEFAULT_AUDIO_SENDER_SSRC "1"
40#define DEFAULT_AUDIO_RECEIVER_SSRC "2"
41#define DEFAULT_AUDIO_PAYLOAD_TYPE "127"
42#define DEFAULT_VIDEO_SENDER_SSRC "11"
43#define DEFAULT_VIDEO_RECEIVER_SSRC "12"
44#define DEFAULT_VIDEO_PAYLOAD_TYPE "96"
45#define DEFAULT_VIDEO_CODEC_WIDTH "1280"
46#define DEFAULT_VIDEO_CODEC_HEIGHT "720"
47#define DEFAULT_VIDEO_CODEC_BITRATE "2000"
48#define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000"
49#define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000"
50
51#define DEFAULT_LOGGING_DURATION "10"
52#define DEFAULT_COMPRESS_LOGS "1"
53
54namespace {
55static const int kAudioChannels = 2;
56static const int kAudioSamplingFrequency = 48000;
57static const int kSoundFrequency = 1234;  // Frequency of sinusoid wave.
58// The tests are commonly implemented with |kFrameTimerMs| RunTask function;
59// a normal video is 30 fps hence the 33 ms between frames.
60static const float kSoundVolume = 0.5f;
61static const int kFrameTimerMs = 33;
62
63// The max allowed size of serialized log.
64const int kMaxSerializedLogBytes = 10 * 1000 * 1000;
65}  // namespace
66
67void GetPort(int* port) {
68  test::InputBuilder input(
69      "Enter receiver port.", DEFAULT_RECEIVER_PORT, 1, INT_MAX);
70  *port = input.GetIntInput();
71}
72
73std::string GetIpAddress(const std::string display_text) {
74  test::InputBuilder input(display_text, DEFAULT_RECEIVER_IP, INT_MIN, INT_MAX);
75  std::string ip_address = input.GetStringInput();
76  // Verify correct form:
77  while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) {
78    ip_address = input.GetStringInput();
79  }
80  return ip_address;
81}
82
83int GetLoggingDuration() {
84  test::InputBuilder input(
85      "Choose logging duration (seconds), 0 for no logging.",
86      DEFAULT_LOGGING_DURATION,
87      0,
88      INT_MAX);
89  return input.GetIntInput();
90}
91
92std::string GetVideoLogFileDestination(bool compress) {
93  test::InputBuilder input(
94      "Enter video events log file destination.",
95      compress ? "./video_events.log.gz" : "./video_events.log",
96      INT_MIN,
97      INT_MAX);
98  return input.GetStringInput();
99}
100
101std::string GetAudioLogFileDestination(bool compress) {
102  test::InputBuilder input(
103      "Enter audio events log file destination.",
104      compress ? "./audio_events.log.gz" : "./audio_events.log",
105      INT_MIN,
106      INT_MAX);
107  return input.GetStringInput();
108}
109
110bool CompressLogs() {
111  test::InputBuilder input(
112      "Enter 1 to enable compression on logs.", DEFAULT_COMPRESS_LOGS, 0, 1);
113  return (1 == input.GetIntInput());
114}
115
116bool ReadFromFile() {
117  test::InputBuilder input(
118      "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1);
119  return (1 == input.GetIntInput());
120}
121
122std::string GetVideoFile() {
123  test::InputBuilder input(
124      "Enter file and path to raw video file.", "", INT_MIN, INT_MAX);
125  return input.GetStringInput();
126}
127
128void GetSsrcs(AudioSenderConfig* audio_config) {
129  test::InputBuilder input_tx(
130      "Choose audio sender SSRC.", DEFAULT_AUDIO_SENDER_SSRC, 1, INT_MAX);
131  audio_config->sender_ssrc = input_tx.GetIntInput();
132
133  test::InputBuilder input_rx(
134      "Choose audio receiver SSRC.", DEFAULT_AUDIO_RECEIVER_SSRC, 1, INT_MAX);
135  audio_config->incoming_feedback_ssrc = input_rx.GetIntInput();
136}
137
138void GetSsrcs(VideoSenderConfig* video_config) {
139  test::InputBuilder input_tx(
140      "Choose video sender SSRC.", DEFAULT_VIDEO_SENDER_SSRC, 1, INT_MAX);
141  video_config->sender_ssrc = input_tx.GetIntInput();
142
143  test::InputBuilder input_rx(
144      "Choose video receiver SSRC.", DEFAULT_VIDEO_RECEIVER_SSRC, 1, INT_MAX);
145  video_config->incoming_feedback_ssrc = input_rx.GetIntInput();
146}
147
148void GetPayloadtype(AudioSenderConfig* audio_config) {
149  test::InputBuilder input(
150      "Choose audio sender payload type.", DEFAULT_AUDIO_PAYLOAD_TYPE, 96, 127);
151  audio_config->rtp_config.payload_type = input.GetIntInput();
152}
153
154AudioSenderConfig GetAudioSenderConfig() {
155  AudioSenderConfig audio_config;
156
157  GetSsrcs(&audio_config);
158  GetPayloadtype(&audio_config);
159
160  audio_config.rtcp_c_name = "audio_sender@a.b.c.d";
161
162  VLOG(0) << "Using OPUS 48Khz stereo at 64kbit/s";
163  audio_config.use_external_encoder = false;
164  audio_config.frequency = kAudioSamplingFrequency;
165  audio_config.channels = kAudioChannels;
166  audio_config.bitrate = 64000;
167  audio_config.codec = transport::kOpus;
168  return audio_config;
169}
170
171void GetPayloadtype(VideoSenderConfig* video_config) {
172  test::InputBuilder input(
173      "Choose video sender payload type.", DEFAULT_VIDEO_PAYLOAD_TYPE, 96, 127);
174  video_config->rtp_config.payload_type = input.GetIntInput();
175}
176
177void GetVideoCodecSize(VideoSenderConfig* video_config) {
178  test::InputBuilder input_width(
179      "Choose video width.", DEFAULT_VIDEO_CODEC_WIDTH, 144, 1920);
180  video_config->width = input_width.GetIntInput();
181
182  test::InputBuilder input_height(
183      "Choose video height.", DEFAULT_VIDEO_CODEC_HEIGHT, 176, 1080);
184  video_config->height = input_height.GetIntInput();
185}
186
187void GetVideoBitrates(VideoSenderConfig* video_config) {
188  test::InputBuilder input_start_br(
189      "Choose start bitrate[kbps].", DEFAULT_VIDEO_CODEC_BITRATE, 0, INT_MAX);
190  video_config->start_bitrate = input_start_br.GetIntInput() * 1000;
191
192  test::InputBuilder input_max_br(
193      "Choose max bitrate[kbps].", DEFAULT_VIDEO_CODEC_MAX_BITRATE, 0, INT_MAX);
194  video_config->max_bitrate = input_max_br.GetIntInput() * 1000;
195
196  test::InputBuilder input_min_br(
197      "Choose min bitrate[kbps].", DEFAULT_VIDEO_CODEC_MIN_BITRATE, 0, INT_MAX);
198  video_config->min_bitrate = input_min_br.GetIntInput() * 1000;
199}
200
201VideoSenderConfig GetVideoSenderConfig() {
202  VideoSenderConfig video_config;
203
204  GetSsrcs(&video_config);
205  GetPayloadtype(&video_config);
206  GetVideoCodecSize(&video_config);
207  GetVideoBitrates(&video_config);
208
209  video_config.rtcp_c_name = "video_sender@a.b.c.d";
210
211  video_config.use_external_encoder = false;
212
213  VLOG(0) << "Using VP8 at 30 fps";
214  video_config.min_qp = 4;
215  video_config.max_qp = 40;
216  video_config.max_frame_rate = 30;
217  video_config.codec = transport::kVp8;
218  video_config.max_number_of_video_buffers_used = 1;
219  return video_config;
220}
221
222class SendProcess {
223 public:
224  SendProcess(scoped_refptr<base::SingleThreadTaskRunner> thread_proxy,
225              base::TickClock* clock,
226              const VideoSenderConfig& video_config,
227              scoped_refptr<AudioFrameInput> audio_frame_input,
228              scoped_refptr<VideoFrameInput> video_frame_input)
229      : test_app_thread_proxy_(thread_proxy),
230        video_config_(video_config),
231        audio_diff_(kFrameTimerMs),
232        audio_frame_input_(audio_frame_input),
233        video_frame_input_(video_frame_input),
234        synthetic_count_(0),
235        clock_(clock),
236        start_time_(),
237        send_time_(),
238        weak_factory_(this) {
239    audio_bus_factory_.reset(new TestAudioBusFactory(kAudioChannels,
240                                                     kAudioSamplingFrequency,
241                                                     kSoundFrequency,
242                                                     kSoundVolume));
243    if (ReadFromFile()) {
244      std::string video_file_name = GetVideoFile();
245      video_file_ = fopen(video_file_name.c_str(), "r");
246      if (video_file_ == NULL) {
247        VLOG(1) << "Failed to open file";
248        exit(-1);
249      }
250    } else {
251      video_file_ = NULL;
252    }
253  }
254
255  ~SendProcess() {
256    if (video_file_)
257      fclose(video_file_);
258  }
259
260  void SendFrame() {
261    // Make sure that we don't drift.
262    int num_10ms_blocks = audio_diff_ / 10;
263    // Avoid drift.
264    audio_diff_ += kFrameTimerMs - num_10ms_blocks * 10;
265
266    audio_frame_input_->InsertAudio(
267        audio_bus_factory_->NextAudioBus(
268            base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks),
269        clock_->NowTicks());
270
271    gfx::Size size(video_config_.width, video_config_.height);
272    // TODO(mikhal): Use the provided timestamp.
273    if (start_time_.is_null())
274      start_time_ = clock_->NowTicks();
275    base::TimeDelta time_diff = clock_->NowTicks() - start_time_;
276    scoped_refptr<media::VideoFrame> video_frame =
277        media::VideoFrame::CreateFrame(
278            VideoFrame::I420, size, gfx::Rect(size), size, time_diff);
279    if (video_file_) {
280      if (!PopulateVideoFrameFromFile(video_frame, video_file_))
281        return;
282    } else {
283      PopulateVideoFrame(video_frame, synthetic_count_);
284      ++synthetic_count_;
285    }
286
287    // Time the sending of the frame to match the set frame rate.
288    // Sleep if that time has yet to elapse.
289    base::TimeTicks now = clock_->NowTicks();
290    base::TimeDelta video_frame_time =
291        base::TimeDelta::FromMilliseconds(kFrameTimerMs);
292    base::TimeDelta elapsed_time = now - send_time_;
293    if (elapsed_time < video_frame_time) {
294      VLOG(1) << "Wait" << (video_frame_time - elapsed_time).InMilliseconds();
295      test_app_thread_proxy_->PostDelayedTask(
296          FROM_HERE,
297          base::Bind(&SendProcess::SendVideoFrameOnTime,
298                     weak_factory_.GetWeakPtr(),
299                     video_frame),
300          video_frame_time - elapsed_time);
301    } else {
302      test_app_thread_proxy_->PostTask(
303          FROM_HERE,
304          base::Bind(&SendProcess::SendVideoFrameOnTime,
305                     weak_factory_.GetWeakPtr(),
306                     video_frame));
307    }
308  }
309
310  void SendVideoFrameOnTime(scoped_refptr<media::VideoFrame> video_frame) {
311    send_time_ = clock_->NowTicks();
312    video_frame_input_->InsertRawVideoFrame(video_frame, send_time_);
313    test_app_thread_proxy_->PostTask(
314        FROM_HERE, base::Bind(&SendProcess::SendFrame, base::Unretained(this)));
315  }
316
317 private:
318  scoped_refptr<base::SingleThreadTaskRunner> test_app_thread_proxy_;
319  const VideoSenderConfig video_config_;
320  int audio_diff_;
321  const scoped_refptr<AudioFrameInput> audio_frame_input_;
322  const scoped_refptr<VideoFrameInput> video_frame_input_;
323  FILE* video_file_;
324  uint8 synthetic_count_;
325  base::TickClock* const clock_;  // Not owned by this class.
326  base::TimeTicks start_time_;
327  base::TimeTicks send_time_;
328  scoped_ptr<TestAudioBusFactory> audio_bus_factory_;
329
330  // NOTE: Weak pointers must be invalidated before all other member variables.
331  base::WeakPtrFactory<SendProcess> weak_factory_;
332
333  DISALLOW_COPY_AND_ASSIGN(SendProcess);
334};
335
336}  // namespace cast
337}  // namespace media
338
339namespace {
340void UpdateCastTransportStatus(
341    media::cast::transport::CastTransportStatus status) {}
342
343void LogRawEvents(
344    const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
345    const std::vector<media::cast::PacketEvent>& packet_events) {
346  VLOG(1) << "Got packet events from transport, size: " << packet_events.size();
347  for (std::vector<media::cast::PacketEvent>::const_iterator it =
348           packet_events.begin();
349       it != packet_events.end();
350       ++it) {
351    cast_environment->Logging()->InsertPacketEvent(it->timestamp,
352                                                   it->type,
353                                                   it->rtp_timestamp,
354                                                   it->frame_id,
355                                                   it->packet_id,
356                                                   it->max_packet_id,
357                                                   it->size);
358  }
359}
360
361void InitializationResult(media::cast::CastInitializationStatus result) {
362  bool end_result = result == media::cast::STATUS_AUDIO_INITIALIZED ||
363                    result == media::cast::STATUS_VIDEO_INITIALIZED;
364  CHECK(end_result) << "Cast sender uninitialized";
365}
366
367net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) {
368  net::IPAddressNumber ip_number;
369  CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number));
370  return net::IPEndPoint(ip_number, port);
371}
372
373void DumpLoggingData(const media::cast::proto::LogMetadata& log_metadata,
374                     const media::cast::FrameEventMap& frame_events,
375                     const media::cast::PacketEventMap& packet_events,
376                     bool compress,
377                     base::ScopedFILE log_file) {
378  VLOG(0) << "Frame map size: " << frame_events.size();
379  VLOG(0) << "Packet map size: " << packet_events.size();
380
381  scoped_ptr<char[]> event_log(new char[media::cast::kMaxSerializedLogBytes]);
382  int event_log_bytes;
383  if (!media::cast::SerializeEvents(log_metadata,
384                                    frame_events,
385                                    packet_events,
386                                    compress,
387                                    media::cast::kMaxSerializedLogBytes,
388                                    event_log.get(),
389                                    &event_log_bytes)) {
390    VLOG(0) << "Failed to serialize events.";
391    return;
392  }
393
394  VLOG(0) << "Events serialized length: " << event_log_bytes;
395
396  int ret = fwrite(event_log.get(), 1, event_log_bytes, log_file.get());
397  if (ret != event_log_bytes)
398    VLOG(0) << "Failed to write logs to file.";
399}
400
401void WriteLogsToFileAndStopSubscribing(
402    const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
403    scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber,
404    scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber,
405    base::ScopedFILE video_log_file,
406    base::ScopedFILE audio_log_file,
407    bool compress) {
408  cast_environment->Logging()->RemoveRawEventSubscriber(
409      video_event_subscriber.get());
410  cast_environment->Logging()->RemoveRawEventSubscriber(
411      audio_event_subscriber.get());
412
413  VLOG(0) << "Dumping logging data for video stream.";
414  media::cast::proto::LogMetadata log_metadata;
415  media::cast::FrameEventMap frame_events;
416  media::cast::PacketEventMap packet_events;
417  video_event_subscriber->GetEventsAndReset(
418      &log_metadata, &frame_events, &packet_events);
419
420  DumpLoggingData(log_metadata,
421                  frame_events,
422                  packet_events,
423                  compress,
424                  video_log_file.Pass());
425
426  VLOG(0) << "Dumping logging data for audio stream.";
427  audio_event_subscriber->GetEventsAndReset(
428      &log_metadata, &frame_events, &packet_events);
429
430  DumpLoggingData(log_metadata,
431                  frame_events,
432                  packet_events,
433                  compress,
434                  audio_log_file.Pass());
435}
436
437}  // namespace
438
439int main(int argc, char** argv) {
440  base::AtExitManager at_exit;
441  CommandLine::Init(argc, argv);
442  InitLogging(logging::LoggingSettings());
443  base::Thread test_thread("Cast sender test app thread");
444  base::Thread audio_thread("Cast audio encoder thread");
445  base::Thread video_thread("Cast video encoder thread");
446  test_thread.Start();
447  audio_thread.Start();
448  video_thread.Start();
449
450  base::MessageLoopForIO io_message_loop;
451
452  int remote_port;
453  media::cast::GetPort(&remote_port);
454
455  std::string remote_ip_address =
456      media::cast::GetIpAddress("Enter receiver IP.");
457
458  media::cast::AudioSenderConfig audio_config =
459      media::cast::GetAudioSenderConfig();
460  media::cast::VideoSenderConfig video_config =
461      media::cast::GetVideoSenderConfig();
462
463  // Running transport on the main thread.
464  // Setting up transport config.
465  media::cast::transport::CastTransportAudioConfig transport_audio_config;
466  media::cast::transport::CastTransportVideoConfig transport_video_config;
467  net::IPEndPoint remote_endpoint =
468      CreateUDPAddress(remote_ip_address, remote_port);
469  transport_audio_config.base.ssrc = audio_config.sender_ssrc;
470  VLOG(0) << "Audio ssrc: " << transport_audio_config.base.ssrc;
471  transport_audio_config.base.rtp_config = audio_config.rtp_config;
472  transport_video_config.base.ssrc = video_config.sender_ssrc;
473  transport_video_config.base.rtp_config = video_config.rtp_config;
474
475  // Enable raw event and stats logging.
476  // Running transport on the main thread.
477  scoped_refptr<media::cast::CastEnvironment> cast_environment(
478      new media::cast::CastEnvironment(
479          make_scoped_ptr<base::TickClock>(new base::DefaultTickClock()),
480          io_message_loop.message_loop_proxy(),
481          audio_thread.message_loop_proxy(),
482          video_thread.message_loop_proxy()));
483
484  scoped_ptr<media::cast::transport::CastTransportSender> transport_sender =
485      media::cast::transport::CastTransportSender::Create(
486          NULL,  // net log.
487          cast_environment->Clock(),
488          remote_endpoint,
489          base::Bind(&UpdateCastTransportStatus),
490          base::Bind(&LogRawEvents, cast_environment),
491          base::TimeDelta::FromSeconds(1),
492          io_message_loop.message_loop_proxy());
493
494  transport_sender->InitializeAudio(transport_audio_config);
495  transport_sender->InitializeVideo(transport_video_config);
496
497  scoped_ptr<media::cast::CastSender> cast_sender =
498      media::cast::CastSender::Create(cast_environment, transport_sender.get());
499
500  cast_sender->InitializeVideo(
501      video_config,
502      base::Bind(&InitializationResult),
503      media::cast::CreateDefaultVideoEncodeAcceleratorCallback(),
504      media::cast::CreateDefaultVideoEncodeMemoryCallback());
505
506  cast_sender->InitializeAudio(audio_config, base::Bind(&InitializationResult));
507
508  transport_sender->SetPacketReceiver(cast_sender->packet_receiver());
509
510  scoped_refptr<media::cast::AudioFrameInput> audio_frame_input =
511      cast_sender->audio_frame_input();
512  scoped_refptr<media::cast::VideoFrameInput> video_frame_input =
513      cast_sender->video_frame_input();
514  scoped_ptr<media::cast::SendProcess> send_process(
515      new media::cast::SendProcess(test_thread.message_loop_proxy(),
516                                   cast_environment->Clock(),
517                                   video_config,
518                                   audio_frame_input,
519                                   video_frame_input));
520
521  // Set up event subscribers.
522  int logging_duration = media::cast::GetLoggingDuration();
523  scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber;
524  scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber;
525  if (logging_duration > 0) {
526    bool compress = media::cast::CompressLogs();
527    std::string video_log_file_name(
528        media::cast::GetVideoLogFileDestination(compress));
529    std::string audio_log_file_name(
530        media::cast::GetAudioLogFileDestination(compress));
531    video_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
532        media::cast::VIDEO_EVENT, 10000));
533    audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
534        media::cast::AUDIO_EVENT, 10000));
535    cast_environment->Logging()->AddRawEventSubscriber(
536        video_event_subscriber.get());
537    cast_environment->Logging()->AddRawEventSubscriber(
538        audio_event_subscriber.get());
539
540    base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w"));
541    if (!video_log_file) {
542      VLOG(1) << "Failed to open video log file for writing.";
543      exit(-1);
544    }
545
546    base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w"));
547    if (!audio_log_file) {
548      VLOG(1) << "Failed to open audio log file for writing.";
549      exit(-1);
550    }
551
552    io_message_loop.message_loop_proxy()->PostDelayedTask(
553        FROM_HERE,
554        base::Bind(&WriteLogsToFileAndStopSubscribing,
555                   cast_environment,
556                   base::Passed(&video_event_subscriber),
557                   base::Passed(&audio_event_subscriber),
558                   base::Passed(&video_log_file),
559                   base::Passed(&audio_log_file),
560                   compress),
561        base::TimeDelta::FromSeconds(logging_duration));
562  }
563
564  test_thread.message_loop_proxy()->PostTask(
565      FROM_HERE,
566      base::Bind(&media::cast::SendProcess::SendFrame,
567                 base::Unretained(send_process.get())));
568
569  io_message_loop.Run();
570
571  return 0;
572}
573