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

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: Fix TODOs 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
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);
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 FWIW, could be ctor arg.
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Done.
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_;
scherkus (not reviewing) 2011/05/24 21:38:37 may want to consider using OneShotTimer() from bas
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Done.
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() {}
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 What happens to an outstanding posted task whose t
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 The task holds a reference that prevents destructi
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_) {
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 reverse the test and early-return lets you de-inde
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Done.
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_) {
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Adding an if (video_ids_.size() < 2) return; to th
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Done.
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_) {
148 message_loop_->PostTask(
149 FROM_HERE,
150 NewRunnableMethod(this,&StreamSwitchManager::OnSwitchDone, status));
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 return after this!
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Good catch. Done
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;
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 I'm surprised this doesn't store the seek target t
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Good catch. Updated the method signature to pass t
87 current_stream_index_ = index; 258
88 DCHECK(streams_[current_stream_index_]); 259 // TODO(acolwell): Figure out what to do if this happens during a stream
89 needs_bitstream_converter_enabled = bitstream_converter_enabled_; 260 // switch.
90 } 261 }
262
263 // This method initiates a stream switch. The diagram below shows the steps
264 // involved.
265 //
266 // +-----------------------+
267 // ChangeCurrentStream() -> | Store stream switch |
268 // | index, seek_function, |
269 // | and switch_cb. |
270 // +-----------------------+
271 // |
272 // \|/
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 You might enjoy v and ^ as arrow-heads for ASCII a
scherkus (not reviewing) 2011/05/24 21:38:37 hahah I was going to make that same comment but wo
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 oh yeah.. duh. Done
273 // +-------------------+ Yes
274 // | Are there pending | -----> (Wait for OnReadDone())
275 // | Read()s on the | |
276 // | current stream? | <-----+ \|/
277 // +-------------------+ +--- OnReadDone()
278 // | No
279 // \|/
280 // StartSwitch()
281 // |
282 // \|/
283 // +------------------------+ No
284 // | Have buffer timestamp? |-----+
285 // +------------------------+ |
286 // | Yes |
287 // \|/ |
288 // +-----------------------------+ |
289 // | Seek stream to timestamp | |
290 // | using switch_seek_function_ | |
291 // +-----------------------------+ |
292 // | |
293 // \|/ |
294 // OnSwitchSeekDone() <-----+
295 // |
296 // \|/
297 // +------------------+ No
298 // | Seek successful? |----------+
299 // +------------------+ |
300 // | Yes |
301 // \|/ |
302 // +------------------------------+ |
303 // | Update current_stream_index_ | |
304 // +------------------------------+ |
305 // | |
306 // \|/ |
307 // +---------------------------+ |
308 // | Call Read() on new stream |<---+
309 // | for deferred reads. |
310 // +---------------------------+
311 // |
312 // \|/
313 // switch_cb(OK | ERROR)
314 //
315 // NOTE: Any AdaptiveDemuxerStream::Read() calls that occur during the stream
316 // switch will be deferred until the switch has completed. The callbacks
317 // will be queued on |read_cb_queue_|, but no Read() will be issued on the
318 // current stream.
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Does this mean that if the switched-to stream stal
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Yes. In follow up CLs this will likely be tuned
Ami GONE FROM CHROMIUM 2011/05/25 05:39:01 Sounds reasonable. Add TODO?
319 void AdaptiveDemuxerStream::ChangeCurrentStream(
320 int index,
321 const SeekFunction& seek_function,
322 const PipelineStatusCB& switch_cb) {
scherkus (not reviewing) 2011/05/24 21:38:37 q: what's our goal w/ PipelineStatus + respective
323 DCHECK_GE(index, 0);
324
325 PipelineStatusCB error_cb;
326 PipelineStatus error_status = PIPELINE_ERROR_INVALID_STATE;
327
328 bool start_switch = false;
329
330 {
331 base::AutoLock auto_lock(lock_);
332
333 DCHECK_LE(index, streams_.size());
334 DCHECK(streams_[index].get());
335 DCHECK(!IsSwitchPending_Locked());
336
337 // TODO(acolwell): Still need to handle the case where the stream has ended.
338 if (index == current_stream_index_) {
339 error_cb = switch_cb;
340 error_status = PIPELINE_OK;
341 } else {
342 switch_cb_ = switch_cb;
343 switch_index_ = index;
344 switch_seek_function_ = seek_function;
345
346 start_switch = CanStartSwitch_Locked();
347 }
348 }
349
350 if (!error_cb.is_null()) {
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 I think you can lose error_cb by initializing erro
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Removed error_cb & error_status per our discussion
351 error_cb.Run(error_status);
352 return;
353 }
354
355 if (start_switch)
356 StartSwitch();
357 }
358
359 void AdaptiveDemuxerStream::OnReadDone(Buffer* buffer) {
360 ReadCallback read_cb;
361 bool start_switch = false;
362
363 {
364 base::AutoLock auto_lock(lock_);
365
366 pending_reads_--;
367
368 DCHECK_GE(pending_reads_, 0);
369 DCHECK_GE(read_cb_queue_.size(), 0);
370
371 read_cb = read_cb_queue_.front();
372 read_cb_queue_.pop_front();
373
374 if (buffer && !buffer->IsEndOfStream())
375 last_buffer_timestamp_ = buffer->GetTimestamp();
376
377 start_switch = IsSwitchPending_Locked() && CanStartSwitch_Locked();
378 }
379
380 if (!read_cb.is_null())
381 read_cb.Run(buffer);
382
383 if (start_switch)
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Is this thread-safe? Just b/c there were no pendi
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 I'm pretty sure this is safe. switch_pending state
384 StartSwitch();
385 }
386
387 bool AdaptiveDemuxerStream::IsSwitchPending_Locked() const {
388 lock_.AssertAcquired();
389 return !switch_cb_.is_null();
390 }
391
392 bool AdaptiveDemuxerStream::CanStartSwitch_Locked() const {
393 lock_.AssertAcquired();
394 return (pending_reads_ == 0);
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Seems strange that this doesn't inspect switch_cb_
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Done.
395 }
396
397 void AdaptiveDemuxerStream::StartSwitch() {
398 SeekFunction seek_function;
399 base::TimeDelta seek_point;
400
401 {
402 base::AutoLock auto_lock(lock_);
403 DCHECK(IsSwitchPending_Locked());
404 DCHECK_EQ(pending_reads_, 0);
405 DCHECK_GE(switch_index_, 0);
406
407 seek_point = last_buffer_timestamp_;
408 seek_function = switch_seek_function_;
409
410 // TODO(acolwell): add code to call switch_cb_ if we are at the end of the
411 // stream now.
412 }
413
414 if (seek_point == kNoTimestamp) {
415 // We haven't seen a buffer so there is no need to seek. Just move on to
416 // the next stage in the switch process.
417 OnSwitchSeekDone(PIPELINE_OK, kNoTimestamp);
418 return;
419 }
420
421 DCHECK(!seek_function.is_null());
422 seek_function.Run(seek_point,
423 base::Bind(&AdaptiveDemuxerStream::OnSwitchSeekDone, this));
424 }
425
426 void AdaptiveDemuxerStream::OnSwitchSeekDone(PipelineStatus status,
427 base::TimeDelta seek_time) {
428 DemuxerStream* stream = NULL;
429 PipelineStatusCB switch_cb;
430 int reads_to_request = 0;
431 bool needs_bitstream_converter_enabled = false;
432
433 {
434 base::AutoLock auto_lock(lock_);
435
436 if (status == PIPELINE_OK) {
437 DCHECK(streams_[switch_index_]);
438
439 current_stream_index_ = switch_index_;
440 needs_bitstream_converter_enabled = bitstream_converter_enabled_;
441 }
442
443 // Clear stream switch state.
444 switch_cb = switch_cb_;
445 switch_cb_.Reset();
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 swap instead of =+Reset()?
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 no swap() method. :(
Ami GONE FROM CHROMIUM 2011/05/25 05:39:01 new-style callbacks are assignable, so std::swap()
acolwell GONE FROM CHROMIUM 2011/05/25 21:43:46 Done.
446 switch_index_ = -1;
447 switch_seek_function_.Reset();
448
449 // Get the number of outstanding Read()s on this object.
450 reads_to_request = read_cb_queue_.size();
451
452 DCHECK_EQ(pending_reads_, 0);
453 pending_reads_ = reads_to_request;
454
455 stream = streams_[current_stream_index_];
456 }
457
91 if (needs_bitstream_converter_enabled) 458 if (needs_bitstream_converter_enabled)
92 EnableBitstreamConverter(); 459 EnableBitstreamConverter();
460
461 if (stream) {
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Do we really allow switching to a NULL stream inde
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 No. Removed if() and added DCHECK
462 // Make the Read() calls that were deferred during the stream switch.
463 for(;reads_to_request > 0; --reads_to_request)
464 stream->Read(base::Bind(&AdaptiveDemuxerStream::OnReadDone, this));
465 }
466
467 // Signal that the stream switch has completed.
468 if (!switch_cb.is_null())
469 switch_cb.Run(status);
93 } 470 }
94 471
95 // 472 //
96 // AdaptiveDemuxer 473 // AdaptiveDemuxer
97 // 474 //
98 475
99 AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, 476 AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers,
100 int initial_audio_demuxer_index, 477 int initial_audio_demuxer_index,
101 int initial_video_demuxer_index) 478 int initial_video_demuxer_index)
102 : demuxers_(demuxers), 479 : demuxers_(demuxers),
103 current_audio_demuxer_index_(initial_audio_demuxer_index), 480 current_audio_demuxer_index_(initial_audio_demuxer_index),
104 current_video_demuxer_index_(initial_video_demuxer_index) { 481 current_video_demuxer_index_(initial_video_demuxer_index),
482 playback_rate_(0),
483 switch_pending_(false),
484 stream_switch_manager_(new StreamSwitchManager()){
105 DCHECK(!demuxers_.empty()); 485 DCHECK(!demuxers_.empty());
106 DCHECK_GE(current_audio_demuxer_index_, -1); 486 DCHECK_GE(current_audio_demuxer_index_, -1);
107 DCHECK_GE(current_video_demuxer_index_, -1); 487 DCHECK_GE(current_video_demuxer_index_, -1);
108 DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size())); 488 DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size()));
109 DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size())); 489 DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size()));
110 DCHECK(current_audio_demuxer_index_ != -1 || 490 DCHECK(current_audio_demuxer_index_ != -1 ||
111 current_video_demuxer_index_ != -1); 491 current_video_demuxer_index_ != -1);
112 AdaptiveDemuxerStream::StreamVector audio_streams, video_streams; 492 AdaptiveDemuxerStream::StreamVector audio_streams, video_streams;
113 for (size_t i = 0; i < demuxers_.size(); ++i) { 493 for (size_t i = 0; i < demuxers_.size(); ++i) {
114 audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO)); 494 audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO));
115 video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO)); 495 video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO));
116 } 496 }
117 if (current_audio_demuxer_index_ >= 0) { 497 if (current_audio_demuxer_index_ >= 0) {
118 audio_stream_ = new AdaptiveDemuxerStream( 498 audio_stream_ = new AdaptiveDemuxerStream(
119 audio_streams, current_audio_demuxer_index_); 499 audio_streams, current_audio_demuxer_index_);
120 } 500 }
121 if (current_video_demuxer_index_ >= 0) { 501 if (current_video_demuxer_index_ >= 0) {
122 video_stream_ = new AdaptiveDemuxerStream( 502 video_stream_ = new AdaptiveDemuxerStream(
123 video_streams, current_video_demuxer_index_); 503 video_streams, current_video_demuxer_index_);
124 } 504 }
125 505
506 stream_switch_manager_->Init(this);
507
126 // TODO(fischman): any streams in the underlying demuxers that aren't being 508 // 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 509 // 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 510 // hold data for them in memory, waiting for them to get drained by a
129 // non-existent decoder. 511 // non-existent decoder.
130 } 512 }
131 513
132 AdaptiveDemuxer::~AdaptiveDemuxer() {} 514 AdaptiveDemuxer::~AdaptiveDemuxer() {}
133 515
134 void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) { 516 // Switches the current video stream. The diagram below describes the switch
517 // process.
518 // +-------------------------------+
519 // ChangeVideoStream() ---> | video_index |
520 // | == | Yes.
521 // | current_video_demuxer_index_? |--> done_cb(OK)
522 // +-------------------------------+
523 // | No.
524 // \|/
525 // Call video_stream_->ChangeCurrentStream()
526 // |
527 // \|/
528 // (Wait for ChangeVideoStreamDone())
529 // |
530 // \|/
531 // ChangeVideoStreamDone()
532 // |
533 // \|/
534 // +------------------------+ No
535 // | Was switch successful? |---> done_cb(ERROR)
536 // +------------------------+
537 // |
538 // \|/
539 // Update current_video_demuxer_index_
540 // & SetPlaybackRate() on new stream.
541 // |
542 // \|/
543 // done_cb(OK).
544 //
545 void AdaptiveDemuxer::ChangeVideoStream(int video_index,
546 const PipelineStatusCB& done_cb) {
135 // TODO(fischman): this is currently broken because when a new Demuxer is to 547 // 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 548 // 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. 549 // current Demuxer if it's no longer being used.
550 AdaptiveDemuxerStream* stream = NULL;
551 bool switching_to_current_stream = false;
552 AdaptiveDemuxerStream::SeekFunction seek_function;
553
554 {
555 base::AutoLock auto_lock(lock_);
556
557 DCHECK(video_stream_);
558 DCHECK(!switch_pending_);
559 if (current_video_demuxer_index_ == video_index) {
560 switching_to_current_stream = true;
561 } else {
562 stream = video_stream_;
563 switch_pending_ = true;
564 seek_function = base::Bind(&AdaptiveDemuxer::StartStreamSwitchSeek,
565 this,
566 DemuxerStream::VIDEO,
567 video_index);
568 }
569 }
570
571 if (switching_to_current_stream) {
572 done_cb.Run(PIPELINE_OK);
573 return;
574 }
575
576 DCHECK(stream);
577 stream->ChangeCurrentStream(
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Once you set switch_pending_=true under lock above
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Yes I think so. Removed stream variable.
578 video_index, seek_function,
579 base::Bind(&AdaptiveDemuxer::ChangeVideoStreamDone, this, video_index,
580 done_cb));
581 }
582
583 int AdaptiveDemuxer::GetCurrentVideoId() const {
138 base::AutoLock auto_lock(lock_); 584 base::AutoLock auto_lock(lock_);
139 current_audio_demuxer_index_ = audio_index; 585 return current_video_demuxer_index_;
140 current_video_demuxer_index_ = video_index; 586 }
141 if (audio_stream_) 587
142 audio_stream_->ChangeCurrentStream(audio_index); 588 AdaptiveDemuxer::StreamIdVector AdaptiveDemuxer::GetVideoIds() const {
143 if (video_stream_) 589 StreamIdVector ids;
144 video_stream_->ChangeCurrentStream(video_index); 590 base::AutoLock auto_lock(lock_);
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Is the return value from this function really some
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 This info shouldn't change during playback. I've r
591
592 for (int i = 0; i < demuxers_.size(); i++) {
593 if (demuxers_[i]->GetStream(DemuxerStream::VIDEO))
594 ids.push_back(i);
595 }
596
597 return ids;
598 }
599
600 void AdaptiveDemuxer::ChangeVideoStreamDone(int new_stream_index,
601 const PipelineStatusCB& done_cb,
602 PipelineStatus status) {
603 {
604 base::AutoLock auto_lock(lock_);
605
606 switch_pending_ = false;
607
608 if (status == PIPELINE_OK) {
609 demuxers_[current_video_demuxer_index_]->SetPlaybackRate(0);
610 current_video_demuxer_index_ = new_stream_index;
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 Whee!
611 demuxers_[current_video_demuxer_index_]->SetPlaybackRate(playback_rate_);
612 }
613 }
614
615 done_cb.Run(status);
145 } 616 }
146 617
147 Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { 618 Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) {
148 base::AutoLock auto_lock(lock_); 619 base::AutoLock auto_lock(lock_);
149 switch (type) { 620 switch (type) {
150 case DemuxerStream::AUDIO: 621 case DemuxerStream::AUDIO:
151 return (current_audio_demuxer_index_ < 0) ? NULL : 622 return (current_audio_demuxer_index_ < 0) ? NULL :
152 demuxers_[current_audio_demuxer_index_]; 623 demuxers_[current_audio_demuxer_index_];
153 case DemuxerStream::VIDEO: 624 case DemuxerStream::VIDEO:
154 return (current_video_demuxer_index_ < 0) ? NULL : 625 return (current_video_demuxer_index_ < 0) ? NULL :
155 demuxers_[current_video_demuxer_index_]; 626 demuxers_[current_video_demuxer_index_];
156 default: 627 default:
157 LOG(DFATAL) << "Unexpected type: " << type; 628 LOG(DFATAL) << "Unexpected type: " << type;
158 return NULL; 629 return NULL;
159 } 630 }
160 } 631 }
161 632
162 // Helper class that wraps a FilterCallback and expects to get called a set 633 // Helper class that wraps a FilterCallback and expects to get called a set
163 // number of times, after which the wrapped callback is fired (and deleted). 634 // number of times, after which the wrapped callback is fired (and deleted).
164 // 635 //
165 // TODO: Remove this class once Stop() is converted to FilterStatusCB. 636 // TODO(acolwell): Remove this class once Stop() is converted to FilterStatusCB.
166 class CountingCallback { 637 class CountingCallback {
167 public: 638 public:
168 CountingCallback(int count, FilterCallback* orig_cb) 639 CountingCallback(int count, FilterCallback* orig_cb)
169 : remaining_count_(count), orig_cb_(orig_cb) { 640 : remaining_count_(count), orig_cb_(orig_cb) {
170 DCHECK_GT(remaining_count_, 0); 641 DCHECK_GT(remaining_count_, 0);
171 DCHECK(orig_cb); 642 DCHECK(orig_cb);
172 } 643 }
173 644
174 FilterCallback* GetACallback() { 645 FilterCallback* GetACallback() {
175 return NewCallback(this, &CountingCallback::OnChildCallbackDone); 646 return NewCallback(this, &CountingCallback::OnChildCallbackDone);
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 705
235 base::Lock lock_; 706 base::Lock lock_;
236 int remaining_count_; 707 int remaining_count_;
237 FilterStatusCB orig_cb_; 708 FilterStatusCB orig_cb_;
238 PipelineStatus overall_status_; 709 PipelineStatus overall_status_;
239 710
240 DISALLOW_COPY_AND_ASSIGN(CountingStatusCB); 711 DISALLOW_COPY_AND_ASSIGN(CountingStatusCB);
241 }; 712 };
242 713
243 void AdaptiveDemuxer::Stop(FilterCallback* callback) { 714 void AdaptiveDemuxer::Stop(FilterCallback* callback) {
715 stream_switch_manager_->Stop();
716
244 // Stop() must be called on all of the demuxers even though only one demuxer 717 // 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 718 // is actively delivering audio and another one is delivering video. This
246 // just satisfies the contract that all demuxers must have Stop() called on 719 // just satisfies the contract that all demuxers must have Stop() called on
247 // them before they are destroyed. 720 // them before they are destroyed.
248 // 721 //
249 // TODO: Remove CountingCallback once Stop() is converted to FilterStatusCB. 722 // TODO(acolwell): Remove CountingCallback once Stop() is converted to
723 // FilterStatusCB.
250 CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback); 724 CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback);
251 for (size_t i = 0; i < demuxers_.size(); ++i) 725 for (size_t i = 0; i < demuxers_.size(); ++i)
252 demuxers_[i]->Stop(wrapper->GetACallback()); 726 demuxers_[i]->Stop(wrapper->GetACallback());
253 } 727 }
254 728
255 void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { 729 void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
730
731 if (audio_stream_)
732 audio_stream_->OnAdaptiveDemuxerSeek();
733
734 if (video_stream_)
735 video_stream_->OnAdaptiveDemuxerSeek();
736
256 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 737 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
257 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 738 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
258 int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); 739 int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
259 CountingStatusCB* wrapper = new CountingStatusCB(count, cb); 740 CountingStatusCB* wrapper = new CountingStatusCB(count, cb);
260 if (audio) 741 if (audio)
261 audio->Seek(time, wrapper->GetACallback()); 742 audio->Seek(time, wrapper->GetACallback());
262 if (video && audio != video) 743 if (video && audio != video)
263 video->Seek(time, wrapper->GetACallback()); 744 video->Seek(time, wrapper->GetACallback());
264 } 745 }
265 746
266 void AdaptiveDemuxer::OnAudioRendererDisabled() { 747 void AdaptiveDemuxer::OnAudioRendererDisabled() {
267 for (size_t i = 0; i < demuxers_.size(); ++i) 748 for (size_t i = 0; i < demuxers_.size(); ++i)
268 demuxers_[i]->OnAudioRendererDisabled(); 749 demuxers_[i]->OnAudioRendererDisabled();
269 } 750 }
270 751
271 void AdaptiveDemuxer::set_host(FilterHost* filter_host) { 752 void AdaptiveDemuxer::set_host(FilterHost* filter_host) {
272 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 753 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
273 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 754 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
274 if (audio) audio->set_host(filter_host); 755 if (audio) audio->set_host(filter_host);
275 if (video && audio != video) video->set_host(filter_host); 756 if (video && audio != video) video->set_host(filter_host);
276 } 757 }
277 758
278 void AdaptiveDemuxer::SetPlaybackRate(float playback_rate) { 759 void AdaptiveDemuxer::SetPlaybackRate(float playback_rate) {
760 {
761 base::AutoLock auto_lock(lock_);
762 if (playback_rate_ == 0 && playback_rate > 0) {
763 stream_switch_manager_->Play();
764 } else if (playback_rate_ > 0 && playback_rate == 0) {
765 stream_switch_manager_->Pause();
766 }
767
768 playback_rate_ = playback_rate;
769 }
770
279 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); 771 Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
280 Demuxer* video = current_demuxer(DemuxerStream::VIDEO); 772 Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
281 if (audio) audio->SetPlaybackRate(playback_rate); 773 if (audio) audio->SetPlaybackRate(playback_rate);
282 if (video && audio != video) video->SetPlaybackRate(playback_rate); 774 if (video && audio != video) video->SetPlaybackRate(playback_rate);
283 } 775 }
284 776
285 void AdaptiveDemuxer::SetPreload(Preload preload) { 777 void AdaptiveDemuxer::SetPreload(Preload preload) {
286 for (size_t i = 0; i < demuxers_.size(); ++i) 778 for (size_t i = 0; i < demuxers_.size(); ++i)
287 demuxers_[i]->SetPreload(preload); 779 demuxers_[i]->SetPreload(preload);
288 } 780 }
289 781
290 scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream( 782 scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream(
291 DemuxerStream::Type type) { 783 DemuxerStream::Type type) {
292 switch (type) { 784 switch (type) {
293 case DemuxerStream::AUDIO: 785 case DemuxerStream::AUDIO:
294 return audio_stream_; 786 return audio_stream_;
295 case DemuxerStream::VIDEO: 787 case DemuxerStream::VIDEO:
296 return video_stream_; 788 return video_stream_;
297 default: 789 default:
298 LOG(DFATAL) << "Unexpected type " << type; 790 LOG(DFATAL) << "Unexpected type " << type;
299 return NULL; 791 return NULL;
300 } 792 }
301 } 793 }
302 794
795 void AdaptiveDemuxer::StartStreamSwitchSeek(
796 DemuxerStream::Type type,
797 int stream_index,
798 base::TimeDelta seek_time,
799 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb) {
800 DCHECK_GE(stream_index, 0);
801 DCHECK(!seek_cb.is_null());
802
803 Demuxer* demuxer = NULL;
804 base::TimeDelta seek_point;
805 FilterStatusCB filter_cb;
806
807 {
808 base::AutoLock auto_lock(lock_);
809
810 demuxer = demuxers_[stream_index];
811
812 if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time,
813 &seek_point)) {
814 // We found a seek point.
815 filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this,
816 seek_point, seek_cb);
817 } else {
818 // We didn't find a seek point. Assume we don't have index data for it
819 // yet. Seek to the specified time to force index data to be loaded.
scherkus (not reviewing) 2011/05/24 21:38:37 OOC does this happen due to deferred CUES fetching
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Yes. It is a temporary hack until we have a WebM d
820 seek_point = seek_time;
821 filter_cb = base::Bind(&AdaptiveDemuxer::OnIndexSeekDone, this,
822 type, stream_index, seek_time, seek_cb);
823 }
824 }
825
826 DCHECK(demuxer);
827 demuxer->Seek(seek_point, filter_cb);
828 }
829
830 void AdaptiveDemuxer::OnIndexSeekDone(
831 DemuxerStream::Type type,
832 int stream_index,
833 base::TimeDelta seek_time,
834 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb,
835 PipelineStatus status) {
836 base::TimeDelta seek_point;
837 FilterStatusCB filter_cb;
838
839 Demuxer* demuxer = NULL;
840
841 if (status != PIPELINE_OK) {
842 seek_cb.Run(status, base::TimeDelta());
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 The ASCII art in this file (and this sort of error
843 return;
844 }
845
846 {
847 base::AutoLock auto_lock(lock_);
848
849 demuxer = demuxers_[stream_index];
850 DCHECK(demuxer);
851
852 // Look for a seek point now that we have index data.
853 if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time,
854 &seek_point)) {
855 filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this,
856 seek_point, seek_cb);
857 } else {
858 // Failed again to find a seek point. Clear the demuxer so that
859 // a seek error will be reported.
860 demuxer = NULL;
861 }
862 }
863
864 if (!demuxer) {
865 seek_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED, base::TimeDelta());
866 return;
867 }
868
869 demuxer->Seek(seek_point, filter_cb);
870 }
871
872 void AdaptiveDemuxer::OnStreamSeekDone(
873 base::TimeDelta seek_point,
874 const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb,
875 PipelineStatus status) {
876 if (status != PIPELINE_OK) {
877 seek_cb.Run(status, base::TimeDelta());
878 return;
879 }
880
881 seek_cb.Run(PIPELINE_OK, seek_point);
Ami GONE FROM CHROMIUM 2011/05/24 06:14:12 FWIW method body can just be seek_cb.Run(status, s
acolwell GONE FROM CHROMIUM 2011/05/24 23:45:16 Remove all the logic in the function so now it jus
Ami GONE FROM CHROMIUM 2011/05/25 05:39:01 Re: Apply: that's what Bind *is*. Re: ref-of-local
acolwell GONE FROM CHROMIUM 2011/05/25 21:43:46 yeah. I wasn't quite thinking straight here.
882 }
883
884
303 // 885 //
304 // AdaptiveDemuxerFactory 886 // AdaptiveDemuxerFactory
305 // 887 //
306 888
307 AdaptiveDemuxerFactory::AdaptiveDemuxerFactory( 889 AdaptiveDemuxerFactory::AdaptiveDemuxerFactory(
308 DemuxerFactory* delegate_factory) 890 DemuxerFactory* delegate_factory)
309 : delegate_factory_(delegate_factory) { 891 : delegate_factory_(delegate_factory) {
310 DCHECK(delegate_factory); 892 DCHECK(delegate_factory);
311 } 893 }
312 894
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
485 delete cb; 1067 delete cb;
486 return; 1068 return;
487 } 1069 }
488 DemuxerAccumulator* accumulator = new DemuxerAccumulator( 1070 DemuxerAccumulator* accumulator = new DemuxerAccumulator(
489 audio_index, video_index, urls.size(), cb); 1071 audio_index, video_index, urls.size(), cb);
490 for (size_t i = 0; i < urls.size(); ++i) 1072 for (size_t i = 0; i < urls.size(); ++i)
491 delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i)); 1073 delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i));
492 } 1074 }
493 1075
494 } // namespace media 1076 } // namespace media
OLDNEW
« media/filters/adaptive_demuxer.h ('K') | « media/filters/adaptive_demuxer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698