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