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 |