• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/threading/thread.h"
12 #include "base/time/default_tick_clock.h"
13 #include "media/base/video_frame.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/logging_defines.h"
18 #include "media/cast/test/audio_utility.h"
19 #include "media/cast/test/transport/transport.h"
20 #include "media/cast/test/utility/input_helper.h"
21 #include "media/cast/test/video_utility.h"
22 #include "ui/gfx/size.h"
23 
24 namespace media {
25 namespace cast {
26 // Settings chosen to match default receiver settings.
27 #define DEFAULT_SEND_PORT "2344"
28 #define DEFAULT_RECEIVE_PORT "2346"
29 #define DEFAULT_SEND_IP "127.0.0.1"
30 #define DEFAULT_READ_FROM_FILE "0"
31 #define DEFAULT_PACKET_LOSS "0"
32 #define DEFAULT_AUDIO_SENDER_SSRC "1"
33 #define DEFAULT_AUDIO_RECEIVER_SSRC "2"
34 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127"
35 #define DEFAULT_VIDEO_SENDER_SSRC "11"
36 #define DEFAULT_VIDEO_RECEIVER_SSRC "12"
37 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96"
38 #define DEFAULT_VIDEO_CODEC_WIDTH "1280"
39 #define DEFAULT_VIDEO_CODEC_HEIGHT "720"
40 #define DEFAULT_VIDEO_CODEC_BITRATE "2000"
41 #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000"
42 #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000"
43 
44 namespace {
45 static const int kAudioChannels = 2;
46 static const int kAudioSamplingFrequency = 48000;
47 static const int kSoundFrequency = 1234;  // Frequency of sinusoid wave.
48 // The tests are commonly implemented with |kFrameTimerMs| RunTask function;
49 // a normal video is 30 fps hence the 33 ms between frames.
50 static const float kSoundVolume = 0.5f;
51 static const int kFrameTimerMs = 33;
52 
53 // Dummy callback function that does nothing except to accept ownership of
54 // |audio_bus| for destruction. This guarantees that the audio_bus is valid for
55 // the entire duration of the encode/send process (not equivalent to DoNothing).
OwnThatAudioBus(scoped_ptr<AudioBus> audio_bus)56 void OwnThatAudioBus(scoped_ptr<AudioBus> audio_bus) {
57 }
58 } // namespace
59 
GetPorts(int * tx_port,int * rx_port)60 void GetPorts(int* tx_port, int* rx_port) {
61   test::InputBuilder tx_input("Enter send port.",
62       DEFAULT_SEND_PORT, 1, INT_MAX);
63   *tx_port = tx_input.GetIntInput();
64 
65   test::InputBuilder rx_input("Enter receive port.",
66       DEFAULT_RECEIVE_PORT, 1, INT_MAX);
67   *rx_port = rx_input.GetIntInput();
68 }
69 
GetPacketLoss()70 int GetPacketLoss() {
71   test::InputBuilder input("Enter send side packet loss %.",
72       DEFAULT_PACKET_LOSS, 0, 99);
73   return input.GetIntInput();
74 }
75 
GetIpAddress(const std::string display_text)76 std::string GetIpAddress(const std::string display_text) {
77   test::InputBuilder input(display_text, DEFAULT_SEND_IP,
78       INT_MIN, INT_MAX);
79   std::string ip_address = input.GetStringInput();
80   // Verify correct form:
81   while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) {
82     ip_address = input.GetStringInput();
83   }
84   return ip_address;
85 }
86 
ReadFromFile()87 bool ReadFromFile() {
88   test::InputBuilder input("Enter 1 to read from file.", DEFAULT_READ_FROM_FILE,
89       0, 1);
90   return (1 == input.GetIntInput());
91 }
92 
GetVideoFile()93 std::string GetVideoFile() {
94   test::InputBuilder input("Enter file and path to raw video file.","",
95       INT_MIN, INT_MAX);
96   return input.GetStringInput();
97 }
98 
GetSsrcs(AudioSenderConfig * audio_config)99 void GetSsrcs(AudioSenderConfig* audio_config) {
100   test::InputBuilder input_tx("Choose audio sender SSRC.",
101       DEFAULT_AUDIO_SENDER_SSRC, 1, INT_MAX);
102   audio_config->sender_ssrc = input_tx.GetIntInput();
103 
104   test::InputBuilder input_rx("Choose audio receiver SSRC.",
105       DEFAULT_AUDIO_RECEIVER_SSRC, 1, INT_MAX);
106   audio_config->incoming_feedback_ssrc = input_rx.GetIntInput();
107 }
108 
GetSsrcs(VideoSenderConfig * video_config)109 void GetSsrcs(VideoSenderConfig* video_config) {
110   test::InputBuilder input_tx("Choose video sender SSRC.",
111       DEFAULT_VIDEO_SENDER_SSRC, 1, INT_MAX);
112   video_config->sender_ssrc = input_tx.GetIntInput();
113 
114   test::InputBuilder input_rx("Choose video receiver SSRC.",
115       DEFAULT_VIDEO_RECEIVER_SSRC, 1, INT_MAX);
116   video_config->incoming_feedback_ssrc = input_rx.GetIntInput();
117 }
118 
GetPayloadtype(AudioSenderConfig * audio_config)119 void GetPayloadtype(AudioSenderConfig* audio_config) {
120   test::InputBuilder input("Choose audio sender payload type.",
121       DEFAULT_AUDIO_PAYLOAD_TYPE, 96, 127);
122   audio_config->rtp_payload_type = input.GetIntInput();
123 }
124 
GetAudioSenderConfig()125 AudioSenderConfig GetAudioSenderConfig() {
126   AudioSenderConfig audio_config;
127 
128   GetSsrcs(&audio_config);
129   GetPayloadtype(&audio_config);
130 
131   audio_config.rtcp_c_name = "audio_sender@a.b.c.d";
132 
133   VLOG(0) << "Using OPUS 48Khz stereo at 64kbit/s";
134   audio_config.use_external_encoder = false;
135   audio_config.frequency = kAudioSamplingFrequency;
136   audio_config.channels = kAudioChannels;
137   audio_config.bitrate = 64000;
138   audio_config.codec = kOpus;
139   return audio_config;
140 }
141 
GetPayloadtype(VideoSenderConfig * video_config)142 void GetPayloadtype(VideoSenderConfig* video_config) {
143   test::InputBuilder input("Choose video sender payload type.",
144       DEFAULT_VIDEO_PAYLOAD_TYPE, 96, 127);
145   video_config->rtp_payload_type = input.GetIntInput();
146 }
147 
GetVideoCodecSize(VideoSenderConfig * video_config)148 void GetVideoCodecSize(VideoSenderConfig* video_config) {
149   test::InputBuilder input_width("Choose video width.",
150       DEFAULT_VIDEO_CODEC_WIDTH, 144, 1920);
151   video_config->width = input_width.GetIntInput();
152 
153   test::InputBuilder input_height("Choose video height.",
154       DEFAULT_VIDEO_CODEC_HEIGHT, 176, 1080);
155   video_config->height = input_height.GetIntInput();
156 }
157 
GetVideoBitrates(VideoSenderConfig * video_config)158 void GetVideoBitrates(VideoSenderConfig* video_config) {
159   test::InputBuilder input_start_br("Choose start bitrate[kbps].",
160       DEFAULT_VIDEO_CODEC_BITRATE, 0, INT_MAX);
161   video_config->start_bitrate = input_start_br.GetIntInput() * 1000;
162 
163   test::InputBuilder input_max_br("Choose max bitrate[kbps].",
164       DEFAULT_VIDEO_CODEC_MAX_BITRATE, 0, INT_MAX);
165   video_config->max_bitrate = input_max_br.GetIntInput() * 1000;
166 
167   test::InputBuilder input_min_br("Choose min bitrate[kbps].",
168       DEFAULT_VIDEO_CODEC_MIN_BITRATE, 0, INT_MAX);
169   video_config->min_bitrate = input_min_br.GetIntInput() * 1000;
170 }
171 
GetVideoSenderConfig()172 VideoSenderConfig GetVideoSenderConfig() {
173   VideoSenderConfig video_config;
174 
175   GetSsrcs(&video_config);
176   GetPayloadtype(&video_config);
177   GetVideoCodecSize(&video_config);
178   GetVideoBitrates(&video_config);
179 
180   video_config.rtcp_c_name = "video_sender@a.b.c.d";
181 
182   video_config.use_external_encoder = false;
183 
184   VLOG(0) << "Using VP8 at 30 fps";
185   video_config.min_qp = 4;
186   video_config.max_qp = 40;
187   video_config.max_frame_rate = 30;
188   video_config.codec = kVp8;
189   video_config.max_number_of_video_buffers_used = 1;
190   video_config.number_of_cores = 1;
191   return video_config;
192 }
193 
194 class SendProcess {
195  public:
SendProcess(scoped_refptr<base::TaskRunner> thread_proxy,base::TickClock * clock,const VideoSenderConfig & video_config,FrameInput * frame_input)196   SendProcess(scoped_refptr<base::TaskRunner> thread_proxy,
197               base::TickClock* clock,
198               const VideoSenderConfig& video_config,
199               FrameInput* frame_input)
200       : test_app_thread_proxy_(thread_proxy),
201         video_config_(video_config),
202         audio_diff_(kFrameTimerMs),
203         frame_input_(frame_input),
204         synthetic_count_(0),
205         clock_(clock),
206         start_time_(),
207         send_time_(),
208         weak_factory_(this) {
209     audio_bus_factory_.reset(new TestAudioBusFactory(kAudioChannels,
210         kAudioSamplingFrequency, kSoundFrequency, kSoundVolume));
211     if (ReadFromFile()) {
212       std::string video_file_name = GetVideoFile();
213       video_file_ = fopen(video_file_name.c_str(), "r");
214       if (video_file_ == NULL) {
215         VLOG(1) << "Failed to open file";
216         exit(-1);
217       }
218     } else {
219       video_file_ = NULL;
220     }
221   }
222 
~SendProcess()223   ~SendProcess() {
224     if (video_file_)
225       fclose(video_file_);
226   }
227 
SendFrame()228   void SendFrame() {
229     // Make sure that we don't drift.
230     int num_10ms_blocks = audio_diff_ / 10;
231     // Avoid drift.
232     audio_diff_ += kFrameTimerMs - num_10ms_blocks * 10;
233 
234     scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
235         base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
236     AudioBus* const audio_bus_ptr = audio_bus.get();
237     frame_input_->InsertAudio(audio_bus_ptr, clock_->NowTicks(),
238         base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
239 
240     gfx::Size size(video_config_.width, video_config_.height);
241     // TODO(mikhal): Use the provided timestamp.
242     if (start_time_.is_null())
243       start_time_ = clock_->NowTicks();
244     base::TimeDelta time_diff = clock_->NowTicks() - start_time_;
245     scoped_refptr<media::VideoFrame> video_frame =
246         media::VideoFrame::CreateFrame(
247         VideoFrame::I420, size, gfx::Rect(size), size, time_diff);
248     if (video_file_) {
249       if (!PopulateVideoFrameFromFile(video_frame, video_file_))
250         return;
251     } else {
252       PopulateVideoFrame(video_frame, synthetic_count_);
253       ++synthetic_count_;
254     }
255 
256     // Time the sending of the frame to match the set frame rate.
257     // Sleep if that time has yet to elapse.
258     base::TimeTicks now = clock_->NowTicks();
259     base::TimeDelta video_frame_time =
260         base::TimeDelta::FromMilliseconds(kFrameTimerMs);
261     base::TimeDelta elapsed_time = now - send_time_;
262     if (elapsed_time < video_frame_time) {
263       VLOG(1) << "Wait" <<
264           (video_frame_time - elapsed_time).InMilliseconds();
265      test_app_thread_proxy_->PostDelayedTask(FROM_HERE,
266         base::Bind(&SendProcess::SendVideoFrameOnTime, base::Unretained(this),
267                    video_frame),
268         video_frame_time - elapsed_time);
269     } else {
270       test_app_thread_proxy_->PostTask(FROM_HERE,
271       base::Bind(&SendProcess::SendVideoFrameOnTime, base::Unretained(this),
272                  video_frame));
273     }
274   }
275 
SendVideoFrameOnTime(scoped_refptr<media::VideoFrame> video_frame)276   void SendVideoFrameOnTime(scoped_refptr<media::VideoFrame> video_frame) {
277     send_time_ = clock_->NowTicks();
278     frame_input_->InsertRawVideoFrame(video_frame, send_time_);
279     test_app_thread_proxy_->PostTask(FROM_HERE,
280           base::Bind(&SendProcess::SendFrame, base::Unretained(this)));
281   }
282 
283  private:
284   scoped_refptr<base::TaskRunner> test_app_thread_proxy_;
285   const VideoSenderConfig video_config_;
286   int audio_diff_;
287   const scoped_refptr<FrameInput> frame_input_;
288   FILE* video_file_;
289   uint8 synthetic_count_;
290   base::TickClock* const clock_;  // Not owned by this class.
291   base::TimeTicks start_time_;
292   base::TimeTicks send_time_;
293   scoped_ptr<TestAudioBusFactory> audio_bus_factory_;
294   base::WeakPtrFactory<SendProcess> weak_factory_;
295 };
296 
297 }  // namespace cast
298 }  // namespace media
299 
300 
main(int argc,char ** argv)301 int main(int argc, char** argv) {
302   base::AtExitManager at_exit;
303   VLOG(1) << "Cast Sender";
304   base::Thread test_thread("Cast sender test app thread");
305   base::Thread main_thread("Cast main send thread");
306   base::Thread audio_thread("Cast audio encoder thread");
307   base::Thread video_thread("Cast video encoder thread");
308   test_thread.Start();
309   main_thread.Start();
310   audio_thread.Start();
311   video_thread.Start();
312 
313   base::DefaultTickClock clock;
314   base::MessageLoopForIO io_message_loop;
315 
316   // Enable main and send side threads only. Disable logging.
317   scoped_refptr<media::cast::CastEnvironment> cast_environment(new
318       media::cast::CastEnvironment(
319       &clock,
320       main_thread.message_loop_proxy(),
321       audio_thread.message_loop_proxy(),
322       NULL,
323       video_thread.message_loop_proxy(),
324       NULL,
325       media::cast::GetDefaultCastLoggingConfig()));
326 
327   media::cast::AudioSenderConfig audio_config =
328       media::cast::GetAudioSenderConfig();
329   media::cast::VideoSenderConfig video_config =
330       media::cast::GetVideoSenderConfig();
331 
332   scoped_ptr<media::cast::test::Transport> transport(
333       new media::cast::test::Transport(io_message_loop.message_loop_proxy()));
334   scoped_ptr<media::cast::CastSender> cast_sender(
335       media::cast::CastSender::CreateCastSender(cast_environment,
336       audio_config,
337       video_config,
338       NULL,  // VideoEncoderController.
339       transport->packet_sender()));
340 
341   media::cast::PacketReceiver* packet_receiver = cast_sender->packet_receiver();
342 
343   int send_to_port, receive_port;
344   media::cast::GetPorts(&send_to_port, &receive_port);
345   std::string ip_address = media::cast::GetIpAddress("Enter destination IP.");
346   std::string local_ip_address = media::cast::GetIpAddress("Enter local IP.");
347   int packet_loss_percentage = media::cast::GetPacketLoss();
348 
349   transport->SetLocalReceiver(packet_receiver, ip_address, local_ip_address,
350                               receive_port);
351   transport->SetSendDestination(ip_address, send_to_port);
352   transport->SetSendSidePacketLoss(packet_loss_percentage);
353 
354   media::cast::FrameInput* frame_input = cast_sender->frame_input();
355   scoped_ptr<media::cast::SendProcess> send_process(new
356       media::cast::SendProcess(test_thread.message_loop_proxy(),
357                                cast_environment->Clock(),
358                                video_config,
359                                frame_input));
360 
361   send_process->SendFrame();
362   io_message_loop.Run();
363   transport->StopReceiving();
364   return 0;
365 }
366