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