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.
5 // Test application that simulates a cast sender - Data can be either generated
6 // or read from a file.
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/utility/audio_utility.h"
19 #include "media/cast/test/utility/input_builder.h"
20 #include "media/cast/test/utility/video_utility.h"
21 #include "media/cast/transport/cast_transport_defines.h"
22 #include "media/cast/transport/cast_transport_sender.h"
23 #include "media/cast/transport/transport/udp_transport.h"
24 #include "ui/gfx/size.h"
28 // Settings chosen to match default receiver settings.
29 #define DEFAULT_RECEIVER_PORT "2344"
30 #define DEFAULT_RECEIVER_IP "127.0.0.1"
31 #define DEFAULT_READ_FROM_FILE "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"
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;
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).
56 void OwnThatAudioBus(scoped_ptr
<AudioBus
> audio_bus
) {}
59 void GetPort(int* port
) {
60 test::InputBuilder
input(
61 "Enter receiver port.", DEFAULT_RECEIVER_PORT
, 1, INT_MAX
);
62 *port
= input
.GetIntInput();
65 std::string
GetIpAddress(const std::string display_text
) {
66 test::InputBuilder
input(display_text
, DEFAULT_RECEIVER_IP
, INT_MIN
, INT_MAX
);
67 std::string ip_address
= input
.GetStringInput();
68 // Verify correct form:
69 while (std::count(ip_address
.begin(), ip_address
.end(), '.') != 3) {
70 ip_address
= input
.GetStringInput();
76 test::InputBuilder
input(
77 "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE
, 0, 1);
78 return (1 == input
.GetIntInput());
81 std::string
GetVideoFile() {
82 test::InputBuilder
input(
83 "Enter file and path to raw video file.", "", INT_MIN
, INT_MAX
);
84 return input
.GetStringInput();
87 void GetSsrcs(AudioSenderConfig
* audio_config
) {
88 test::InputBuilder
input_tx(
89 "Choose audio sender SSRC.", DEFAULT_AUDIO_SENDER_SSRC
, 1, INT_MAX
);
90 audio_config
->sender_ssrc
= input_tx
.GetIntInput();
92 test::InputBuilder
input_rx(
93 "Choose audio receiver SSRC.", DEFAULT_AUDIO_RECEIVER_SSRC
, 1, INT_MAX
);
94 audio_config
->incoming_feedback_ssrc
= input_rx
.GetIntInput();
97 void GetSsrcs(VideoSenderConfig
* video_config
) {
98 test::InputBuilder
input_tx(
99 "Choose video sender SSRC.", DEFAULT_VIDEO_SENDER_SSRC
, 1, INT_MAX
);
100 video_config
->sender_ssrc
= input_tx
.GetIntInput();
102 test::InputBuilder
input_rx(
103 "Choose video receiver SSRC.", DEFAULT_VIDEO_RECEIVER_SSRC
, 1, INT_MAX
);
104 video_config
->incoming_feedback_ssrc
= input_rx
.GetIntInput();
107 void GetPayloadtype(AudioSenderConfig
* audio_config
) {
108 test::InputBuilder
input(
109 "Choose audio sender payload type.", DEFAULT_AUDIO_PAYLOAD_TYPE
, 96, 127);
110 audio_config
->rtp_config
.payload_type
= input
.GetIntInput();
113 AudioSenderConfig
GetAudioSenderConfig() {
114 AudioSenderConfig audio_config
;
116 GetSsrcs(&audio_config
);
117 GetPayloadtype(&audio_config
);
119 audio_config
.rtcp_c_name
= "audio_sender@a.b.c.d";
121 VLOG(0) << "Using OPUS 48Khz stereo at 64kbit/s";
122 audio_config
.use_external_encoder
= false;
123 audio_config
.frequency
= kAudioSamplingFrequency
;
124 audio_config
.channels
= kAudioChannels
;
125 audio_config
.bitrate
= 64000;
126 audio_config
.codec
= transport::kOpus
;
130 void GetPayloadtype(VideoSenderConfig
* video_config
) {
131 test::InputBuilder
input(
132 "Choose video sender payload type.", DEFAULT_VIDEO_PAYLOAD_TYPE
, 96, 127);
133 video_config
->rtp_config
.payload_type
= input
.GetIntInput();
136 void GetVideoCodecSize(VideoSenderConfig
* video_config
) {
137 test::InputBuilder
input_width(
138 "Choose video width.", DEFAULT_VIDEO_CODEC_WIDTH
, 144, 1920);
139 video_config
->width
= input_width
.GetIntInput();
141 test::InputBuilder
input_height(
142 "Choose video height.", DEFAULT_VIDEO_CODEC_HEIGHT
, 176, 1080);
143 video_config
->height
= input_height
.GetIntInput();
146 void GetVideoBitrates(VideoSenderConfig
* video_config
) {
147 test::InputBuilder
input_start_br(
148 "Choose start bitrate[kbps].", DEFAULT_VIDEO_CODEC_BITRATE
, 0, INT_MAX
);
149 video_config
->start_bitrate
= input_start_br
.GetIntInput() * 1000;
151 test::InputBuilder
input_max_br(
152 "Choose max bitrate[kbps].", DEFAULT_VIDEO_CODEC_MAX_BITRATE
, 0, INT_MAX
);
153 video_config
->max_bitrate
= input_max_br
.GetIntInput() * 1000;
155 test::InputBuilder
input_min_br(
156 "Choose min bitrate[kbps].", DEFAULT_VIDEO_CODEC_MIN_BITRATE
, 0, INT_MAX
);
157 video_config
->min_bitrate
= input_min_br
.GetIntInput() * 1000;
160 VideoSenderConfig
GetVideoSenderConfig() {
161 VideoSenderConfig video_config
;
163 GetSsrcs(&video_config
);
164 GetPayloadtype(&video_config
);
165 GetVideoCodecSize(&video_config
);
166 GetVideoBitrates(&video_config
);
168 video_config
.rtcp_c_name
= "video_sender@a.b.c.d";
170 video_config
.use_external_encoder
= false;
172 VLOG(0) << "Using VP8 at 30 fps";
173 video_config
.min_qp
= 4;
174 video_config
.max_qp
= 40;
175 video_config
.max_frame_rate
= 30;
176 video_config
.codec
= transport::kVp8
;
177 video_config
.max_number_of_video_buffers_used
= 1;
178 video_config
.number_of_cores
= 1;
184 SendProcess(scoped_refptr
<base::SingleThreadTaskRunner
> thread_proxy
,
185 base::TickClock
* clock
,
186 const VideoSenderConfig
& video_config
,
187 FrameInput
* frame_input
)
188 : test_app_thread_proxy_(thread_proxy
),
189 video_config_(video_config
),
190 audio_diff_(kFrameTimerMs
),
191 frame_input_(frame_input
),
196 weak_factory_(this) {
197 audio_bus_factory_
.reset(new TestAudioBusFactory(kAudioChannels
,
198 kAudioSamplingFrequency
,
201 if (ReadFromFile()) {
202 std::string video_file_name
= GetVideoFile();
203 video_file_
= fopen(video_file_name
.c_str(), "r");
204 if (video_file_
== NULL
) {
205 VLOG(1) << "Failed to open file";
219 // Make sure that we don't drift.
220 int num_10ms_blocks
= audio_diff_
/ 10;
222 audio_diff_
+= kFrameTimerMs
- num_10ms_blocks
* 10;
224 scoped_ptr
<AudioBus
> audio_bus(audio_bus_factory_
->NextAudioBus(
225 base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks
));
226 AudioBus
* const audio_bus_ptr
= audio_bus
.get();
227 frame_input_
->InsertAudio(
230 base::Bind(&OwnThatAudioBus
, base::Passed(&audio_bus
)));
232 gfx::Size
size(video_config_
.width
, video_config_
.height
);
233 // TODO(mikhal): Use the provided timestamp.
234 if (start_time_
.is_null())
235 start_time_
= clock_
->NowTicks();
236 base::TimeDelta time_diff
= clock_
->NowTicks() - start_time_
;
237 scoped_refptr
<media::VideoFrame
> video_frame
=
238 media::VideoFrame::CreateFrame(
239 VideoFrame::I420
, size
, gfx::Rect(size
), size
, time_diff
);
241 if (!PopulateVideoFrameFromFile(video_frame
, video_file_
))
244 PopulateVideoFrame(video_frame
, synthetic_count_
);
248 // Time the sending of the frame to match the set frame rate.
249 // Sleep if that time has yet to elapse.
250 base::TimeTicks now
= clock_
->NowTicks();
251 base::TimeDelta video_frame_time
=
252 base::TimeDelta::FromMilliseconds(kFrameTimerMs
);
253 base::TimeDelta elapsed_time
= now
- send_time_
;
254 if (elapsed_time
< video_frame_time
) {
255 VLOG(1) << "Wait" << (video_frame_time
- elapsed_time
).InMilliseconds();
256 test_app_thread_proxy_
->PostDelayedTask(
258 base::Bind(&SendProcess::SendVideoFrameOnTime
,
259 base::Unretained(this),
261 video_frame_time
- elapsed_time
);
263 test_app_thread_proxy_
->PostTask(
265 base::Bind(&SendProcess::SendVideoFrameOnTime
,
266 base::Unretained(this),
271 void SendVideoFrameOnTime(scoped_refptr
<media::VideoFrame
> video_frame
) {
272 send_time_
= clock_
->NowTicks();
273 frame_input_
->InsertRawVideoFrame(video_frame
, send_time_
);
274 test_app_thread_proxy_
->PostTask(
275 FROM_HERE
, base::Bind(&SendProcess::SendFrame
, base::Unretained(this)));
279 scoped_refptr
<base::SingleThreadTaskRunner
> test_app_thread_proxy_
;
280 const VideoSenderConfig video_config_
;
282 const scoped_refptr
<FrameInput
> frame_input_
;
284 uint8 synthetic_count_
;
285 base::TickClock
* const clock_
; // Not owned by this class.
286 base::TimeTicks start_time_
;
287 base::TimeTicks send_time_
;
288 scoped_ptr
<TestAudioBusFactory
> audio_bus_factory_
;
289 base::WeakPtrFactory
<SendProcess
> weak_factory_
;
296 void UpdateCastTransportStatus(
297 media::cast::transport::CastTransportStatus status
) {}
299 void InitializationResult(media::cast::CastInitializationStatus result
) {
300 CHECK_EQ(result
, media::cast::STATUS_INITIALIZED
);
301 VLOG(1) << "Cast Sender initialized";
304 net::IPEndPoint
CreateUDPAddress(std::string ip_str
, int port
) {
305 net::IPAddressNumber ip_number
;
306 CHECK(net::ParseIPLiteralToNumber(ip_str
, &ip_number
));
307 return net::IPEndPoint(ip_number
, port
);
312 int main(int argc
, char** argv
) {
313 base::AtExitManager at_exit
;
314 VLOG(1) << "Cast Sender";
315 base::Thread
test_thread("Cast sender test app thread");
316 base::Thread
audio_thread("Cast audio encoder thread");
317 base::Thread
video_thread("Cast video encoder thread");
319 audio_thread
.Start();
320 video_thread
.Start();
322 scoped_ptr
<base::TickClock
> clock(new base::DefaultTickClock());
323 base::MessageLoopForIO io_message_loop
;
326 media::cast::GetPort(&remote_port
);
328 std::string remote_ip_address
=
329 media::cast::GetIpAddress("Enter receiver IP.");
331 media::cast::AudioSenderConfig audio_config
=
332 media::cast::GetAudioSenderConfig();
333 media::cast::VideoSenderConfig video_config
=
334 media::cast::GetVideoSenderConfig();
336 // Setting up transport config.
337 media::cast::transport::CastTransportConfig config
;
338 config
.receiver_endpoint
= CreateUDPAddress(remote_ip_address
, remote_port
);
339 config
.local_endpoint
= CreateUDPAddress("0.0.0.0", 0);
340 config
.audio_ssrc
= audio_config
.sender_ssrc
;
341 config
.video_ssrc
= video_config
.sender_ssrc
;
342 config
.audio_rtp_config
= audio_config
.rtp_config
;
343 config
.video_rtp_config
= video_config
.rtp_config
;
345 scoped_ptr
<media::cast::transport::CastTransportSender
> transport_sender(
346 media::cast::transport::CastTransportSender::CreateCastTransportSender(
349 base::Bind(&UpdateCastTransportStatus
),
350 io_message_loop
.message_loop_proxy()));
352 // Enable main and send side threads only. Disable logging.
353 // Running transport on the main thread.
354 scoped_refptr
<media::cast::CastEnvironment
> cast_environment(
355 new media::cast::CastEnvironment(
357 io_message_loop
.message_loop_proxy(),
358 audio_thread
.message_loop_proxy(),
360 video_thread
.message_loop_proxy(),
362 io_message_loop
.message_loop_proxy(),
363 media::cast::GetDefaultCastSenderLoggingConfig()));
365 scoped_ptr
<media::cast::CastSender
> cast_sender(
366 media::cast::CastSender::CreateCastSender(
370 NULL
, // gpu_factories.
371 base::Bind(&InitializationResult
),
372 transport_sender
.get()));
374 transport_sender
->SetPacketReceiver(cast_sender
->packet_receiver());
376 media::cast::FrameInput
* frame_input
= cast_sender
->frame_input();
377 scoped_ptr
<media::cast::SendProcess
> send_process(
378 new media::cast::SendProcess(test_thread
.message_loop_proxy(),
379 cast_environment
->Clock(),
383 test_thread
.message_loop_proxy()->PostTask(
385 base::Bind(&media::cast::SendProcess::SendFrame
,
386 base::Unretained(send_process
.get())));
388 io_message_loop
.Run();