Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: media/filters/adaptive_demuxer.cc

Issue 7044008: Initial implementation of stream switching in AdaptiveDemuxer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed CR comments Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/adaptive_demuxer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "base/bind.h" 5 #include "base/bind.h"
6 #include "base/logging.h" 6 #include "base/logging.h"
7 #include "base/message_loop.h"
7 #include "base/string_number_conversions.h" 8 #include "base/string_number_conversions.h"
8 #include "base/string_split.h" 9 #include "base/string_split.h"
9 #include "base/string_util.h" 10 #include "base/string_util.h"
11 #include "media/ffmpeg/ffmpeg_common.h"
10 #include "media/filters/adaptive_demuxer.h" 12 #include "media/filters/adaptive_demuxer.h"
11 13
12 namespace media { 14 namespace media {
13 15
16 static const int64 kSwitchTimerPeriod = 5000; // In milliseconds.
17
18 // Object that decides when to switch streams.
19 class StreamSwitchManager
20 : public base::RefCountedThreadSafe<StreamSwitchManager> {
21 public:
22 StreamSwitchManager();
23 virtual ~StreamSwitchManager();
24
25 void Init(AdaptiveDemuxer* demuxer);
26
27 // Playback events. These methods are called when playback starts, pauses, or
28 // stops.
29 void Play();
30 void Pause();
31 void Stop();
32
33 private:
34 // Method called periodically to determine if we should switch
35 // streams.
36 void OnSwitchTimer();
37
38 // Called when the demuxer completes a stream switch.
39 void OnSwitchDone(PipelineStatus status);
40
41 // Helper method that schedules OnSwitchTimer() to be called.
42 void StartSwitchTimer();
43
44 // The demuxer that owns this object.
45 AdaptiveDemuxer* demuxer_;
46
47 // Message loop this object runs on.
48 MessageLoop* message_loop_;
49
50 // Is clip playing or not?
51 bool playing_;
52
53 // Is a stream switch in progress?
54 bool switch_pending_;
55
56 // Has an OnSwitchTimer() call been scheduled?
57 bool switch_timer_running_;
58
59 // The stream IDs for the video streams.
60 AdaptiveDemuxer::StreamIdVector video_ids_;
61
62 // An index into |video_ids_| for the current video stream.
63 int current_id_index_;
64
65 DISALLOW_COPY_AND_ASSIGN(StreamSwitchManager);
66 };
67
68 StreamSwitchManager::StreamSwitchManager()
69 : demuxer_(NULL),
70 playing_(false),
71 switch_pending_(false),
72 switch_timer_running_(false),
73 current_id_index_(-1) {
74 }
75
76 StreamSwitchManager::~StreamSwitchManager() {}
77
78 void StreamSwitchManager::Init(AdaptiveDemuxer* demuxer) {
79 DCHECK(demuxer);
80 message_loop_ = MessageLoop::current();
81 demuxer_ = demuxer;
82 video_ids_ = demuxer->GetVideoIds();
83 current_id_index_ = -1;
84
85 if (video_ids_.size() > 0) {
86 // Find the index in video_ids_ for the current video ID.
87 int current_id = demuxer->GetCurrentVideoId();
88 current_id_index_ = 0;
89 for (int i = 0; i < video_ids_.size(); i++) {
90 if (current_id == video_ids_[i]) {
91 current_id_index_ = i;
92 break;
93 }
94 }
95 }
96 }
97
98 void StreamSwitchManager::Play() {
99 DCHECK_EQ(MessageLoop::current(), message_loop_);
100 DCHECK(!playing_);
101 playing_ = true;
102
103 if (video_ids_.size() > 1 && !switch_timer_running_) {
104 StartSwitchTimer();
105 }
106 }
107
108 void StreamSwitchManager::Pause() {
109 DCHECK_EQ(MessageLoop::current(), message_loop_);
110 DCHECK(playing_);
111 playing_ = false;
112 }
113
114 void StreamSwitchManager::Stop() {
115 DCHECK_EQ(MessageLoop::current(), message_loop_);
116 DCHECK(!playing_);
117 demuxer_ = NULL;
118 }
119
120 void StreamSwitchManager::OnSwitchTimer() {
121 DCHECK_EQ(MessageLoop::current(), message_loop_);
122
123 switch_timer_running_ = false;
124
125 if (!playing_)
126 return;
127
128 if (!switch_pending_) {
129 // Select the stream to switch to. For now we are just rotating
130 // through the available streams.
131 int new_id_index = (current_id_index_ + 1) % video_ids_.size();
132
133 // Did we select a new stream?
134 if (new_id_index != current_id_index_) {
135 current_id_index_ = new_id_index;
136 switch_pending_ = true;
137 demuxer_->ChangeVideoStream(video_ids_[new_id_index],
138 base::Bind(&StreamSwitchManager::OnSwitchDone,
139 this));
140 }
141 }
142
143 StartSwitchTimer();
144 }
145
146 void StreamSwitchManager::OnSwitchDone(PipelineStatus status) {
147 if (MessageLoop::current() != message_loop_) {
scherkus (not reviewing) 2011/05/23 23:17:06 nothing actionable right now but some food for tho
acolwell GONE FROM CHROMIUM 2011/05/23 23:46:40 I was going to comment on this when I made the cha
148 message_loop_->PostTask(
149 FROM_HERE,
150 NewRunnableMethod(this,&StreamSwitchManager::OnSwitchDone, status));
151 }
152 switch_pending_ = false;
153 }
154
155 void StreamSwitchManager::StartSwitchTimer() {
156 DCHECK_EQ(MessageLoop::current(), message_loop_);
157 switch_timer_running_ = true;
158 message_loop_->PostDelayedTask(
159 FROM_HERE, NewRunnableMethod(this, &StreamSwitchManager::OnSwitchTimer),
160 kSwitchTimerPeriod);
161 }
162
14 // 163 //
15 // AdaptiveDemuxerStream 164 // AdaptiveDemuxerStream
16 // 165 //
17 AdaptiveDemuxerStream::AdaptiveDemuxerStream( 166 AdaptiveDemuxerStream::AdaptiveDemuxerStream(
18 StreamVector const& streams, int initial_stream) 167 StreamVector const& streams, int initial_stream)
19 : streams_(streams), current_stream_index_(initial_stream), 168 : streams_(streams), current_stream_index_(initial_stream),
20 bitstream_converter_enabled_(false) { 169 bitstream_converter_enabled_(false),
170 pending_reads_(0),
171 switch_index_(-1),
172 last_buffer_timestamp_(kNoTimestamp) {
21 DCheckSanity(); 173 DCheckSanity();
22 } 174 }
23 175
24 void AdaptiveDemuxerStream::DCheckSanity() { 176 void AdaptiveDemuxerStream::DCheckSanity() {
25 // We only carry out sanity checks in debug mode. 177 // We only carry out sanity checks in debug mode.
26 if (!logging::DEBUG_MODE) 178 if (!logging::DEBUG_MODE)
27 return; 179 return;
28 DCHECK(streams_[current_stream_index_].get()); 180 DCHECK(streams_[current_stream_index_].get());
29 181
30 bool non_null_stream_seen = false; 182 bool non_null_stream_seen = false;
(...skipping 19 matching lines...) Expand all
50 current_stream_index_ = -1; 202 current_stream_index_ = -1;
51 streams_.clear(); 203 streams_.clear();
52 } 204 }
53 205
54 DemuxerStream* AdaptiveDemuxerStream::current_stream() { 206 DemuxerStream* AdaptiveDemuxerStream::current_stream() {
55 base::AutoLock auto_lock(lock_); 207 base::AutoLock auto_lock(lock_);
56 return streams_[current_stream_index_]; 208 return streams_[current_stream_index_];
57 } 209 }
58 210
59 void AdaptiveDemuxerStream::Read(const ReadCallback& read_callback) { 211 void AdaptiveDemuxerStream::Read(const ReadCallback& read_callback) {
60 current_stream()->Read(read_callback); 212 DemuxerStream* stream = NULL;
213
214 {
215 base::AutoLock auto_lock(lock_);
216
217 read_cb_queue_.push_back(read_callback);
218
219 // Check to make sure we aren't doing a stream switch. We only want to
220 // make calls on |streams_[current_stream_index_]| when we aren't
221 // in the middle of a stream switch. Since the callback is stored in
222 // |read_cb_queue_| we will issue the Read() on the new stream once
223 // the switch has completed.
224 if (!IsSwitchPending_Locked()) {
225 stream = streams_[current_stream_index_];
226 pending_reads_++;
227 }
228 }
229
230 if (stream)
231 stream->Read(base::Bind(&AdaptiveDemuxerStream::OnReadDone, this));
61 } 232 }
62 233
63 AVStream* AdaptiveDemuxerStream::GetAVStream() { 234 AVStream* AdaptiveDemuxerStream::GetAVStream() {
64 return current_stream()->GetAVStream(); 235 return current_stream()->GetAVStream();
65 } 236 }
66 237
67 DemuxerStream::Type AdaptiveDemuxerStream::type() { 238 DemuxerStream::Type AdaptiveDemuxerStream::type() {
68 return current_stream()->type(); 239 return current_stream()->type();
69 } 240 }
70 241
71 const MediaFormat& AdaptiveDemuxerStream::media_format() { 242 const MediaFormat& AdaptiveDemuxerStream::media_format() {
72 return current_stream()->media_format(); 243 return current_stream()->media_format();
73 } 244 }
74 245
75 void AdaptiveDemuxerStream::EnableBitstreamConverter() { 246 void AdaptiveDemuxerStream::EnableBitstreamConverter() {
76 { 247 {
77 base::AutoLock auto_lock(lock_); 248 base::AutoLock auto_lock(lock_);
78 bitstream_converter_enabled_ = true; 249 bitstream_converter_enabled_ = true;
79 } 250 }
80 current_stream()->EnableBitstreamConverter(); 251 current_stream()->EnableBitstreamConverter();
81 } 252 }
82 253
83 void AdaptiveDemuxerStream::ChangeCurrentStream(int index) { 254 void AdaptiveDemuxerStream::OnAdaptiveDemuxerSeek() {
84 bool needs_bitstream_converter_enabled; 255 base::AutoLock auto_lock(lock_);
85 { 256
86 base::AutoLock auto_lock(lock_); 257 last_buffer_timestamp_ = kNoTimestamp;
87 current_stream_index_ = index; 258
88 DCHECK(streams_[current_stream_index_]); 259 // TODO Figure out what to do if this happens during a stream switch.
scherkus (not reviewing) 2011/05/23 23:17:06 TODO -> TODO(acolwell):
acolwell GONE FROM CHROMIUM 2011/05/23 23:46:40 Done.
89 needs_bitstream_converter_enabled = bitstream_converter_enabled_; 260 }
90 } 261
262 // This method initiates a stream switch. The diagram below shows the steps
263 // involved.
264 //
265 // +-----------------------+
266 // ChangeCurrentStream() -> | Store stream switch |
267 // | index, seek_function, |
268 // | and switch_cb. |
269 // +-----------------------+
270 // |
271 // \|/
272 // +-------------------+ Yes
273 // | Are there pending | -----> (Wait for OnReadDone())
274 // | Read()s on the | |
275 // | current stream? | <-----+ \|/
276 // +-------------------+ +--- OnReadDone()
277 // | No
278 // \|/
279 // StartSwitch()
280 // |
281 // \|/
282 // +------------------------+ No
283 // | Have buffer timestamp? |-----+
284 // +------------------------+ |
285 // | Yes |
286 // \|/ |
287 // +-----------------------------+ |
288 // | Seek stream to timestamp | |
289 // | using switch_seek_function_ | |
290 // +-----------------------------+ |
291 // | |
292 // \|/ |
293 // OnSwitchSeekDone() <-----+
294 // |
295 // \|/
296 // +------------------+ No
297 // | Seek successful? |----------+
298 // +------------------+ |
299 // | Yes |
300 // \|/ |
301 // +------------------------------+ |
302 // | Update current_stream_index_ | |
303 // +------------------------------+ |
304 // | |
305 // \|/ |
306 // +---------------------------+ |
307 // | Call Read() on new stream |<---+
308 // | for deferred reads. |
309 // +---------------------------+
310 // |
311 // \|/
312 // switch_cb(OK | ERROR)
313 //
314 // NOTE: Any AdaptiveDemuxerStream::Read() calls that occur during the stream
315 // switch will be deferred until the switch has completed. The callbacks
316 // will be queued on |read_cb_queue_|, but no Read() will be issued on the
317 // current stream.
318 void AdaptiveDemuxerStream::ChangeCurrentStream(
319 int index,
320 const SeekFunction& seek_function,
321 const PipelineStatusCB& switch_cb) {
322 DCHECK_GE(index, 0);
323
324 PipelineStatusCB error_cb;
325 PipelineStatus error_status = PIPELINE_ERROR_INVALID_STATE;
326
327 bool start_switch = false;
328
329 {
330 base::AutoLock auto_lock(lock_);
331
332 DCHECK_LE(index, streams_.size());
333 DCHECK(streams_[index].get());
334 DCHECK(!IsSwitchPending_Locked());
335
336 // TODO - Still need to handle the case where the stream has ended.
337 if (index == current_stream_index_) {
338 error_cb = switch_cb;
339 error_status = PIPELINE_OK;
340 } else {
341 switch_cb_ = switch_cb;
342 switch_index_ = index;
343 switch_seek_function_ = seek_function;
344
345 start_switch = CanStartSwitch_Locked();
346 }
347 }
348
349 if (!error_cb.is_null()) {
350 error_cb.Run(error_status);
351 return;
352 }
353
354 if (start_switch)
355 StartSwitch();
356 }
357
358 void AdaptiveDemuxerStream::OnReadDone(Buffer* buffer) {
359 ReadCallback read_cb;
360 bool start_switch = false;
361
362 {
363 base::AutoLock auto_lock(lock_);
364
365 pending_reads_--;
366
367 DCHECK_GE(pending_reads_, 0);
368 DCHECK_GE(read_cb_queue_.size(), 0);
369
370 read_cb = read_cb_queue_.front();
371 read_cb_queue_.pop_front();
372
373 if (buffer && !buffer->IsEndOfStream())
374 last_buffer_timestamp_ = buffer->GetTimestamp();
375
376 start_switch = IsSwitchPending_Locked() && CanStartSwitch_Locked();
377 }
378
379 if (!read_cb.is_null())
380 read_cb.Run(buffer);
381
382 if (start_switch)
383 StartSwitch();
384 }
385
386 bool AdaptiveDemuxerStream::IsSwitchPending_Locked() const {
387 lock_.AssertAcquired();
388 return !switch_cb_.is_null();
389 }
390
391 bool AdaptiveDemuxerStream::CanStartSwitch_Locked() const {
392 lock_.AssertAcquired();
393 return (pending_reads_ == 0);
394 }
395
396 void AdaptiveDemuxerStream::StartSwitch() {
397 SeekFunction seek_function;
398 base::TimeDelta seek_point;
399
400 {
401 base::AutoLock auto_lock(lock_);
402 DCHECK(IsSwitchPending_Locked());
403 DCHECK_EQ(pending_reads_, 0);
404 DCHECK_GE(switch_index_, 0);
405
406 seek_point = last_buffer_timestamp_;
407 seek_function = switch_seek_function_;
408
409 // TODO add code to call switch_cb_ if we are at the end of the stream now.
410 }
411
412 if (seek_point == kNoTimestamp) {
413 // We haven't seen a buffer so there is no need to seek. Just move on to
414 // the next stage in the switch process.
415 OnSwitchSeekDone(PIPELINE_OK, kNoTimestamp);
416 return;
417 }
418
419 DCHECK(!seek_function.is_null());
420 seek_function.Run(seek_point,
421 base::Bind(&AdaptiveDemuxerStream::OnSwitchSeekDone, this));
422 }
423
424 void AdaptiveDemuxerStream::OnSwitchSeekDone(PipelineStatus status,
425 base::TimeDelta seek_time) {
426 DemuxerStream* stream = NULL;
427 PipelineStatusCB switch_cb;
428 int reads_to_request = 0;
429 bool needs_bitstream_converter_enabled = false;
430
431 {
432 base::AutoLock auto_lock(lock_);
433
434 if (status == PIPELINE_OK) {
435 DCHECK(streams_[switch_index_]);
436
437 current_stream_index_ = switch_index_;
438 needs_bitstream_converter_enabled = bitstream_converter_enabled_;
439 }
440
441 // Clear stream switch state.
442 switch_cb = switch_cb_;
443 switch_cb_.Reset();
444 switch_index_ = -1;
445 switch_seek_function_.Reset();
446
447 // Get the number of outstanding Read()s on this object.
448 reads_to_request = read_cb_queue_.size();
449
450 DCHECK_EQ(pending_reads_, 0);
451 pending_reads_ = reads_to_request;
452
453 stream = streams_[current_stream_index_];
454 }
455
91 if (needs_bitstream_converter_enabled) 456 if (needs_bitstream_converter_enabled)
92 EnableBitstreamConverter(); 457 EnableBitstreamConverter();
458
459 if (stream) {
460 // Make the Read() calls that were deferred during the stream switch.
461 for(;reads_to_request > 0; --reads_to_request)
462 stream->Read(base::Bind(&AdaptiveDemuxerStream::OnReadDone, this));
463 }
464
465 // Signal that the stream switch has completed.
466 if (!switch_cb.is_null())
467 switch_cb.Run(status);
93 } 468 }
94 469
95 // 470 //
96 // AdaptiveDemuxer 471 // AdaptiveDemuxer
97 // 472 //
98 473
99 AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, 474 AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers,
100 int initial_audio_demuxer_index, 475 int initial_audio_demuxer_index,
101 int initial_video_demuxer_index) 476 int initial_video_demuxer_index)
102 : demuxers_(demuxers), 477 : demuxers_(demuxers),
103 current_audio_demuxer_index_(initial_audio_demuxer_index), 478 current_audio_demuxer_index_(initial_audio_demuxer_index),
104 current_video_demuxer_index_(initial_video_demuxer_index) { 479 current_video_demuxer_index_(initial_video_demuxer_index),
480 playback_rate_(0),
481 switch_pending_(false),
482 stream_switch_manager_(new StreamSwitchManager()){
105 DCHECK(!demuxers_.empty()); 483 DCHECK(!demuxers_.empty());
106 DCHECK_GE(current_audio_demuxer_index_, -1); 484 DCHECK_GE(current_audio_demuxer_index_, -1);
107 DCHECK_GE(current_video_demuxer_index_, -1); 485 DCHECK_GE(current_video_demuxer_index_, -1);
108 DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size())); 486 DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size()));
109 DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size())); 487 DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size()));
110 DCHECK(current_audio_demuxer_index_ != -1 || 488 DCHECK(current_audio_demuxer_index_ != -1 ||
111 current_video_demuxer_index_ != -1); 489 current_video_demuxer_index_ != -1);
112 AdaptiveDemuxerStream::StreamVector audio_streams, video_streams; 490 AdaptiveDemuxerStream::StreamVector audio_streams, video_streams;
113 for (size_t i = 0; i < demuxers_.size(); ++i) { 491 for (size_t i = 0; i < demuxers_.size(); ++i) {
114 audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO)); 492 audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO));
115 video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO)); 493 video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO));
116 } 494 }
117 if (current_audio_demuxer_index_ >= 0) { 495 if (current_audio_demuxer_index_ >= 0) {
118 audio_stream_ = new AdaptiveDemuxerStream( 496 audio_stream_ = new AdaptiveDemuxerStream(
119 audio_streams, current_audio_demuxer_index_); 497 audio_streams, current_audio_demuxer_index_);
120 } 498 }
121 if (current_video_demuxer_index_ >= 0) { 499 if (current_video_demuxer_index_ >= 0) {
122 video_stream_ = new AdaptiveDemuxerStream( 500 video_stream_ = new AdaptiveDemuxerStream(
123 video_streams, current_video_demuxer_index_); 501 video_streams, current_video_demuxer_index_);
124 } 502 }
125 503
504 stream_switch_manager_->Init(this);
505
126 // TODO(fischman): any streams in the underlying demuxers that aren't being 506 // TODO(fischman): any streams in the underlying demuxers that aren't being
127 // consumed currently need to be sent to /dev/null or else FFmpegDemuxer will 507 // consumed currently need to be sent to /dev/null or else FFmpegDemuxer will
128 // hold data for them in memory, waiting for them to get drained by a 508 // hold data for them in memory, waiting for them to get drained by a
129 // non-existent decoder. 509 // non-existent decoder.
130 } 510 }
131 511
132 AdaptiveDemuxer::~AdaptiveDemuxer() {} 512 AdaptiveDemuxer::~AdaptiveDemuxer() {}
133 513
134 void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) { 514 // Switches the current video stream. The diagram below describes the switch
515 // process.
516 // +-------------------------------+
517 // ChangeVideoStream() ---> | video_index |
518 // | == | Yes.
519 // | current_video_demuxer_index_? |--> done_cb(OK)
520 // +-------------------------------+
521 // | No.
522 // \|/
523 // Call video_stream_->ChangeCurrentStream()
524 // |
525 // \|/
526 // (Wait for ChangeVideoStreamDone())
527 // |
528 // \|/
529 // ChangeVideoStreamDone()
530 // |
531 // \|/
532 // +------------------------+ No
533 // | Was switch successful? |---> done_cb(ERROR)
534 // +------------------------+
535 // |
536 // \|/
537 // Update current_video_demuxer_index_
538 // & SetPlaybackRate() on new stream.
539 // |
540 // \|/
541 // done_cb(OK).
542 //
543 void AdaptiveDemuxer::ChangeVideoStream(int video_index,
544 const PipelineStatusCB& done_cb) {
135 // TODO(fischman): this is currently broken because when a new Demuxer is to 545 // TODO(fischman): this is currently broken because when a new Demuxer is to
136 // be used we need to set_host(host()) it, and we need to set_host(NULL) the 546 // be used we need to set_host(host()) it, and we need to set_host(NULL) the
137 // current Demuxer if it's no longer being used. 547 // current Demuxer if it's no longer being used.
548 AdaptiveDemuxerStream* stream = NULL;
549 bool switching_to_current_stream = false;
550 AdaptiveDemuxerStream::SeekFunction seek_function;
551
552 {
553 base::AutoLock auto_lock(lock_);
554
555 DCHECK(video_stream_);
556 DCHECK(!switch_pending_);
557 if (current_video_demuxer_index_ == video_index) {
558 switching_to_current_stream = true;
559 } else {
560 stream = video_stream_;
561 switch_pending_ = true;
562 seek_function = base::Bind(&AdaptiveDemuxer::StartStreamSwitchSeek,
563 this,
564 DemuxerStream::VIDEO,
565 video_index);
566 }
567 }
568
569 if (switching_to_current_stream) {
570 done_cb.Run(PIPELINE_OK);
571 return;
572 }
573
574 DCHECK(stream);
575 stream->ChangeCurrentStream(
576 video_index, seek_function,
577 base::Bind(&AdaptiveDemuxer::ChangeVideoStreamDone, this, video_index,
578 done_cb));
579 }
580
581 int AdaptiveDemuxer::GetCurrentVideoId() const {
138 base::AutoLock auto_lock(lock_); 582 base::AutoLock auto_lock(lock_);
139 current_audio_demuxer_index_ = audio_index; 583 return current_video_demuxer_index_;
140 current_video_demuxer_index_ = video_index; 584 }
141 if (audio_stream_) 585
142 audio_stream_->ChangeCurrentStream(audio_index); 586 AdaptiveDemuxer::StreamIdVector AdaptiveDemuxer::GetVideoIds() const {
143 if (video_stream_) 587 StreamIdVector ids;
144 video_stream_->ChangeCurrentStream(video_index); 588 base::AutoLock auto_lock(lock_);
589
590 for (int i = 0; i < demuxers_.size(); i++) {
591 if (demuxers_[i]->GetStream(DemuxerStream::VIDEO))
592 ids.push_back(i);
593 }
594
595 return ids;
596 }
597
598 void AdaptiveDemuxer::ChangeVideoStreamDone(int new_stream_index,
599 const PipelineStatusCB& done_cb,
600 PipelineStatus status) {
601 {
602 base::AutoLock auto_lock(lock_);
603
604 switch_pending_ = false;
605
606 if (status == PIPELINE_OK) {
607 demuxers_[current_video_demuxer_index_]->SetPlaybackRate(0);
608 current_video_demuxer_index_ = new_stream_index;
609 demuxers_[current_video_demuxer_index_]->SetPlaybackRate(playback_rate_);
610 }
611 }
612
613 done_cb.Run(status);
145 } 614 }
146 615
147 Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { 616 Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) {
148 base::AutoLock auto_lock(lock_); 617 base::AutoLock auto_lock(lock_);
149 switch (type) { 618 switch (type) {
150 case DemuxerStream::AUDIO: 619 case DemuxerStream::AUDIO:
151 return (current_audio_demuxer_index_ < 0) ? NULL : 620 return (current_audio_demuxer_index_ < 0) ? NULL :
152 demuxers_[current_audio_demuxer_index_]; 621 demuxers_[current_audio_demuxer_index_];
153 case DemuxerStream::VIDEO: 622 case DemuxerStream::VIDEO:
154 return (current_video_demuxer_index_ < 0) ? NULL : 623 return (current_video_demuxer_index_ < 0) ? NULL :
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 703
235 base::Lock lock_; 704 base::Lock lock_;
236 int remaining_count_; 705 int remaining_count_;
237 FilterStatusCB orig_cb_; 706 FilterStatusCB orig_cb_;
238 PipelineStatus overall_status_; 707 PipelineStatus overall_status_;
239 708
240 DISALLOW_COPY_AND_ASSIGN(CountingStatusCB); 709 DISALLOW_COPY_AND_ASSIGN(CountingStatusCB);
241 }; 710 };
242 711
243 void AdaptiveDemuxer::Stop(FilterCallback* callback) { 712 void AdaptiveDemuxer::Stop(FilterCallback* callback) {
713 stream_switch_manager_->Stop();
714
244 // Stop() must be called on all of the demuxers even though only one demuxer 715 // Stop() must be called on all of the demuxers even though only one demuxer
245 // is actively delivering audio and another one is delivering video. This 716 // is actively delivering audio and another one is delivering video. This
246 // just satisfies the contract that all demuxers must have Stop() called on 717 // just satisfies the contract that all demuxers must have Stop() called on
247 // them before they are destroyed. 718 // them before they are destroyed.
248 // 719 //
249 // TODO: Remove CountingCallback once Stop() is converted to FilterStatusCB. 720 // TODO: Remove CountingCallback once Stop() is converted to FilterStatusCB.
250 CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback); 721 CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback);
251 for (size_t i = 0; i < demuxers_.size(); ++i) 722 for (size_t i = 0; i < demuxers_.size(); ++i)
252 demuxers_[i]->Stop(wrapper->GetACallback()); 723 demuxers_[i]->Stop(wrapper->GetACallback());
253 } 724 }
254 725
255 void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { 726 void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
727
728 if (audio_stream_)
729 audio_stream_->OnAdaptiveDemuxerSeek();
730
731 if (video_stream_)
732 video_stream_->OnAdaptiveDemuxerSeek();
733
256 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 734 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
257 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 735 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
258 int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); 736 int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
259 CountingStatusCB* wrapper = new CountingStatusCB(count, cb); 737 CountingStatusCB* wrapper = new CountingStatusCB(count, cb);
260 if (audio) 738 if (audio)
261 audio->Seek(time, wrapper->GetACallback()); 739 audio->Seek(time, wrapper->GetACallback());
262 if (video && audio != video) 740 if (video && audio != video)
263 video->Seek(time, wrapper->GetACallback()); 741 video->Seek(time, wrapper->GetACallback());
264 } 742 }
265 743
266 void AdaptiveDemuxer::OnAudioRendererDisabled() { 744 void AdaptiveDemuxer::OnAudioRendererDisabled() {
267 for (size_t i = 0; i < demuxers_.size(); ++i) 745 for (size_t i = 0; i < demuxers_.size(); ++i)
268 demuxers_[i]->OnAudioRendererDisabled(); 746 demuxers_[i]->OnAudioRendererDisabled();
269 } 747 }
270 748
271 void AdaptiveDemuxer::set_host(FilterHost* filter_host) { 749 void AdaptiveDemuxer::set_host(FilterHost* filter_host) {
272 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 750 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
273 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 751 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
274 if (audio) audio->set_host(filter_host); 752 if (audio) audio->set_host(filter_host);
275 if (video && audio != video) video->set_host(filter_host); 753 if (video && audio != video) video->set_host(filter_host);
276 } 754 }
277 755
278 void AdaptiveDemuxer::SetPlaybackRate(float playback_rate) { 756 void AdaptiveDemuxer::SetPlaybackRate(float playback_rate) {
757 {
758 base::AutoLock auto_lock(lock_);
759 if (playback_rate_ == 0 && playback_rate > 0) {
760 stream_switch_manager_->Play();
761 } else if (playback_rate_ > 0 && playback_rate == 0) {
762 stream_switch_manager_->Pause();
763 }
764
765 playback_rate_ = playback_rate;
766 }
767
279 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 768 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
280 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 769 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
281 if (audio) audio->SetPlaybackRate(playback_rate); 770 if (audio) audio->SetPlaybackRate(playback_rate);
282 if (video && audio != video) video->SetPlaybackRate(playback_rate); 771 if (video && audio != video) video->SetPlaybackRate(playback_rate);
283 } 772 }
284 773
285 void AdaptiveDemuxer::SetPreload(Preload preload) { 774 void AdaptiveDemuxer::SetPreload(Preload preload) {
286 for (size_t i = 0; i < demuxers_.size(); ++i) 775 for (size_t i = 0; i < demuxers_.size(); ++i)
287 demuxers_[i]->SetPreload(preload); 776 demuxers_[i]->SetPreload(preload);
288 } 777 }
289 778
290 scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream( 779 scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream(
291 DemuxerStream::Type type) { 780 DemuxerStream::Type type) {
292 switch (type) { 781 switch (type) {
293 case DemuxerStream::AUDIO: 782 case DemuxerStream::AUDIO:
294 return audio_stream_; 783 return audio_stream_;
295 case DemuxerStream::VIDEO: 784 case DemuxerStream::VIDEO:
296 return video_stream_; 785 return video_stream_;
297 default: 786 default:
298 LOG(DFATAL) << "Unexpected type " << type; 787 LOG(DFATAL) << "Unexpected type " << type;
299 return NULL; 788 return NULL;
300 } 789 }
301 } 790 }
302 791
792 void AdaptiveDemuxer::StartStreamSwitchSeek(
793 DemuxerStream::Type type,
794 int stream_index,
795 base::TimeDelta seek_time,
796 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb) {
797 DCHECK_GE(stream_index, 0);
798 DCHECK(!seek_cb.is_null());
799
800 Demuxer* demuxer = NULL;
801 base::TimeDelta seek_point;
802 FilterStatusCB filter_cb;
803
804 {
805 base::AutoLock auto_lock(lock_);
806
807 demuxer = demuxers_[stream_index];
808
809 if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time,
810 &seek_point)) {
811 // We found a seek point.
812 filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this,
813 seek_point, seek_cb);
814 } else {
815 // We didn't find a seek point. Assume we don't have index data for it
816 // yet. Seek to the specified time to force index data to be loaded.
817 seek_point = seek_time;
818 filter_cb = base::Bind(&AdaptiveDemuxer::OnIndexSeekDone, this,
819 type, stream_index, seek_time, seek_cb);
820 }
821 }
822
823 DCHECK(demuxer);
824 demuxer->Seek(seek_point, filter_cb);
825 }
826
827 void AdaptiveDemuxer::OnIndexSeekDone(
828 DemuxerStream::Type type,
829 int stream_index,
830 base::TimeDelta seek_time,
831 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb,
832 PipelineStatus status) {
833 base::TimeDelta seek_point;
834 FilterStatusCB filter_cb;
835
836 Demuxer* demuxer = NULL;
837
838 if (status != PIPELINE_OK) {
839 seek_cb.Run(status, base::TimeDelta());
840 return;
841 }
842
843 {
844 base::AutoLock auto_lock(lock_);
845
846 demuxer = demuxers_[stream_index];
847 DCHECK(demuxer);
848
849 // Look for a seek point now that we have index data.
850 if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time,
851 &seek_point)) {
852 filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this,
853 seek_point, seek_cb);
854 } else {
855 // Failed again to find a seek point. Clear the demuxer so that
856 // a seek error will be reported.
857 demuxer = NULL;
858 }
859 }
860
861 if (!demuxer) {
862 seek_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED, base::TimeDelta());
863 return;
864 }
865
866 demuxer->Seek(seek_point, filter_cb);
867 }
868
869 void AdaptiveDemuxer::OnStreamSeekDone(
870 base::TimeDelta seek_point,
871 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb,
872 PipelineStatus status) {
873 if (status != PIPELINE_OK) {
874 seek_cb.Run(status, base::TimeDelta());
875 return;
876 }
877
878 seek_cb.Run(PIPELINE_OK, seek_point);
879 }
880
881
303 // 882 //
304 // AdaptiveDemuxerFactory 883 // AdaptiveDemuxerFactory
305 // 884 //
306 885
307 AdaptiveDemuxerFactory::AdaptiveDemuxerFactory( 886 AdaptiveDemuxerFactory::AdaptiveDemuxerFactory(
308 DemuxerFactory* delegate_factory) 887 DemuxerFactory* delegate_factory)
309 : delegate_factory_(delegate_factory) { 888 : delegate_factory_(delegate_factory) {
310 DCHECK(delegate_factory); 889 DCHECK(delegate_factory);
311 } 890 }
312 891
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
485 delete cb; 1064 delete cb;
486 return; 1065 return;
487 } 1066 }
488 DemuxerAccumulator* accumulator = new DemuxerAccumulator( 1067 DemuxerAccumulator* accumulator = new DemuxerAccumulator(
489 audio_index, video_index, urls.size(), cb); 1068 audio_index, video_index, urls.size(), cb);
490 for (size_t i = 0; i < urls.size(); ++i) 1069 for (size_t i = 0; i < urls.size(); ++i)
491 delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i)); 1070 delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i));
492 } 1071 }
493 1072
494 } // namespace media 1073 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/adaptive_demuxer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698