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