OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "media/filters/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 10 matching lines...) Expand all Loading... | |
21 #include "media/base/decoder_buffer.h" | 21 #include "media/base/decoder_buffer.h" |
22 #include "media/base/limits.h" | 22 #include "media/base/limits.h" |
23 #include "media/base/media_switches.h" | 23 #include "media/base/media_switches.h" |
24 #include "media/base/video_decoder_config.h" | 24 #include "media/base/video_decoder_config.h" |
25 #include "media/ffmpeg/ffmpeg_common.h" | 25 #include "media/ffmpeg/ffmpeg_common.h" |
26 #include "media/filters/ffmpeg_glue.h" | 26 #include "media/filters/ffmpeg_glue.h" |
27 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" | 27 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" |
28 | 28 |
29 namespace media { | 29 namespace media { |
30 | 30 |
31 // Helpers for making ReadCBs always run on a new execution stack. | |
32 static void RunReadCB(const scoped_refptr<base::MessageLoopProxy>& message_loop, | |
33 const DemuxerStream::ReadCB& read_cb, | |
34 DemuxerStream::Status status, | |
35 const scoped_refptr<DecoderBuffer>& buffer) { | |
36 message_loop->PostTask(FROM_HERE, base::Bind(read_cb, status, buffer)); | |
37 } | |
38 | |
39 static DemuxerStream::ReadCB WrapReadCB(const DemuxerStream::ReadCB& read_cb) { | |
40 return base::Bind(&RunReadCB, base::MessageLoopProxy::current(), read_cb); | |
41 } | |
42 | |
31 // | 43 // |
32 // FFmpegDemuxerStream | 44 // FFmpegDemuxerStream |
33 // | 45 // |
34 FFmpegDemuxerStream::FFmpegDemuxerStream( | 46 FFmpegDemuxerStream::FFmpegDemuxerStream( |
35 FFmpegDemuxer* demuxer, | 47 FFmpegDemuxer* demuxer, |
36 AVStream* stream) | 48 AVStream* stream) |
37 : demuxer_(demuxer), | 49 : demuxer_(demuxer), |
38 message_loop_(base::MessageLoopProxy::current()), | 50 message_loop_(base::MessageLoopProxy::current()), |
39 stream_(stream), | 51 stream_(stream), |
40 type_(UNKNOWN), | 52 type_(UNKNOWN), |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
127 read_queue_.clear(); | 139 read_queue_.clear(); |
128 stopped_ = true; | 140 stopped_ = true; |
129 end_of_stream_ = true; | 141 end_of_stream_ = true; |
130 } | 142 } |
131 | 143 |
132 base::TimeDelta FFmpegDemuxerStream::duration() { | 144 base::TimeDelta FFmpegDemuxerStream::duration() { |
133 return duration_; | 145 return duration_; |
134 } | 146 } |
135 | 147 |
136 DemuxerStream::Type FFmpegDemuxerStream::type() { | 148 DemuxerStream::Type FFmpegDemuxerStream::type() { |
149 DCHECK(message_loop_->BelongsToCurrentThread()); | |
137 return type_; | 150 return type_; |
138 } | 151 } |
139 | 152 |
140 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { | 153 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
141 if (!message_loop_->BelongsToCurrentThread()) { | 154 DCHECK(message_loop_->BelongsToCurrentThread()); |
142 message_loop_->PostTask(FROM_HERE, base::Bind( | 155 ReadCB wrapped_read_cb = WrapReadCB(read_cb); |
143 &FFmpegDemuxerStream::Read, this, read_cb)); | |
144 return; | |
145 } | |
146 | 156 |
147 // Don't accept any additional reads if we've been told to stop. | 157 // Don't accept any additional reads if we've been told to stop. |
148 // The |demuxer_| may have been destroyed in the pipeline thread. | 158 // The |demuxer_| may have been destroyed in the pipeline thread. |
149 // | 159 // |
150 // TODO(scherkus): it would be cleaner to reply with an error message. | 160 // TODO(scherkus): it would be cleaner to reply with an error message. |
151 if (stopped_) { | 161 if (stopped_) { |
152 read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 162 wrapped_read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
153 return; | 163 return; |
154 } | 164 } |
155 | 165 |
156 read_queue_.push_back(read_cb); | 166 read_queue_.push_back(wrapped_read_cb); |
acolwell GONE FROM CHROMIUM
2012/12/03 23:14:21
Do we still need this queue?
scherkus (not reviewing)
2012/12/07 22:18:55
We don't! Removed.
| |
157 SatisfyPendingReads(); | 167 SatisfyPendingReads(); |
158 } | 168 } |
159 | 169 |
160 void FFmpegDemuxerStream::EnableBitstreamConverter() { | 170 void FFmpegDemuxerStream::EnableBitstreamConverter() { |
161 if (!message_loop_->BelongsToCurrentThread()) { | 171 DCHECK(message_loop_->BelongsToCurrentThread()); |
162 message_loop_->PostTask(FROM_HERE, base::Bind( | |
163 &FFmpegDemuxerStream::EnableBitstreamConverter, this)); | |
164 return; | |
165 } | |
166 CHECK(bitstream_converter_.get()); | 172 CHECK(bitstream_converter_.get()); |
167 bitstream_converter_enabled_ = true; | 173 bitstream_converter_enabled_ = true; |
168 } | 174 } |
169 | 175 |
170 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { | 176 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { |
177 DCHECK(message_loop_->BelongsToCurrentThread()); | |
171 CHECK_EQ(type_, AUDIO); | 178 CHECK_EQ(type_, AUDIO); |
172 return audio_config_; | 179 return audio_config_; |
173 } | 180 } |
174 | 181 |
175 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { | 182 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { |
183 DCHECK(message_loop_->BelongsToCurrentThread()); | |
176 CHECK_EQ(type_, VIDEO); | 184 CHECK_EQ(type_, VIDEO); |
177 return video_config_; | 185 return video_config_; |
178 } | 186 } |
179 | 187 |
180 FFmpegDemuxerStream::~FFmpegDemuxerStream() { | 188 FFmpegDemuxerStream::~FFmpegDemuxerStream() { |
181 DCHECK(stopped_); | 189 DCHECK(stopped_); |
182 DCHECK(read_queue_.empty()); | 190 DCHECK(read_queue_.empty()); |
183 DCHECK(buffer_queue_.IsEmpty()); | 191 DCHECK(buffer_queue_.IsEmpty()); |
184 } | 192 } |
185 | 193 |
(...skipping 14 matching lines...) Expand all Loading... | |
200 buffer = buffer_queue_.Pop(); | 208 buffer = buffer_queue_.Pop(); |
201 } else if (end_of_stream_) { | 209 } else if (end_of_stream_) { |
202 buffer = DecoderBuffer::CreateEOSBuffer(); | 210 buffer = DecoderBuffer::CreateEOSBuffer(); |
203 } else { | 211 } else { |
204 break; | 212 break; |
205 } | 213 } |
206 | 214 |
207 // Send buffer back on a new execution stack to avoid recursing. | 215 // Send buffer back on a new execution stack to avoid recursing. |
208 ReadCB read_cb = read_queue_.front(); | 216 ReadCB read_cb = read_queue_.front(); |
209 read_queue_.pop_front(); | 217 read_queue_.pop_front(); |
210 message_loop_->PostTask(FROM_HERE, base::Bind( | 218 read_cb.Run(DemuxerStream::kOk, buffer); |
211 read_cb, DemuxerStream::kOk, buffer)); | |
212 } | 219 } |
213 | 220 |
214 // Have capacity? Ask for more! | 221 // Have capacity? Ask for more! |
215 if (HasAvailableCapacity() && !end_of_stream_) { | 222 if (HasAvailableCapacity() && !end_of_stream_) { |
216 demuxer_->NotifyCapacityAvailable(); | 223 demuxer_->NotifyCapacityAvailable(); |
217 } | 224 } |
218 } | 225 } |
219 | 226 |
220 bool FFmpegDemuxerStream::HasAvailableCapacity() { | 227 bool FFmpegDemuxerStream::HasAvailableCapacity() { |
221 // Try to have one second's worth of encoded data per stream. | 228 // Try to have one second's worth of encoded data per stream. |
(...skipping 28 matching lines...) Expand all Loading... | |
250 duration_known_(false), | 257 duration_known_(false), |
251 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 258 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( |
252 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))) { | 259 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))) { |
253 DCHECK(message_loop_); | 260 DCHECK(message_loop_); |
254 DCHECK(data_source_); | 261 DCHECK(data_source_); |
255 } | 262 } |
256 | 263 |
257 FFmpegDemuxer::~FFmpegDemuxer() {} | 264 FFmpegDemuxer::~FFmpegDemuxer() {} |
258 | 265 |
259 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 266 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
260 // Post a task to notify the streams to stop as well. | 267 DCHECK(message_loop_->BelongsToCurrentThread()); |
261 message_loop_->PostTask(FROM_HERE, | 268 url_protocol_.Abort(); |
262 base::Bind(&FFmpegDemuxer::StopTask, this, callback)); | 269 data_source_->Stop(BindToLoop(message_loop_, base::Bind( |
270 &FFmpegDemuxer::OnDataSourceStopped, this, callback))); | |
263 } | 271 } |
264 | 272 |
265 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 273 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
266 message_loop_->PostTask(FROM_HERE, | 274 DCHECK(message_loop_->BelongsToCurrentThread()); |
267 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); | 275 CHECK(!pending_seek_); |
276 | |
277 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | |
278 // otherwise we can end up waiting for a pre-seek read to complete even though | |
279 // we know we're going to drop it on the floor. | |
280 | |
281 // Always seek to a timestamp less than or equal to the desired timestamp. | |
282 int flags = AVSEEK_FLAG_BACKWARD; | |
283 | |
284 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | |
285 // will attempt to use the lowest-index video stream, if present, followed by | |
286 // the lowest-index audio stream. | |
287 pending_seek_ = true; | |
288 base::PostTaskAndReplyWithResult( | |
289 blocking_thread_.message_loop_proxy(), FROM_HERE, | |
290 base::Bind(&av_seek_frame, glue_->format_context(), -1, | |
291 time.InMicroseconds(), flags), | |
292 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); | |
268 } | 293 } |
269 | 294 |
270 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 295 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
271 DCHECK(data_source_.get()); | 296 DCHECK(message_loop_->BelongsToCurrentThread()); |
272 data_source_->SetPlaybackRate(playback_rate); | 297 data_source_->SetPlaybackRate(playback_rate); |
273 } | 298 } |
274 | 299 |
275 void FFmpegDemuxer::OnAudioRendererDisabled() { | 300 void FFmpegDemuxer::OnAudioRendererDisabled() { |
276 message_loop_->PostTask(FROM_HERE, base::Bind( | 301 DCHECK(message_loop_->BelongsToCurrentThread()); |
277 &FFmpegDemuxer::DisableAudioStreamTask, this)); | 302 audio_disabled_ = true; |
303 StreamVector::iterator iter; | |
304 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | |
305 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | |
306 (*iter)->Stop(); | |
307 } | |
308 } | |
278 } | 309 } |
279 | 310 |
280 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 311 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
281 const PipelineStatusCB& status_cb) { | 312 const PipelineStatusCB& status_cb) { |
282 message_loop_->PostTask(FROM_HERE, base::Bind( | 313 DCHECK(message_loop_->BelongsToCurrentThread()); |
283 &FFmpegDemuxer::InitializeTask, this, host, status_cb)); | 314 host_ = host; |
315 | |
316 // TODO(scherkus): DataSource should have a host by this point, | |
317 // see http://crbug.com/122071 | |
318 data_source_->set_host(host); | |
319 | |
320 glue_.reset(new FFmpegGlue(&url_protocol_)); | |
321 AVFormatContext* format_context = glue_->format_context(); | |
322 | |
323 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | |
324 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | |
325 // available, so add a metadata entry to ensure some is always present. | |
326 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | |
327 | |
328 // Open the AVFormatContext using our glue layer. | |
329 CHECK(blocking_thread_.Start()); | |
330 base::PostTaskAndReplyWithResult( | |
331 blocking_thread_.message_loop_proxy(), FROM_HERE, | |
332 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | |
333 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); | |
284 } | 334 } |
285 | 335 |
286 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 336 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
287 DemuxerStream::Type type) { | 337 DemuxerStream::Type type) { |
338 DCHECK(message_loop_->BelongsToCurrentThread()); | |
288 return GetFFmpegStream(type); | 339 return GetFFmpegStream(type); |
289 } | 340 } |
290 | 341 |
291 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( | 342 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( |
292 DemuxerStream::Type type) const { | 343 DemuxerStream::Type type) const { |
293 StreamVector::const_iterator iter; | 344 StreamVector::const_iterator iter; |
294 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 345 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
295 if (*iter && (*iter)->type() == type) { | 346 if (*iter && (*iter)->type() == type) { |
296 return *iter; | 347 return *iter; |
297 } | 348 } |
298 } | 349 } |
299 return NULL; | 350 return NULL; |
300 } | 351 } |
301 | 352 |
302 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | 353 base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
354 DCHECK(message_loop_->BelongsToCurrentThread()); | |
303 return start_time_; | 355 return start_time_; |
304 } | 356 } |
305 | 357 |
306 // Helper for calculating the bitrate of the media based on information stored | 358 // Helper for calculating the bitrate of the media based on information stored |
307 // in |format_context| or failing that the size and duration of the media. | 359 // in |format_context| or failing that the size and duration of the media. |
308 // | 360 // |
309 // Returns 0 if a bitrate could not be determined. | 361 // Returns 0 if a bitrate could not be determined. |
310 static int CalculateBitrate( | 362 static int CalculateBitrate( |
311 AVFormatContext* format_context, | 363 AVFormatContext* format_context, |
312 const base::TimeDelta& duration, | 364 const base::TimeDelta& duration, |
(...skipping 19 matching lines...) Expand all Loading... | |
332 return 0; | 384 return 0; |
333 } | 385 } |
334 | 386 |
335 // Do math in floating point as we'd overflow an int64 if the filesize was | 387 // Do math in floating point as we'd overflow an int64 if the filesize was |
336 // larger than ~1073GB. | 388 // larger than ~1073GB. |
337 double bytes = filesize_in_bytes; | 389 double bytes = filesize_in_bytes; |
338 double duration_us = duration.InMicroseconds(); | 390 double duration_us = duration.InMicroseconds(); |
339 return bytes * 8000000.0 / duration_us; | 391 return bytes * 8000000.0 / duration_us; |
340 } | 392 } |
341 | 393 |
342 void FFmpegDemuxer::InitializeTask(DemuxerHost* host, | |
343 const PipelineStatusCB& status_cb) { | |
344 DCHECK(message_loop_->BelongsToCurrentThread()); | |
345 host_ = host; | |
346 | |
347 // TODO(scherkus): DataSource should have a host by this point, | |
348 // see http://crbug.com/122071 | |
349 data_source_->set_host(host); | |
350 | |
351 glue_.reset(new FFmpegGlue(&url_protocol_)); | |
352 AVFormatContext* format_context = glue_->format_context(); | |
353 | |
354 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | |
355 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | |
356 // available, so add a metadata entry to ensure some is always present. | |
357 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | |
358 | |
359 // Open the AVFormatContext using our glue layer. | |
360 CHECK(blocking_thread_.Start()); | |
361 base::PostTaskAndReplyWithResult( | |
362 blocking_thread_.message_loop_proxy(), FROM_HERE, | |
363 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | |
364 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); | |
365 } | |
366 | |
367 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, | 394 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, |
368 bool result) { | 395 bool result) { |
369 DCHECK(message_loop_->BelongsToCurrentThread()); | 396 DCHECK(message_loop_->BelongsToCurrentThread()); |
370 if (!blocking_thread_.IsRunning()) { | 397 if (!blocking_thread_.IsRunning()) { |
371 status_cb.Run(PIPELINE_ERROR_ABORT); | 398 status_cb.Run(PIPELINE_ERROR_ABORT); |
372 return; | 399 return; |
373 } | 400 } |
374 | 401 |
375 if (!result) { | 402 if (!result) { |
376 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 403 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
471 | 498 |
472 int64 filesize_in_bytes = 0; | 499 int64 filesize_in_bytes = 0; |
473 url_protocol_.GetSize(&filesize_in_bytes); | 500 url_protocol_.GetSize(&filesize_in_bytes); |
474 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); | 501 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); |
475 if (bitrate_ > 0) | 502 if (bitrate_ > 0) |
476 data_source_->SetBitrate(bitrate_); | 503 data_source_->SetBitrate(bitrate_); |
477 | 504 |
478 status_cb.Run(PIPELINE_OK); | 505 status_cb.Run(PIPELINE_OK); |
479 } | 506 } |
480 | 507 |
481 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { | |
482 DCHECK(message_loop_->BelongsToCurrentThread()); | |
483 CHECK(!pending_seek_); | |
484 | |
485 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | |
486 // otherwise we can end up waiting for a pre-seek read to complete even though | |
487 // we know we're going to drop it on the floor. | |
488 | |
489 // Always seek to a timestamp less than or equal to the desired timestamp. | |
490 int flags = AVSEEK_FLAG_BACKWARD; | |
491 | |
492 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | |
493 // will attempt to use the lowest-index video stream, if present, followed by | |
494 // the lowest-index audio stream. | |
495 pending_seek_ = true; | |
496 base::PostTaskAndReplyWithResult( | |
497 blocking_thread_.message_loop_proxy(), FROM_HERE, | |
498 base::Bind(&av_seek_frame, glue_->format_context(), -1, | |
499 time.InMicroseconds(), flags), | |
500 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); | |
501 } | |
502 | |
503 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { | 508 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
504 DCHECK(message_loop_->BelongsToCurrentThread()); | 509 DCHECK(message_loop_->BelongsToCurrentThread()); |
505 CHECK(pending_seek_); | 510 CHECK(pending_seek_); |
506 pending_seek_ = false; | 511 pending_seek_ = false; |
507 | 512 |
508 if (!blocking_thread_.IsRunning()) { | 513 if (!blocking_thread_.IsRunning()) { |
509 cb.Run(PIPELINE_ERROR_ABORT); | 514 cb.Run(PIPELINE_ERROR_ABORT); |
510 return; | 515 return; |
511 } | 516 } |
512 | 517 |
513 if (result < 0) { | 518 if (result < 0) { |
514 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 519 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
515 // captured from stdout and contaminates testing. | 520 // captured from stdout and contaminates testing. |
516 // TODO(scherkus): Implement this properly and signal error (BUG=23447). | 521 // TODO(scherkus): Implement this properly and signal error (BUG=23447). |
517 VLOG(1) << "Not implemented"; | 522 VLOG(1) << "Not implemented"; |
518 } | 523 } |
519 | 524 |
520 // Tell streams to flush buffers due to seeking. | 525 // Tell streams to flush buffers due to seeking. |
521 StreamVector::iterator iter; | 526 StreamVector::iterator iter; |
522 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 527 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
523 if (*iter) | 528 if (*iter) |
524 (*iter)->FlushBuffers(); | 529 (*iter)->FlushBuffers(); |
525 } | 530 } |
526 | 531 |
527 // Resume demuxing until capacity. | 532 // Resume reading until capacity. |
528 DemuxTask(); | 533 ReadFrameIfNeeded(); |
529 | 534 |
530 // Notify we're finished seeking. | 535 // Notify we're finished seeking. |
531 cb.Run(PIPELINE_OK); | 536 cb.Run(PIPELINE_OK); |
532 } | 537 } |
533 | 538 |
534 void FFmpegDemuxer::DemuxTask() { | 539 void FFmpegDemuxer::ReadFrameIfNeeded() { |
535 DCHECK(message_loop_->BelongsToCurrentThread()); | 540 DCHECK(message_loop_->BelongsToCurrentThread()); |
536 | 541 |
537 // Make sure we have work to do before demuxing. | 542 // Make sure we have work to do before reading. |
538 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || | 543 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || |
539 pending_read_ || pending_seek_) { | 544 pending_read_ || pending_seek_) { |
540 return; | 545 return; |
541 } | 546 } |
542 | 547 |
543 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 548 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
544 // evaluation order of packet.get() and base::Passed(&packet) is | 549 // evaluation order of packet.get() and base::Passed(&packet) is |
545 // undefined. | 550 // undefined. |
546 ScopedAVPacket packet(new AVPacket()); | 551 ScopedAVPacket packet(new AVPacket()); |
547 AVPacket* packet_ptr = packet.get(); | 552 AVPacket* packet_ptr = packet.get(); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 // Defend against ffmpeg giving us a bad stream index. | 598 // Defend against ffmpeg giving us a bad stream index. |
594 if (packet->stream_index >= 0 && | 599 if (packet->stream_index >= 0 && |
595 packet->stream_index < static_cast<int>(streams_.size()) && | 600 packet->stream_index < static_cast<int>(streams_.size()) && |
596 streams_[packet->stream_index] && | 601 streams_[packet->stream_index] && |
597 (!audio_disabled_ || | 602 (!audio_disabled_ || |
598 streams_[packet->stream_index]->type() != DemuxerStream::AUDIO)) { | 603 streams_[packet->stream_index]->type() != DemuxerStream::AUDIO)) { |
599 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; | 604 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; |
600 demuxer_stream->EnqueuePacket(packet.Pass()); | 605 demuxer_stream->EnqueuePacket(packet.Pass()); |
601 } | 606 } |
602 | 607 |
603 // Keep demuxing until we've reached capacity. | 608 // Keep reading until we've reached capacity. |
604 DemuxTask(); | 609 ReadFrameIfNeeded(); |
605 } | |
606 | |
607 void FFmpegDemuxer::StopTask(const base::Closure& callback) { | |
608 DCHECK(message_loop_->BelongsToCurrentThread()); | |
609 url_protocol_.Abort(); | |
610 data_source_->Stop(BindToLoop(message_loop_, base::Bind( | |
611 &FFmpegDemuxer::OnDataSourceStopped, this, callback))); | |
612 } | 610 } |
613 | 611 |
614 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { | 612 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { |
615 // This will block until all tasks complete. Note that after this returns it's | 613 // This will block until all tasks complete. Note that after this returns it's |
616 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this | 614 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this |
617 // thread. Each of the reply task methods must check whether we've stopped the | 615 // thread. Each of the reply task methods must check whether we've stopped the |
618 // thread and drop their results on the floor. | 616 // thread and drop their results on the floor. |
619 DCHECK(message_loop_->BelongsToCurrentThread()); | 617 DCHECK(message_loop_->BelongsToCurrentThread()); |
620 blocking_thread_.Stop(); | 618 blocking_thread_.Stop(); |
621 | 619 |
622 StreamVector::iterator iter; | 620 StreamVector::iterator iter; |
623 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 621 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
624 if (*iter) | 622 if (*iter) |
625 (*iter)->Stop(); | 623 (*iter)->Stop(); |
626 } | 624 } |
627 | 625 |
628 callback.Run(); | 626 callback.Run(); |
629 } | 627 } |
630 | 628 |
631 void FFmpegDemuxer::DisableAudioStreamTask() { | |
632 DCHECK(message_loop_->BelongsToCurrentThread()); | |
633 audio_disabled_ = true; | |
634 StreamVector::iterator iter; | |
635 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | |
636 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | |
637 (*iter)->Stop(); | |
638 } | |
639 } | |
640 } | |
641 | |
642 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { | 629 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { |
643 DCHECK(message_loop_->BelongsToCurrentThread()); | 630 DCHECK(message_loop_->BelongsToCurrentThread()); |
644 StreamVector::iterator iter; | 631 StreamVector::iterator iter; |
645 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 632 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
646 if (*iter && (*iter)->HasAvailableCapacity()) { | 633 if (*iter && (*iter)->HasAvailableCapacity()) { |
647 return true; | 634 return true; |
648 } | 635 } |
649 } | 636 } |
650 return false; | 637 return false; |
651 } | 638 } |
652 | 639 |
653 void FFmpegDemuxer::StreamHasEnded() { | 640 void FFmpegDemuxer::StreamHasEnded() { |
654 DCHECK(message_loop_->BelongsToCurrentThread()); | 641 DCHECK(message_loop_->BelongsToCurrentThread()); |
655 StreamVector::iterator iter; | 642 StreamVector::iterator iter; |
656 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 643 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
657 if (!*iter || | 644 if (!*iter || |
658 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { | 645 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { |
659 continue; | 646 continue; |
660 } | 647 } |
661 (*iter)->SetEndOfStream(); | 648 (*iter)->SetEndOfStream(); |
662 } | 649 } |
663 } | 650 } |
664 | 651 |
665 void FFmpegDemuxer::NotifyCapacityAvailable() { | 652 void FFmpegDemuxer::NotifyCapacityAvailable() { |
666 DCHECK(message_loop_->BelongsToCurrentThread()); | 653 DCHECK(message_loop_->BelongsToCurrentThread()); |
667 DemuxTask(); | 654 ReadFrameIfNeeded(); |
668 } | 655 } |
669 | 656 |
670 void FFmpegDemuxer::NotifyBufferingChanged() { | 657 void FFmpegDemuxer::NotifyBufferingChanged() { |
671 DCHECK(message_loop_->BelongsToCurrentThread()); | 658 DCHECK(message_loop_->BelongsToCurrentThread()); |
672 Ranges<base::TimeDelta> buffered; | 659 Ranges<base::TimeDelta> buffered; |
673 scoped_refptr<FFmpegDemuxerStream> audio = | 660 scoped_refptr<FFmpegDemuxerStream> audio = |
674 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 661 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
675 scoped_refptr<FFmpegDemuxerStream> video = | 662 scoped_refptr<FFmpegDemuxerStream> video = |
676 GetFFmpegStream(DemuxerStream::VIDEO); | 663 GetFFmpegStream(DemuxerStream::VIDEO); |
677 if (audio && video) { | 664 if (audio && video) { |
678 buffered = audio->GetBufferedRanges().IntersectionWith( | 665 buffered = audio->GetBufferedRanges().IntersectionWith( |
679 video->GetBufferedRanges()); | 666 video->GetBufferedRanges()); |
680 } else if (audio) { | 667 } else if (audio) { |
681 buffered = audio->GetBufferedRanges(); | 668 buffered = audio->GetBufferedRanges(); |
682 } else if (video) { | 669 } else if (video) { |
683 buffered = video->GetBufferedRanges(); | 670 buffered = video->GetBufferedRanges(); |
684 } | 671 } |
685 for (size_t i = 0; i < buffered.size(); ++i) | 672 for (size_t i = 0; i < buffered.size(); ++i) |
686 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 673 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
687 } | 674 } |
688 | 675 |
689 void FFmpegDemuxer::OnDataSourceError() { | 676 void FFmpegDemuxer::OnDataSourceError() { |
690 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 677 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
691 } | 678 } |
692 | 679 |
693 } // namespace media | 680 } // namespace media |
OLD | NEW |