Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <climits> | 6 #include <climits> |
| 7 #include <cstdarg> | 7 #include <cstdarg> |
| 8 #include <cstdio> | 8 #include <cstdio> |
| 9 #include <deque> | |
| 9 #include <string> | 10 #include <string> |
| 11 #include <utility> | |
| 10 | 12 |
| 11 #include "base/at_exit.h" | 13 #include "base/at_exit.h" |
| 12 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 13 #include "base/logging.h" | 15 #include "base/logging.h" |
| 14 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
| 15 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 16 #include "base/message_loop/message_loop.h" | 18 #include "base/message_loop/message_loop.h" |
| 19 #include "base/synchronization/lock.h" | |
| 20 #include "base/synchronization/waitable_event.h" | |
| 17 #include "base/threading/thread.h" | 21 #include "base/threading/thread.h" |
| 18 #include "base/time/default_tick_clock.h" | 22 #include "base/time/default_tick_clock.h" |
| 23 #include "base/timer/timer.h" | |
| 24 #include "media/audio/audio_io.h" | |
| 25 #include "media/audio/audio_manager.h" | |
| 26 #include "media/audio/audio_parameters.h" | |
| 27 #include "media/audio/fake_audio_log_factory.h" | |
| 19 #include "media/base/audio_bus.h" | 28 #include "media/base/audio_bus.h" |
| 29 #include "media/base/channel_layout.h" | |
| 20 #include "media/base/video_frame.h" | 30 #include "media/base/video_frame.h" |
| 21 #include "media/cast/cast_config.h" | 31 #include "media/cast/cast_config.h" |
| 22 #include "media/cast/cast_environment.h" | 32 #include "media/cast/cast_environment.h" |
| 23 #include "media/cast/cast_receiver.h" | 33 #include "media/cast/cast_receiver.h" |
| 24 #include "media/cast/logging/logging_defines.h" | 34 #include "media/cast/logging/logging_defines.h" |
| 25 #include "media/cast/test/utility/default_config.h" | 35 #include "media/cast/test/utility/default_config.h" |
| 26 #include "media/cast/test/utility/in_process_receiver.h" | 36 #include "media/cast/test/utility/in_process_receiver.h" |
| 27 #include "media/cast/test/utility/input_builder.h" | 37 #include "media/cast/test/utility/input_builder.h" |
| 28 #include "media/cast/test/utility/standalone_cast_environment.h" | 38 #include "media/cast/test/utility/standalone_cast_environment.h" |
| 29 #include "media/cast/transport/transport/udp_transport.h" | 39 #include "media/cast/transport/transport/udp_transport.h" |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 video_config->rtp_payload_type = input.GetIntInput(); | 130 video_config->rtp_payload_type = input.GetIntInput(); |
| 121 } | 131 } |
| 122 | 132 |
| 123 VideoReceiverConfig GetVideoReceiverConfig() { | 133 VideoReceiverConfig GetVideoReceiverConfig() { |
| 124 VideoReceiverConfig video_config = GetDefaultVideoReceiverConfig(); | 134 VideoReceiverConfig video_config = GetDefaultVideoReceiverConfig(); |
| 125 GetSsrcs(&video_config); | 135 GetSsrcs(&video_config); |
| 126 GetPayloadtype(&video_config); | 136 GetPayloadtype(&video_config); |
| 127 return video_config; | 137 return video_config; |
| 128 } | 138 } |
| 129 | 139 |
| 130 // An InProcessReceiver that renders video frames to a LinuxOutputWindow. While | 140 AudioParameters ToAudioParameters(const AudioReceiverConfig& config) { |
| 131 // it does receive audio frames, it does not play them. | 141 const int samples_in_10ms = config.frequency / 100; |
| 132 class ReceiverDisplay : public InProcessReceiver { | 142 return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 143 GuessChannelLayout(config.channels), | |
| 144 config.frequency, 32, samples_in_10ms); | |
| 145 } | |
| 146 | |
| 147 // An InProcessReceiver that renders video frames to a LinuxOutputWindow and | |
| 148 // audio frames via Chromium's audio stack. | |
| 149 // | |
| 150 // InProcessReceiver pushes audio and video frames to this subclass, and these | |
| 151 // frames are pushed into a queue. Then, for audio, the Chromium audio stack | |
| 152 // will make polling calls on a separate, unknown thread whereby audio frames | |
| 153 // are pulled out of the audio queue as needed. For video, however, NaivePlayer | |
| 154 // is responsible for scheduling updates to the screen itself. For both, the | |
| 155 // queues are pruned (i.e., received frames are skipped) when the system is not | |
| 156 // able to play back as fast as frames are entering the queue. | |
| 157 // | |
| 158 // This is NOT a good reference implementation for a Cast receiver player since: | |
| 159 // 1. It only skips frames to handle slower-than-expected playout, or halts | |
| 160 // playback to handle frame underruns. | |
| 161 // 2. It makes no attempt to synchronize the timing of playout of the video | |
| 162 // frames with the audio frames. | |
| 163 // 3. It does nothing to smooth or hide discontinuities in playback due to | |
| 164 // timing issues or missing frames. | |
| 165 class NaivePlayer : public InProcessReceiver, | |
| 166 public AudioOutputStream::AudioSourceCallback { | |
| 133 public: | 167 public: |
| 134 ReceiverDisplay(const scoped_refptr<CastEnvironment>& cast_environment, | 168 NaivePlayer(const scoped_refptr<CastEnvironment>& cast_environment, |
| 135 const net::IPEndPoint& local_end_point, | 169 const net::IPEndPoint& local_end_point, |
| 136 const net::IPEndPoint& remote_end_point, | 170 const net::IPEndPoint& remote_end_point, |
| 137 const AudioReceiverConfig& audio_config, | 171 const AudioReceiverConfig& audio_config, |
| 138 const VideoReceiverConfig& video_config) | 172 const VideoReceiverConfig& video_config) |
| 139 : InProcessReceiver(cast_environment, | 173 : InProcessReceiver(cast_environment, |
| 140 local_end_point, | 174 local_end_point, |
| 141 remote_end_point, | 175 remote_end_point, |
| 142 audio_config, | 176 audio_config, |
| 143 video_config), | 177 video_config), |
| 178 max_frame_age_(base::TimeDelta::FromSeconds(1) * 3 / | |
|
imcheng
2014/04/09 01:34:45
Could you add a comment here about how you arrived
miu
2014/04/09 04:07:19
Done.
| |
| 179 video_config.max_frame_rate), | |
| 144 #if defined(OS_LINUX) | 180 #if defined(OS_LINUX) |
| 145 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), | 181 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), |
| 146 #endif // OS_LINUX | 182 #endif // OS_LINUX |
| 147 last_playout_time_(), | 183 num_video_frames_processed_(0), |
| 148 last_render_time_() { | 184 num_audio_frames_processed_(0), |
| 149 } | 185 currently_playing_audio_frame_start_(-1) {} |
| 150 | 186 |
| 151 virtual ~ReceiverDisplay() {} | 187 virtual ~NaivePlayer() {} |
| 152 | 188 |
| 153 protected: | 189 virtual void Start() OVERRIDE { |
| 154 virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame, | 190 AudioManager::Get()->GetTaskRunner()->PostTask( |
| 155 const base::TimeTicks& render_time, | 191 FROM_HERE, |
| 192 base::Bind(&NaivePlayer::StartAudioOutputOnAudioManagerThread, | |
| 193 base::Unretained(this))); | |
| 194 InProcessReceiver::Start(); | |
|
imcheng
2014/04/09 01:34:45
Do we need to wait for StartAudioOutputOnAudioMana
miu
2014/04/09 04:07:19
Added comment to explain.
| |
| 195 } | |
| 196 | |
| 197 virtual void Stop() OVERRIDE { | |
| 198 // First, stop audio output to the Chromium audio stack. | |
| 199 base::WaitableEvent done(false, false); | |
| 200 DCHECK(!AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); | |
|
imcheng
2014/04/09 01:34:45
Do we also need this DCHECK in Start()?
miu
2014/04/09 04:07:19
No, it doesn't matter there since the thread is no
| |
| 201 AudioManager::Get()->GetTaskRunner()->PostTask( | |
| 202 FROM_HERE, | |
| 203 base::Bind(&NaivePlayer::StopAudioOutputOnAudioManagerThread, | |
| 204 base::Unretained(this), | |
| 205 &done)); | |
| 206 done.Wait(); | |
| 207 | |
| 208 // Now, stop receiving new frames. | |
| 209 InProcessReceiver::Stop(); | |
| 210 | |
| 211 // Finally, clear out any frames remaining in the queues. | |
| 212 while (!audio_playout_queue_.empty()) { | |
|
imcheng
2014/04/09 01:34:45
If linked_ptr is used, then you won't have do this
| |
| 213 const scoped_ptr<AudioBus> to_be_deleted( | |
| 214 audio_playout_queue_.front().second); | |
| 215 audio_playout_queue_.pop_front(); | |
| 216 } | |
| 217 video_playout_queue_.clear(); | |
| 218 } | |
| 219 | |
| 220 private: | |
| 221 void StartAudioOutputOnAudioManagerThread() { | |
| 222 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); | |
| 223 audio_output_stream_.reset(AudioManager::Get()->MakeAudioOutputStreamProxy( | |
|
imcheng
2014/04/09 01:34:45
It looks like you will need to call Stop() and Clo
miu
2014/04/09 04:07:19
Instead, I added a DCHECK(!audio_output_stream_) s
| |
| 224 ToAudioParameters(audio_config()), "")); | |
| 225 if (audio_output_stream_.get() && audio_output_stream_->Open()) { | |
| 226 audio_output_stream_->Start(this); | |
| 227 } else { | |
| 228 LOG(ERROR) << "Failed to open an audio output stream. " | |
| 229 << "Audio playback disabled."; | |
| 230 audio_output_stream_.reset(); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 void StopAudioOutputOnAudioManagerThread(base::WaitableEvent* done) { | |
| 235 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); | |
| 236 if (audio_output_stream_.get()) { | |
| 237 audio_output_stream_->Stop(); | |
| 238 audio_output_stream_->Close(); | |
| 239 audio_output_stream_.reset(); | |
| 240 } | |
| 241 done->Signal(); | |
| 242 } | |
| 243 | |
| 244 //////////////////////////////////////////////////////////////////// | |
| 245 // InProcessReceiver overrides. | |
| 246 | |
| 247 virtual void OnVideoFrame(const scoped_refptr<VideoFrame>& video_frame, | |
| 248 const base::TimeTicks& playout_time, | |
| 156 bool is_continuous) OVERRIDE { | 249 bool is_continuous) OVERRIDE { |
| 157 #ifdef OS_LINUX | 250 DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); |
| 158 render_.RenderFrame(video_frame); | 251 LOG_IF(WARNING, !is_continuous) |
| 159 #endif // OS_LINUX | 252 << "Video: Discontinuity in received frames."; |
| 160 // Print out the delta between frames. | 253 video_playout_queue_.push_back(std::make_pair(playout_time, video_frame)); |
| 161 if (!last_render_time_.is_null()) { | 254 ScheduleVideoPlayout(); |
| 162 base::TimeDelta time_diff = render_time - last_render_time_; | |
| 163 VLOG(2) << "Size = " << video_frame->coded_size().ToString() | |
| 164 << "; RenderDelay[mS] = " << time_diff.InMilliseconds(); | |
| 165 } | |
| 166 last_render_time_ = render_time; | |
| 167 } | 255 } |
| 168 | 256 |
| 169 virtual void OnAudioFrame(scoped_ptr<AudioBus> audio_frame, | 257 virtual void OnAudioFrame(scoped_ptr<AudioBus> audio_frame, |
| 170 const base::TimeTicks& playout_time, | 258 const base::TimeTicks& playout_time, |
| 171 bool is_continuous) OVERRIDE { | 259 bool is_continuous) OVERRIDE { |
| 172 // For audio just print the playout delta between audio frames. | 260 DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); |
| 173 if (!last_playout_time_.is_null()) { | 261 LOG_IF(WARNING, !is_continuous) |
| 174 base::TimeDelta time_diff = playout_time - last_playout_time_; | 262 << "Audio: Discontinuity in received frames."; |
| 175 VLOG(2) << "SampleRate = " << audio_config().frequency | 263 base::AutoLock auto_lock(audio_lock_); |
| 176 << "; PlayoutDelay[mS] = " << time_diff.InMilliseconds(); | 264 audio_playout_queue_.push_back( |
| 177 } | 265 std::make_pair(playout_time, audio_frame.release())); |
| 178 last_playout_time_ = playout_time; | 266 } |
| 179 } | 267 |
| 180 | 268 // End of InProcessReceiver overrides. |
| 269 //////////////////////////////////////////////////////////////////// | |
| 270 | |
| 271 //////////////////////////////////////////////////////////////////// | |
| 272 // AudioSourceCallback implementation. | |
| 273 | |
| 274 virtual int OnMoreData(AudioBus* dest, AudioBuffersState buffers_state) | |
| 275 OVERRIDE { | |
| 276 // Note: This method is being invoked by a separate thread unknown to us | |
| 277 // (i.e., outside of CastEnvironment). | |
| 278 | |
| 279 int samples_remaining = dest->frames(); | |
| 280 | |
| 281 while (samples_remaining > 0) { | |
| 282 // Get next audio frame ready for playout. | |
| 283 if (!currently_playing_audio_frame_.get()) { | |
| 284 base::AutoLock auto_lock(audio_lock_); | |
| 285 | |
| 286 // Prune the queue, skipping entries that are too old. | |
| 287 // TODO(miu): Use |buffers_state| to account for audio buffering delays | |
| 288 // upstream. | |
| 289 const base::TimeTicks earliest_time_to_play = | |
| 290 cast_env()->Clock()->NowTicks() - max_frame_age_; | |
| 291 while (!audio_playout_queue_.empty() && | |
| 292 audio_playout_queue_.front().first < earliest_time_to_play) { | |
| 293 PopOneAudioFrame(true); | |
| 294 } | |
| 295 if (audio_playout_queue_.empty()) | |
| 296 break; | |
| 297 | |
| 298 currently_playing_audio_frame_.reset( | |
| 299 audio_playout_queue_.front().second); | |
| 300 currently_playing_audio_frame_start_ = 0; | |
| 301 PopOneAudioFrame(false); | |
| 302 } | |
| 303 | |
| 304 // Copy some or all of the samples in |currently_playing_audio_frame_| to | |
| 305 // |dest|. Once all samples in |currently_playing_audio_frame_| have been | |
| 306 // consumed, release it. | |
| 307 const int num_samples_to_copy = | |
| 308 std::min(samples_remaining, | |
| 309 currently_playing_audio_frame_->frames() - | |
| 310 currently_playing_audio_frame_start_); | |
| 311 currently_playing_audio_frame_->CopyPartialFramesTo( | |
| 312 currently_playing_audio_frame_start_, | |
| 313 num_samples_to_copy, | |
| 314 0, | |
| 315 dest); | |
| 316 samples_remaining -= num_samples_to_copy; | |
| 317 currently_playing_audio_frame_start_ += num_samples_to_copy; | |
| 318 if (currently_playing_audio_frame_start_ == | |
| 319 currently_playing_audio_frame_->frames()) { | |
| 320 currently_playing_audio_frame_.reset(); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 // If |dest| has not been fully filled, then an underrun has occurred; and | |
| 325 // fill the remainder of |dest| with zeros. | |
| 326 if (samples_remaining > 0) { | |
| 327 // Note: Only logging underruns after the first frame has been received. | |
| 328 LOG_IF(WARNING, currently_playing_audio_frame_start_ != -1) | |
| 329 << "Audio: Playback underrun of " << samples_remaining << " samples!"; | |
| 330 dest->ZeroFramesPartial(dest->frames() - samples_remaining, | |
| 331 samples_remaining); | |
| 332 } | |
| 333 | |
| 334 return dest->frames(); | |
| 335 } | |
| 336 | |
| 337 virtual int OnMoreIOData(AudioBus* source, | |
| 338 AudioBus* dest, | |
| 339 AudioBuffersState buffers_state) OVERRIDE { | |
| 340 return OnMoreData(dest, buffers_state); | |
| 341 } | |
| 342 | |
| 343 virtual void OnError(AudioOutputStream* stream) OVERRIDE { | |
| 344 LOG(ERROR) << "AudioOutputStream reports an error. " | |
| 345 << "Playback is unlikely to continue."; | |
| 346 } | |
| 347 | |
| 348 // End of AudioSourceCallback implementation. | |
| 349 //////////////////////////////////////////////////////////////////// | |
| 350 | |
| 351 void ScheduleVideoPlayout() { | |
| 352 DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); | |
| 353 | |
| 354 // Prune the queue, skipping entries that are too old. | |
| 355 const base::TimeTicks now = cast_env()->Clock()->NowTicks(); | |
| 356 const base::TimeTicks earliest_time_to_play = now - max_frame_age_; | |
| 357 while (!video_playout_queue_.empty() && | |
| 358 video_playout_queue_.front().first < earliest_time_to_play) { | |
| 359 PopOneVideoFrame(true); | |
| 360 } | |
| 361 | |
| 362 // If the queue is not empty, schedule playout of its first frame. | |
| 363 if (video_playout_queue_.empty()) { | |
| 364 video_playout_timer_.Stop(); | |
| 365 } else { | |
| 366 video_playout_timer_.Start( | |
| 367 FROM_HERE, | |
| 368 video_playout_queue_.front().first - now, | |
| 369 base::Bind(&NaivePlayer::PlayNextVideoFrame, | |
| 370 base::Unretained(this))); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 void PlayNextVideoFrame() { | |
| 375 DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); | |
| 376 if (!video_playout_queue_.empty()) { | |
| 377 const scoped_refptr<VideoFrame> video_frame = | |
| 378 video_playout_queue_.front().second; | |
| 379 PopOneVideoFrame(false); | |
| 380 #ifdef OS_LINUX | |
| 381 render_.RenderFrame(video_frame); | |
| 382 #endif // OS_LINUX | |
| 383 } | |
| 384 ScheduleVideoPlayout(); | |
| 385 } | |
| 386 | |
| 387 void PopOneVideoFrame(bool is_being_skipped) { | |
| 388 DCHECK(cast_env()->CurrentlyOn(CastEnvironment::MAIN)); | |
| 389 | |
| 390 if (is_being_skipped) { | |
| 391 VLOG(1) << "VideoFrame[" << num_video_frames_processed_ << "]: Skipped."; | |
| 392 } else { | |
| 393 VLOG(1) << "VideoFrame[" << num_video_frames_processed_ << "]: Playing " | |
| 394 << (cast_env()->Clock()->NowTicks() - | |
| 395 video_playout_queue_.front().first).InMicroseconds() | |
| 396 << " usec later than intended."; | |
| 397 } | |
| 398 | |
| 399 video_playout_queue_.pop_front(); | |
| 400 ++num_video_frames_processed_; | |
| 401 } | |
| 402 | |
| 403 void PopOneAudioFrame(bool was_skipped) { | |
| 404 audio_lock_.AssertAcquired(); | |
| 405 | |
| 406 if (was_skipped) { | |
| 407 VLOG(1) << "AudioFrame[" << num_audio_frames_processed_ << "]: Skipped"; | |
| 408 } else { | |
| 409 VLOG(1) << "AudioFrame[" << num_audio_frames_processed_ << "]: Playing " | |
| 410 << (cast_env()->Clock()->NowTicks() - | |
| 411 audio_playout_queue_.front().first).InMicroseconds() | |
| 412 << " usec later than intended."; | |
| 413 } | |
| 414 | |
| 415 audio_playout_queue_.pop_front(); | |
|
imcheng
2014/04/09 01:34:45
Do you need to destroy audio_playout_queue_.front(
miu
2014/04/09 04:07:19
Good catch. Fixed this memory-leak.
| |
| 416 ++num_audio_frames_processed_; | |
| 417 } | |
| 418 | |
| 419 // Frames in the queue older than this (relative to NowTicks()) will be | |
| 420 // dropped (i.e., playback is falling behind). | |
| 421 const base::TimeDelta max_frame_age_; | |
| 422 | |
| 423 // Outputs created, started, and destroyed by this NaivePlayer. | |
| 181 #ifdef OS_LINUX | 424 #ifdef OS_LINUX |
| 182 test::LinuxOutputWindow render_; | 425 test::LinuxOutputWindow render_; |
| 183 #endif // OS_LINUX | 426 #endif // OS_LINUX |
| 184 base::TimeTicks last_playout_time_; | 427 scoped_ptr<AudioOutputStream> audio_output_stream_; |
| 185 base::TimeTicks last_render_time_; | 428 |
| 429 // Video playout queue. | |
| 430 typedef std::pair<base::TimeTicks, scoped_refptr<VideoFrame> > | |
| 431 VideoQueueEntry; | |
| 432 std::deque<VideoQueueEntry> video_playout_queue_; | |
| 433 int64 num_video_frames_processed_; | |
| 434 | |
| 435 base::OneShotTimer<NaivePlayer> video_playout_timer_; | |
| 436 | |
| 437 // Audio playout queue, synchronized by |audio_lock_|. | |
| 438 base::Lock audio_lock_; | |
| 439 typedef std::pair<base::TimeTicks, AudioBus*> AudioQueueEntry; | |
|
imcheng
2014/04/09 01:34:45
Can you make this into <base::TimeTicks, linked_pt
miu
2014/04/09 04:07:19
Unfortunately, no. AudioBus defines a private des
| |
| 440 std::deque<AudioQueueEntry> audio_playout_queue_; | |
| 441 int64 num_audio_frames_processed_; | |
| 442 | |
| 443 // These must only be used on the audio thread calling OnMoreData(). | |
| 444 scoped_ptr<AudioBus> currently_playing_audio_frame_; | |
| 445 int currently_playing_audio_frame_start_; | |
| 186 }; | 446 }; |
| 187 | 447 |
| 188 } // namespace cast | 448 } // namespace cast |
| 189 } // namespace media | 449 } // namespace media |
| 190 | 450 |
| 191 int main(int argc, char** argv) { | 451 int main(int argc, char** argv) { |
| 192 base::AtExitManager at_exit; | 452 base::AtExitManager at_exit; |
| 193 CommandLine::Init(argc, argv); | 453 CommandLine::Init(argc, argv); |
| 194 InitLogging(logging::LoggingSettings()); | 454 InitLogging(logging::LoggingSettings()); |
| 195 | 455 |
| 196 scoped_refptr<media::cast::CastEnvironment> cast_environment( | 456 scoped_refptr<media::cast::CastEnvironment> cast_environment( |
| 197 new media::cast::StandaloneCastEnvironment); | 457 new media::cast::StandaloneCastEnvironment); |
| 198 | 458 |
| 459 // Start up Chromium audio system. | |
| 460 media::FakeAudioLogFactory fake_audio_log_factory_; | |
| 461 const scoped_ptr<media::AudioManager> audio_manager( | |
| 462 media::AudioManager::Create(&fake_audio_log_factory_)); | |
| 463 CHECK(media::AudioManager::Get()); | |
| 464 | |
| 199 media::cast::AudioReceiverConfig audio_config = | 465 media::cast::AudioReceiverConfig audio_config = |
| 200 media::cast::GetAudioReceiverConfig(); | 466 media::cast::GetAudioReceiverConfig(); |
| 201 media::cast::VideoReceiverConfig video_config = | 467 media::cast::VideoReceiverConfig video_config = |
| 202 media::cast::GetVideoReceiverConfig(); | 468 media::cast::GetVideoReceiverConfig(); |
| 203 | 469 |
| 470 // Determine local and remote endpoints. | |
| 204 int remote_port, local_port; | 471 int remote_port, local_port; |
| 205 media::cast::GetPorts(&remote_port, &local_port); | 472 media::cast::GetPorts(&remote_port, &local_port); |
| 206 if (!local_port) { | 473 if (!local_port) { |
| 207 LOG(ERROR) << "Invalid local port."; | 474 LOG(ERROR) << "Invalid local port."; |
| 208 return 1; | 475 return 1; |
| 209 } | 476 } |
| 210 | |
| 211 std::string remote_ip_address = media::cast::GetIpAddress("Enter remote IP."); | 477 std::string remote_ip_address = media::cast::GetIpAddress("Enter remote IP."); |
| 212 std::string local_ip_address = media::cast::GetIpAddress("Enter local IP."); | 478 std::string local_ip_address = media::cast::GetIpAddress("Enter local IP."); |
| 213 net::IPAddressNumber remote_ip_number; | 479 net::IPAddressNumber remote_ip_number; |
| 214 net::IPAddressNumber local_ip_number; | 480 net::IPAddressNumber local_ip_number; |
| 215 | |
| 216 if (!net::ParseIPLiteralToNumber(remote_ip_address, &remote_ip_number)) { | 481 if (!net::ParseIPLiteralToNumber(remote_ip_address, &remote_ip_number)) { |
| 217 LOG(ERROR) << "Invalid remote IP address."; | 482 LOG(ERROR) << "Invalid remote IP address."; |
| 218 return 1; | 483 return 1; |
| 219 } | 484 } |
| 220 | |
| 221 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { | 485 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { |
| 222 LOG(ERROR) << "Invalid local IP address."; | 486 LOG(ERROR) << "Invalid local IP address."; |
| 223 return 1; | 487 return 1; |
| 224 } | 488 } |
| 225 | |
| 226 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); | 489 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); |
| 227 net::IPEndPoint local_end_point(local_ip_number, local_port); | 490 net::IPEndPoint local_end_point(local_ip_number, local_port); |
| 228 | 491 |
| 229 media::cast::ReceiverDisplay* const receiver_display = | 492 // Create and start the player. |
| 230 new media::cast::ReceiverDisplay(cast_environment, | 493 media::cast::NaivePlayer player(cast_environment, |
| 231 local_end_point, | 494 local_end_point, |
| 232 remote_end_point, | 495 remote_end_point, |
| 233 audio_config, | 496 audio_config, |
| 234 video_config); | 497 video_config); |
| 235 receiver_display->Start(); | 498 player.Start(); |
| 236 | 499 |
| 237 base::MessageLoop().Run(); // Run forever (i.e., until SIGTERM). | 500 base::MessageLoop().Run(); // Run forever (i.e., until SIGTERM). |
| 238 NOTREACHED(); | 501 NOTREACHED(); |
| 239 return 0; | 502 return 0; |
| 240 } | 503 } |
| OLD | NEW |