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)#include <algorithm> 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <climits> 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <cstdarg> 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <cstdio> 9c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include <deque> 105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <map> 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <string> 12c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include <utility> 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/at_exit.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/command_line.h" 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/logging.h" 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/memory/ref_counted.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/memory/scoped_ptr.h" 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/message_loop/message_loop.h" 20c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/synchronization/lock.h" 21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/synchronization/waitable_event.h" 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/threading/thread.h" 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/time/default_tick_clock.h" 24c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/timer/timer.h" 25c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/audio/audio_io.h" 26c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/audio/audio_manager.h" 27c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/audio/audio_parameters.h" 28c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/audio/fake_audio_log_factory.h" 29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/base/audio_bus.h" 30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "media/base/channel_layout.h" 31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/base/video_frame.h" 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/cast/cast_config.h" 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/cast/cast_environment.h" 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/cast/cast_receiver.h" 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/cast/logging/logging_defines.h" 36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "media/cast/net/udp_transport.h" 375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "media/cast/test/utility/audio_utility.h" 385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "media/cast/test/utility/barcode.h" 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "media/cast/test/utility/default_config.h" 40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "media/cast/test/utility/in_process_receiver.h" 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "media/cast/test/utility/input_builder.h" 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "media/cast/test/utility/standalone_cast_environment.h" 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "net/base/net_util.h" 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/cast/test/linux_output_window.h" 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace media { 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace cast { 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Settings chosen to match default sender settings. 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#define DEFAULT_SEND_PORT "0" 54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define DEFAULT_RECEIVE_PORT "2344" 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#define DEFAULT_SEND_IP "0.0.0.0" 5623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#define DEFAULT_AUDIO_FEEDBACK_SSRC "2" 5723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#define DEFAULT_AUDIO_INCOMING_SSRC "1" 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define DEFAULT_AUDIO_PAYLOAD_TYPE "127" 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define DEFAULT_VIDEO_FEEDBACK_SSRC "12" 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define DEFAULT_VIDEO_INCOMING_SSRC "11" 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define DEFAULT_VIDEO_PAYLOAD_TYPE "96" 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 64c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst char* kVideoWindowWidth = "1280"; 65c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst char* kVideoWindowHeight = "720"; 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void GetPorts(int* tx_port, int* rx_port) { 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder tx_input( 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *tx_port = tx_input.GetIntInput(); 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder rx_input( 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *rx_port = rx_input.GetIntInput(); 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::string GetIpAddress(const std::string display_text) { 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder input(display_text, DEFAULT_SEND_IP, INT_MIN, INT_MAX); 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) std::string ip_address = input.GetStringInput(); 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Ensure IP address is either the default value or in correct form. 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) while (ip_address != DEFAULT_SEND_IP && 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::count(ip_address.begin(), ip_address.end(), '.') != 3) { 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ip_address = input.GetStringInput(); 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return ip_address; 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GetAudioSsrcs(FrameReceiverConfig* audio_config) { 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder input_tx( 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Choose audio sender SSRC.", DEFAULT_AUDIO_FEEDBACK_SSRC, 1, INT_MAX); 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_config->feedback_ssrc = input_tx.GetIntInput(); 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder input_rx( 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Choose audio receiver SSRC.", DEFAULT_AUDIO_INCOMING_SSRC, 1, INT_MAX); 96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch audio_config->incoming_ssrc = input_rx.GetIntInput(); 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GetVideoSsrcs(FrameReceiverConfig* video_config) { 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder input_tx( 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Choose video sender SSRC.", DEFAULT_VIDEO_FEEDBACK_SSRC, 1, INT_MAX); 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) video_config->feedback_ssrc = input_tx.GetIntInput(); 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) test::InputBuilder input_rx( 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Choose video receiver SSRC.", DEFAULT_VIDEO_INCOMING_SSRC, 1, INT_MAX); 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) video_config->incoming_ssrc = input_rx.GetIntInput(); 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 110c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid GetWindowSize(int* width, int* height) { 111c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Resolution values based on sender settings 112c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch test::InputBuilder input_w( 113c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch "Choose window width.", kVideoWindowWidth, 144, 1920); 114c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *width = input_w.GetIntInput(); 115c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 116c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch test::InputBuilder input_h( 117c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch "Choose window height.", kVideoWindowHeight, 176, 1080); 118c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *height = input_h.GetIntInput(); 119c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch} 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 121c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GetAudioPayloadtype(FrameReceiverConfig* audio_config) { 123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) test::InputBuilder input("Choose audio receiver payload type.", 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DEFAULT_AUDIO_PAYLOAD_TYPE, 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 96, 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 127); 127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_config->rtp_payload_type = input.GetIntInput(); 128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)FrameReceiverConfig GetAudioReceiverConfig() { 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) FrameReceiverConfig audio_config = GetDefaultAudioReceiverConfig(); 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) GetAudioSsrcs(&audio_config); 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) GetAudioPayloadtype(&audio_config); 1340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch audio_config.rtp_max_delay_ms = 300; 135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return audio_config; 136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GetVideoPayloadtype(FrameReceiverConfig* video_config) { 139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) test::InputBuilder input("Choose video receiver payload type.", 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DEFAULT_VIDEO_PAYLOAD_TYPE, 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 96, 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 127); 143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) video_config->rtp_payload_type = input.GetIntInput(); 144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)FrameReceiverConfig GetVideoReceiverConfig() { 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) FrameReceiverConfig video_config = GetDefaultVideoReceiverConfig(); 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) GetVideoSsrcs(&video_config); 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) GetVideoPayloadtype(&video_config); 1500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch video_config.rtp_max_delay_ms = 300; 151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return video_config; 152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AudioParameters ToAudioParameters(const FrameReceiverConfig& config) { 155c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const int samples_in_10ms = config.frequency / 100; 156c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, 157c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch GuessChannelLayout(config.channels), 158c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch config.frequency, 32, samples_in_10ms); 159c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch} 160c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 161c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// An InProcessReceiver that renders video frames to a LinuxOutputWindow and 162c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// audio frames via Chromium's audio stack. 163c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// 164c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// InProcessReceiver pushes audio and video frames to this subclass, and these 165c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// frames are pushed into a queue. Then, for audio, the Chromium audio stack 166c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// will make polling calls on a separate, unknown thread whereby audio frames 167c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// are pulled out of the audio queue as needed. For video, however, NaivePlayer 168c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// is responsible for scheduling updates to the screen itself. For both, the 169c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// queues are pruned (i.e., received frames are skipped) when the system is not 170c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// able to play back as fast as frames are entering the queue. 171c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// 172c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// This is NOT a good reference implementation for a Cast receiver player since: 173c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// 1. It only skips frames to handle slower-than-expected playout, or halts 174c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// playback to handle frame underruns. 175c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// 2. It makes no attempt to synchronize the timing of playout of the video 176c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// frames with the audio frames. 177c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// 3. It does nothing to smooth or hide discontinuities in playback due to 178c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// timing issues or missing frames. 179c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochclass NaivePlayer : public InProcessReceiver, 180c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch public AudioOutputStream::AudioSourceCallback { 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public: 182c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch NaivePlayer(const scoped_refptr<CastEnvironment>& cast_environment, 183c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const net::IPEndPoint& local_end_point, 184c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const net::IPEndPoint& remote_end_point, 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const FrameReceiverConfig& audio_config, 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const FrameReceiverConfig& video_config, 187c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int window_width, 188c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int window_height) 189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) : InProcessReceiver(cast_environment, 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) local_end_point, 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) remote_end_point, 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) audio_config, 193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) video_config), 194c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Maximum age is the duration of 3 video frames. 3 was chosen 195c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // arbitrarily, but seems to work well. 196c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch max_frame_age_(base::TimeDelta::FromSeconds(1) * 3 / 197c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_config.max_frame_rate), 1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 199c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch render_(0, 0, window_width, window_height, "Cast_receiver"), 2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 201c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch num_video_frames_processed_(0), 202c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch num_audio_frames_processed_(0), 203c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_start_(-1) {} 204c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 205c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual ~NaivePlayer() {} 206c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 207c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual void Start() OVERRIDE { 208c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch AudioManager::Get()->GetTaskRunner()->PostTask( 209c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch FROM_HERE, 210c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Bind(&NaivePlayer::StartAudioOutputOnAudioManagerThread, 211c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Unretained(this))); 212c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Note: No need to wait for audio polling to start since the push-and-pull 213c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // mechanism is synchronized via the |audio_playout_queue_|. 214c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch InProcessReceiver::Start(); 215c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 216c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 217c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual void Stop() OVERRIDE { 218c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // First, stop audio output to the Chromium audio stack. 219c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::WaitableEvent done(false, false); 220c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(!AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 221c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch AudioManager::Get()->GetTaskRunner()->PostTask( 222c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch FROM_HERE, 223c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Bind(&NaivePlayer::StopAudioOutputOnAudioManagerThread, 224c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Unretained(this), 225c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch &done)); 226c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch done.Wait(); 227c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 228c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Now, stop receiving new frames. 229c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch InProcessReceiver::Stop(); 230c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 231c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Finally, clear out any frames remaining in the queues. 232c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch while (!audio_playout_queue_.empty()) { 233c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const scoped_ptr<AudioBus> to_be_deleted( 234c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.front().second); 235c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.pop_front(); 236c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 237c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.clear(); 238c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 239c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 240c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch private: 241c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch void StartAudioOutputOnAudioManagerThread() { 242c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 243c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(!audio_output_stream_); 244c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_.reset(AudioManager::Get()->MakeAudioOutputStreamProxy( 245c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ToAudioParameters(audio_config()), "")); 246c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (audio_output_stream_.get() && audio_output_stream_->Open()) { 247c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_->Start(this); 248c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } else { 249c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch LOG(ERROR) << "Failed to open an audio output stream. " 250c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << "Audio playback disabled."; 251c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_.reset(); 252c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 253c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 254c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 255c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch void StopAudioOutputOnAudioManagerThread(base::WaitableEvent* done) { 256c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 257c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (audio_output_stream_.get()) { 258c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_->Stop(); 259c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_->Close(); 260c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_output_stream_.reset(); 261c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 262c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch done->Signal(); 263c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 264c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 265c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch //////////////////////////////////////////////////////////////////// 266c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // InProcessReceiver overrides. 267c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 268c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual void OnVideoFrame(const scoped_refptr<VideoFrame>& video_frame, 269c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeTicks& playout_time, 270c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch bool is_continuous) OVERRIDE { 271c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); 272c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch LOG_IF(WARNING, !is_continuous) 273c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << "Video: Discontinuity in received frames."; 274c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.push_back(std::make_pair(playout_time, video_frame)); 275c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ScheduleVideoPlayout(); 2765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu uint16 frame_no; 2775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (media::cast::test::DecodeBarcode(video_frame, &frame_no)) { 2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu video_play_times_.insert( 2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::pair<uint16, base::TimeTicks>(frame_no, playout_time)); 2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu VLOG(2) << "Barcode decode failed!"; 2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 283c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 284c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 285c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual void OnAudioFrame(scoped_ptr<AudioBus> audio_frame, 286c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeTicks& playout_time, 287c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch bool is_continuous) OVERRIDE { 288c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); 289c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch LOG_IF(WARNING, !is_continuous) 290c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << "Audio: Discontinuity in received frames."; 291c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::AutoLock auto_lock(audio_lock_); 2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu uint16 frame_no; 2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (media::cast::DecodeTimestamp(audio_frame->channel(0), 2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_frame->frames(), 2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu &frame_no)) { 2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Since there are lots of audio packets with the same frame_no, 2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // we really want to make sure that we get the playout_time from 2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // the first one. If is_continous is true, then it's possible 2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // that we already missed the first one. 3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (is_continuous && frame_no == last_audio_frame_no_ + 1) { 3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_play_times_.insert( 3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::pair<uint16, base::TimeTicks>(frame_no, playout_time)); 3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu last_audio_frame_no_ = frame_no; 3055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu VLOG(2) << "Audio decode failed!"; 3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu last_audio_frame_no_ = -2; 3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 309c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.push_back( 310c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch std::make_pair(playout_time, audio_frame.release())); 311c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 312c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 313c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // End of InProcessReceiver overrides. 314c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch //////////////////////////////////////////////////////////////////// 315c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 316c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch //////////////////////////////////////////////////////////////////// 317c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // AudioSourceCallback implementation. 318c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 319c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual int OnMoreData(AudioBus* dest, AudioBuffersState buffers_state) 320c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch OVERRIDE { 321c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Note: This method is being invoked by a separate thread unknown to us 322c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // (i.e., outside of CastEnvironment). 323c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 324c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int samples_remaining = dest->frames(); 325c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 326c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch while (samples_remaining > 0) { 327c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Get next audio frame ready for playout. 328c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (!currently_playing_audio_frame_.get()) { 329c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::AutoLock auto_lock(audio_lock_); 330c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 331c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Prune the queue, skipping entries that are too old. 332c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // TODO(miu): Use |buffers_state| to account for audio buffering delays 333c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // upstream. 334c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeTicks earliest_time_to_play = 335c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch cast_env()->Clock()->NowTicks() - max_frame_age_; 336c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch while (!audio_playout_queue_.empty() && 337c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.front().first < earliest_time_to_play) { 338c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch PopOneAudioFrame(true); 339c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 340c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (audio_playout_queue_.empty()) 341c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch break; 342c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 343c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_ = PopOneAudioFrame(false).Pass(); 344c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_start_ = 0; 345c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 346c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 347c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Copy some or all of the samples in |currently_playing_audio_frame_| to 348c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // |dest|. Once all samples in |currently_playing_audio_frame_| have been 349c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // consumed, release it. 350c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const int num_samples_to_copy = 351c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch std::min(samples_remaining, 352c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_->frames() - 353c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_start_); 354c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_->CopyPartialFramesTo( 355c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_start_, 356c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch num_samples_to_copy, 357c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 0, 358c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch dest); 359c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch samples_remaining -= num_samples_to_copy; 360c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_start_ += num_samples_to_copy; 361c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (currently_playing_audio_frame_start_ == 362c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_->frames()) { 363c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch currently_playing_audio_frame_.reset(); 364c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 365c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 366c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 367c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // If |dest| has not been fully filled, then an underrun has occurred; and 368c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // fill the remainder of |dest| with zeros. 369c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (samples_remaining > 0) { 370c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Note: Only logging underruns after the first frame has been received. 371c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch LOG_IF(WARNING, currently_playing_audio_frame_start_ != -1) 372c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << "Audio: Playback underrun of " << samples_remaining << " samples!"; 373c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch dest->ZeroFramesPartial(dest->frames() - samples_remaining, 374c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch samples_remaining); 375c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 376c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 377c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return dest->frames(); 378c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 379c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 380c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch virtual void OnError(AudioOutputStream* stream) OVERRIDE { 381c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch LOG(ERROR) << "AudioOutputStream reports an error. " 382c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << "Playback is unlikely to continue."; 383c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 384c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 385c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // End of AudioSourceCallback implementation. 386c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch //////////////////////////////////////////////////////////////////// 387c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 388c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch void ScheduleVideoPlayout() { 389c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); 390c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 391c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Prune the queue, skipping entries that are too old. 392c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeTicks now = cast_env()->Clock()->NowTicks(); 393c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeTicks earliest_time_to_play = now - max_frame_age_; 394c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch while (!video_playout_queue_.empty() && 395c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.front().first < earliest_time_to_play) { 396c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch PopOneVideoFrame(true); 397c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 398c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 399c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // If the queue is not empty, schedule playout of its first frame. 400c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (video_playout_queue_.empty()) { 401c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_timer_.Stop(); 402c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } else { 403c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_timer_.Start( 404c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch FROM_HERE, 405c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.front().first - now, 406c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Bind(&NaivePlayer::PlayNextVideoFrame, 407c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Unretained(this))); 408c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 409c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 410f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 411c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch void PlayNextVideoFrame() { 412c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); 413c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (!video_playout_queue_.empty()) { 414c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const scoped_refptr<VideoFrame> video_frame = PopOneVideoFrame(false); 4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 416c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch render_.RenderFrame(video_frame); 4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 418f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 419c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ScheduleVideoPlayout(); 4205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu CheckAVSync(); 421f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 422f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 423c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_refptr<VideoFrame> PopOneVideoFrame(bool is_being_skipped) { 424c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); 425c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 426c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (is_being_skipped) { 427cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "VideoFrame[" << num_video_frames_processed_ 428cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " (dt=" << (video_playout_queue_.front().first - 429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_video_playout_time_).InMicroseconds() 430cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " usec)]: Skipped."; 431c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } else { 432cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "VideoFrame[" << num_video_frames_processed_ 433cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " (dt=" << (video_playout_queue_.front().first - 434cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_video_playout_time_).InMicroseconds() 435cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " usec)]: Playing " 436c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << (cast_env()->Clock()->NowTicks() - 437c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.front().first).InMicroseconds() 438c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << " usec later than intended."; 439f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 440c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_video_playout_time_ = video_playout_queue_.front().first; 442c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const scoped_refptr<VideoFrame> ret = video_playout_queue_.front().second; 443c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_playout_queue_.pop_front(); 444c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ++num_video_frames_processed_; 445c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return ret; 446f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 447f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 448c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_ptr<AudioBus> PopOneAudioFrame(bool was_skipped) { 449c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_lock_.AssertAcquired(); 450c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 451c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (was_skipped) { 452cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "AudioFrame[" << num_audio_frames_processed_ 453cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " (dt=" << (audio_playout_queue_.front().first - 454cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_audio_playout_time_).InMicroseconds() 455cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " usec)]: Skipped."; 456c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } else { 457cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "AudioFrame[" << num_audio_frames_processed_ 458cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " (dt=" << (audio_playout_queue_.front().first - 459cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_audio_playout_time_).InMicroseconds() 460cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << " usec)]: Playing " 461c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << (cast_env()->Clock()->NowTicks() - 462c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.front().first).InMicroseconds() 463c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch << " usec later than intended."; 464c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 465c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 466cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) last_popped_audio_playout_time_ = audio_playout_queue_.front().first; 467c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_ptr<AudioBus> ret(audio_playout_queue_.front().second); 468c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_playout_queue_.pop_front(); 469c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ++num_audio_frames_processed_; 470c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return ret.Pass(); 471c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch } 472c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 4735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu void CheckAVSync() { 4745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (video_play_times_.size() > 30 && 4755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_play_times_.size() > 30) { 4765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu size_t num_events = 0; 4775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base::TimeDelta delta; 4785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::map<uint16, base::TimeTicks>::iterator audio_iter, video_iter; 4795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (video_iter = video_play_times_.begin(); 4805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu video_iter != video_play_times_.end(); 4815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu ++video_iter) { 4825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_iter = audio_play_times_.find(video_iter->first); 4835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (audio_iter != audio_play_times_.end()) { 4845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu num_events++; 4855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Positive values means audio is running behind video. 4865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu delta += audio_iter->second - video_iter->second; 4875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (num_events > 30) { 4915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu VLOG(0) << "Audio behind by: " 4925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu << (delta / num_events).InMilliseconds() 4935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu << "ms"; 4945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu video_play_times_.clear(); 4955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_play_times_.clear(); 4965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (video_play_times_.size() + audio_play_times_.size() > 500) { 4985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // We are decoding audio or video timestamps, but not both, clear it out. 4995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu video_play_times_.clear(); 5005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu audio_play_times_.clear(); 5015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 5025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 5035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 504c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Frames in the queue older than this (relative to NowTicks()) will be 505c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // dropped (i.e., playback is falling behind). 506c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const base::TimeDelta max_frame_age_; 507c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 508c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Outputs created, started, and destroyed by this NaivePlayer. 5095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 510f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) test::LinuxOutputWindow render_; 5115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 512c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_ptr<AudioOutputStream> audio_output_stream_; 513c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 514c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Video playout queue. 515c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch typedef std::pair<base::TimeTicks, scoped_refptr<VideoFrame> > 516c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch VideoQueueEntry; 517c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch std::deque<VideoQueueEntry> video_playout_queue_; 518cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::TimeTicks last_popped_video_playout_time_; 519c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int64 num_video_frames_processed_; 520c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 521c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::OneShotTimer<NaivePlayer> video_playout_timer_; 522c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 523c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Audio playout queue, synchronized by |audio_lock_|. 524c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base::Lock audio_lock_; 525c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch typedef std::pair<base::TimeTicks, AudioBus*> AudioQueueEntry; 526c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch std::deque<AudioQueueEntry> audio_playout_queue_; 527cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::TimeTicks last_popped_audio_playout_time_; 528c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int64 num_audio_frames_processed_; 529c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 530c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // These must only be used on the audio thread calling OnMoreData(). 531c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_ptr<AudioBus> currently_playing_audio_frame_; 532c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int currently_playing_audio_frame_start_; 5335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 5345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::map<uint16, base::TimeTicks> audio_play_times_; 5355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::map<uint16, base::TimeTicks> video_play_times_; 5365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu int32 last_audio_frame_no_; 537f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}; 538f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 539f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace cast 540f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace media 541f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 542f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int main(int argc, char** argv) { 543f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::AtExitManager at_exit; 5445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) CommandLine::Init(argc, argv); 5455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InitLogging(logging::LoggingSettings()); 5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<media::cast::CastEnvironment> cast_environment( 548effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch new media::cast::StandaloneCastEnvironment); 549f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 550c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Start up Chromium audio system. 551c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch media::FakeAudioLogFactory fake_audio_log_factory_; 552c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const scoped_ptr<media::AudioManager> audio_manager( 553c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch media::AudioManager::Create(&fake_audio_log_factory_)); 554c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch CHECK(media::AudioManager::Get()); 555c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 556cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) media::cast::FrameReceiverConfig audio_config = 557f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::cast::GetAudioReceiverConfig(); 558cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) media::cast::FrameReceiverConfig video_config = 559f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::cast::GetVideoReceiverConfig(); 560f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 561c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Determine local and remote endpoints. 5625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int remote_port, local_port; 5635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) media::cast::GetPorts(&remote_port, &local_port); 5645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!local_port) { 5655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "Invalid local port."; 5665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 1; 5675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 5685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string remote_ip_address = media::cast::GetIpAddress("Enter remote IP."); 5695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string local_ip_address = media::cast::GetIpAddress("Enter local IP."); 5705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) net::IPAddressNumber remote_ip_number; 5715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) net::IPAddressNumber local_ip_number; 5725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!net::ParseIPLiteralToNumber(remote_ip_address, &remote_ip_number)) { 5735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "Invalid remote IP address."; 5745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 1; 5755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 5765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { 5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "Invalid local IP address."; 5785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 1; 5795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 5805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) net::IPEndPoint remote_end_point(remote_ip_number, remote_port); 5815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) net::IPEndPoint local_end_point(local_ip_number, local_port); 5825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 583c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Create and start the player. 584c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int window_width = 0; 585c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch int window_height = 0; 5865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#if defined(USE_X11) 587c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch media::cast::GetWindowSize(&window_width, &window_height); 5885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif // defined(USE_X11) 589c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch media::cast::NaivePlayer player(cast_environment, 590c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch local_end_point, 591c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch remote_end_point, 592c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch audio_config, 593c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch video_config, 594c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch window_width, 595c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch window_height); 596c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch player.Start(); 597a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 598a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::MessageLoop().Run(); // Run forever (i.e., until SIGTERM). 599a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) NOTREACHED(); 600f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 0; 601f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 602