| 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/base64.h" | 10 #include "base/base64.h" |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/callback.h" | 12 #include "base/callback.h" |
| 13 #include "base/callback_helpers.h" | 13 #include "base/callback_helpers.h" |
| 14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop_proxy.h" |
| 16 #include "base/metrics/sparse_histogram.h" | 16 #include "base/metrics/sparse_histogram.h" |
| 17 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
| 18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
| 19 #include "base/sys_byteorder.h" | 19 #include "base/sys_byteorder.h" |
| 20 #include "base/task_runner_util.h" | 20 #include "base/task_runner_util.h" |
| 21 #include "base/time/time.h" | 21 #include "base/time/time.h" |
| 22 #include "media/base/audio_decoder_config.h" | 22 #include "media/base/audio_decoder_config.h" |
| 23 #include "media/base/bind_to_loop.h" | 23 #include "media/base/bind_to_loop.h" |
| 24 #include "media/base/decoder_buffer.h" | 24 #include "media/base/decoder_buffer.h" |
| 25 #include "media/base/decrypt_config.h" | 25 #include "media/base/decrypt_config.h" |
| 26 #include "media/base/limits.h" | 26 #include "media/base/limits.h" |
| 27 #include "media/base/media_log.h" | 27 #include "media/base/media_log.h" |
| 28 #include "media/base/video_decoder_config.h" | 28 #include "media/base/video_decoder_config.h" |
| 29 #include "media/ffmpeg/ffmpeg_common.h" | 29 #include "media/ffmpeg/ffmpeg_common.h" |
| 30 #include "media/filters/ffmpeg_glue.h" | 30 #include "media/filters/ffmpeg_glue.h" |
| 31 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" | 31 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" |
| 32 #include "media/filters/webvtt_util.h" | 32 #include "media/filters/webvtt_util.h" |
| 33 #include "media/webm/webm_crypto_helpers.h" | 33 #include "media/webm/webm_crypto_helpers.h" |
| 34 | 34 |
| 35 namespace media { | 35 namespace media { |
| 36 | 36 |
| 37 // | 37 // |
| 38 // FFmpegDemuxerStream | 38 // FFmpegDemuxerStream |
| 39 // | 39 // |
| 40 FFmpegDemuxerStream::FFmpegDemuxerStream( | 40 FFmpegDemuxerStream::FFmpegDemuxerStream( |
| 41 FFmpegDemuxer* demuxer, | 41 FFmpegDemuxer* demuxer, |
| 42 AVStream* stream) | 42 AVStream* stream) |
| 43 : demuxer_(demuxer), | 43 : demuxer_(demuxer), |
| 44 message_loop_(base::MessageLoopProxy::current()), | 44 task_runner_(base::MessageLoopProxy::current()), |
| 45 stream_(stream), | 45 stream_(stream), |
| 46 type_(UNKNOWN), | 46 type_(UNKNOWN), |
| 47 end_of_stream_(false), | 47 end_of_stream_(false), |
| 48 last_packet_timestamp_(kNoTimestamp()), | 48 last_packet_timestamp_(kNoTimestamp()), |
| 49 bitstream_converter_enabled_(false) { | 49 bitstream_converter_enabled_(false) { |
| 50 DCHECK(demuxer_); | 50 DCHECK(demuxer_); |
| 51 | 51 |
| 52 bool is_encrypted = false; | 52 bool is_encrypted = false; |
| 53 | 53 |
| 54 // Determine our media format. | 54 // Determine our media format. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 DCHECK(!enc_key_id.empty()); | 92 DCHECK(!enc_key_id.empty()); |
| 93 if (enc_key_id.empty()) | 93 if (enc_key_id.empty()) |
| 94 return; | 94 return; |
| 95 | 95 |
| 96 encryption_key_id_.assign(enc_key_id); | 96 encryption_key_id_.assign(enc_key_id); |
| 97 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); | 97 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); |
| 98 } | 98 } |
| 99 } | 99 } |
| 100 | 100 |
| 101 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { | 101 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { |
| 102 DCHECK(message_loop_->BelongsToCurrentThread()); | 102 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 103 | 103 |
| 104 if (!demuxer_ || end_of_stream_) { | 104 if (!demuxer_ || end_of_stream_) { |
| 105 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; | 105 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; |
| 106 return; | 106 return; |
| 107 } | 107 } |
| 108 | 108 |
| 109 // Convert the packet if there is a bitstream filter. | 109 // Convert the packet if there is a bitstream filter. |
| 110 if (packet->data && bitstream_converter_enabled_ && | 110 if (packet->data && bitstream_converter_enabled_ && |
| 111 !bitstream_converter_->ConvertPacket(packet.get())) { | 111 !bitstream_converter_->ConvertPacket(packet.get())) { |
| 112 LOG(ERROR) << "Format conversion failed."; | 112 LOG(ERROR) << "Format conversion failed."; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 194 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); | 194 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
| 195 demuxer_->NotifyBufferingChanged(); | 195 demuxer_->NotifyBufferingChanged(); |
| 196 } | 196 } |
| 197 last_packet_timestamp_ = buffer->timestamp(); | 197 last_packet_timestamp_ = buffer->timestamp(); |
| 198 | 198 |
| 199 buffer_queue_.Push(buffer); | 199 buffer_queue_.Push(buffer); |
| 200 SatisfyPendingRead(); | 200 SatisfyPendingRead(); |
| 201 } | 201 } |
| 202 | 202 |
| 203 void FFmpegDemuxerStream::SetEndOfStream() { | 203 void FFmpegDemuxerStream::SetEndOfStream() { |
| 204 DCHECK(message_loop_->BelongsToCurrentThread()); | 204 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 205 end_of_stream_ = true; | 205 end_of_stream_ = true; |
| 206 SatisfyPendingRead(); | 206 SatisfyPendingRead(); |
| 207 } | 207 } |
| 208 | 208 |
| 209 void FFmpegDemuxerStream::FlushBuffers() { | 209 void FFmpegDemuxerStream::FlushBuffers() { |
| 210 DCHECK(message_loop_->BelongsToCurrentThread()); | 210 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 211 DCHECK(read_cb_.is_null()) << "There should be no pending read"; | 211 DCHECK(read_cb_.is_null()) << "There should be no pending read"; |
| 212 buffer_queue_.Clear(); | 212 buffer_queue_.Clear(); |
| 213 end_of_stream_ = false; | 213 end_of_stream_ = false; |
| 214 last_packet_timestamp_ = kNoTimestamp(); | 214 last_packet_timestamp_ = kNoTimestamp(); |
| 215 } | 215 } |
| 216 | 216 |
| 217 void FFmpegDemuxerStream::Stop() { | 217 void FFmpegDemuxerStream::Stop() { |
| 218 DCHECK(message_loop_->BelongsToCurrentThread()); | 218 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 219 buffer_queue_.Clear(); | 219 buffer_queue_.Clear(); |
| 220 if (!read_cb_.is_null()) { | 220 if (!read_cb_.is_null()) { |
| 221 base::ResetAndReturn(&read_cb_).Run( | 221 base::ResetAndReturn(&read_cb_).Run( |
| 222 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 222 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 223 } | 223 } |
| 224 demuxer_ = NULL; | 224 demuxer_ = NULL; |
| 225 stream_ = NULL; | 225 stream_ = NULL; |
| 226 end_of_stream_ = true; | 226 end_of_stream_ = true; |
| 227 } | 227 } |
| 228 | 228 |
| 229 base::TimeDelta FFmpegDemuxerStream::duration() { | 229 base::TimeDelta FFmpegDemuxerStream::duration() { |
| 230 return duration_; | 230 return duration_; |
| 231 } | 231 } |
| 232 | 232 |
| 233 DemuxerStream::Type FFmpegDemuxerStream::type() { | 233 DemuxerStream::Type FFmpegDemuxerStream::type() { |
| 234 DCHECK(message_loop_->BelongsToCurrentThread()); | 234 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 235 return type_; | 235 return type_; |
| 236 } | 236 } |
| 237 | 237 |
| 238 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { | 238 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
| 239 DCHECK(message_loop_->BelongsToCurrentThread()); | 239 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 240 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; | 240 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; |
| 241 read_cb_ = BindToCurrentLoop(read_cb); | 241 read_cb_ = BindToCurrentLoop(read_cb); |
| 242 | 242 |
| 243 // Don't accept any additional reads if we've been told to stop. | 243 // Don't accept any additional reads if we've been told to stop. |
| 244 // The |demuxer_| may have been destroyed in the pipeline thread. | 244 // The |demuxer_| may have been destroyed in the pipeline thread. |
| 245 // | 245 // |
| 246 // TODO(scherkus): it would be cleaner to reply with an error message. | 246 // TODO(scherkus): it would be cleaner to reply with an error message. |
| 247 if (!demuxer_) { | 247 if (!demuxer_) { |
| 248 base::ResetAndReturn(&read_cb_).Run( | 248 base::ResetAndReturn(&read_cb_).Run( |
| 249 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 249 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 250 return; | 250 return; |
| 251 } | 251 } |
| 252 | 252 |
| 253 SatisfyPendingRead(); | 253 SatisfyPendingRead(); |
| 254 } | 254 } |
| 255 | 255 |
| 256 void FFmpegDemuxerStream::EnableBitstreamConverter() { | 256 void FFmpegDemuxerStream::EnableBitstreamConverter() { |
| 257 DCHECK(message_loop_->BelongsToCurrentThread()); | 257 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 258 CHECK(bitstream_converter_.get()); | 258 CHECK(bitstream_converter_.get()); |
| 259 bitstream_converter_enabled_ = true; | 259 bitstream_converter_enabled_ = true; |
| 260 } | 260 } |
| 261 | 261 |
| 262 AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() { | 262 AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() { |
| 263 DCHECK(message_loop_->BelongsToCurrentThread()); | 263 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 264 CHECK_EQ(type_, AUDIO); | 264 CHECK_EQ(type_, AUDIO); |
| 265 return audio_config_; | 265 return audio_config_; |
| 266 } | 266 } |
| 267 | 267 |
| 268 VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() { | 268 VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() { |
| 269 DCHECK(message_loop_->BelongsToCurrentThread()); | 269 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 270 CHECK_EQ(type_, VIDEO); | 270 CHECK_EQ(type_, VIDEO); |
| 271 return video_config_; | 271 return video_config_; |
| 272 } | 272 } |
| 273 | 273 |
| 274 FFmpegDemuxerStream::~FFmpegDemuxerStream() { | 274 FFmpegDemuxerStream::~FFmpegDemuxerStream() { |
| 275 DCHECK(!demuxer_); | 275 DCHECK(!demuxer_); |
| 276 DCHECK(read_cb_.is_null()); | 276 DCHECK(read_cb_.is_null()); |
| 277 DCHECK(buffer_queue_.IsEmpty()); | 277 DCHECK(buffer_queue_.IsEmpty()); |
| 278 } | 278 } |
| 279 | 279 |
| 280 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { | 280 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { |
| 281 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); | 281 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); |
| 282 } | 282 } |
| 283 | 283 |
| 284 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { | 284 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { |
| 285 return buffered_ranges_; | 285 return buffered_ranges_; |
| 286 } | 286 } |
| 287 | 287 |
| 288 void FFmpegDemuxerStream::SatisfyPendingRead() { | 288 void FFmpegDemuxerStream::SatisfyPendingRead() { |
| 289 DCHECK(message_loop_->BelongsToCurrentThread()); | 289 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 290 if (!read_cb_.is_null()) { | 290 if (!read_cb_.is_null()) { |
| 291 if (!buffer_queue_.IsEmpty()) { | 291 if (!buffer_queue_.IsEmpty()) { |
| 292 base::ResetAndReturn(&read_cb_).Run( | 292 base::ResetAndReturn(&read_cb_).Run( |
| 293 DemuxerStream::kOk, buffer_queue_.Pop()); | 293 DemuxerStream::kOk, buffer_queue_.Pop()); |
| 294 } else if (end_of_stream_) { | 294 } else if (end_of_stream_) { |
| 295 base::ResetAndReturn(&read_cb_).Run( | 295 base::ResetAndReturn(&read_cb_).Run( |
| 296 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 296 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 297 } | 297 } |
| 298 } | 298 } |
| 299 | 299 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) | 341 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) |
| 342 return kNoTimestamp(); | 342 return kNoTimestamp(); |
| 343 | 343 |
| 344 return ConvertFromTimeBase(time_base, timestamp); | 344 return ConvertFromTimeBase(time_base, timestamp); |
| 345 } | 345 } |
| 346 | 346 |
| 347 // | 347 // |
| 348 // FFmpegDemuxer | 348 // FFmpegDemuxer |
| 349 // | 349 // |
| 350 FFmpegDemuxer::FFmpegDemuxer( | 350 FFmpegDemuxer::FFmpegDemuxer( |
| 351 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 351 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 352 DataSource* data_source, | 352 DataSource* data_source, |
| 353 const NeedKeyCB& need_key_cb, | 353 const NeedKeyCB& need_key_cb, |
| 354 const scoped_refptr<MediaLog>& media_log) | 354 const scoped_refptr<MediaLog>& media_log) |
| 355 : host_(NULL), | 355 : host_(NULL), |
| 356 message_loop_(message_loop), | 356 task_runner_(task_runner), |
| 357 weak_factory_(this), | 357 weak_factory_(this), |
| 358 blocking_thread_("FFmpegDemuxer"), | 358 blocking_thread_("FFmpegDemuxer"), |
| 359 pending_read_(false), | 359 pending_read_(false), |
| 360 pending_seek_(false), | 360 pending_seek_(false), |
| 361 data_source_(data_source), | 361 data_source_(data_source), |
| 362 media_log_(media_log), | 362 media_log_(media_log), |
| 363 bitrate_(0), | 363 bitrate_(0), |
| 364 start_time_(kNoTimestamp()), | 364 start_time_(kNoTimestamp()), |
| 365 audio_disabled_(false), | 365 audio_disabled_(false), |
| 366 text_enabled_(false), | 366 text_enabled_(false), |
| 367 duration_known_(false), | 367 duration_known_(false), |
| 368 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 368 url_protocol_(data_source, BindToLoop(task_runner_, base::Bind( |
| 369 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), | 369 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), |
| 370 need_key_cb_(need_key_cb) { | 370 need_key_cb_(need_key_cb) { |
| 371 DCHECK(message_loop_.get()); | 371 DCHECK(task_runner_.get()); |
| 372 DCHECK(data_source_); | 372 DCHECK(data_source_); |
| 373 } | 373 } |
| 374 | 374 |
| 375 FFmpegDemuxer::~FFmpegDemuxer() {} | 375 FFmpegDemuxer::~FFmpegDemuxer() {} |
| 376 | 376 |
| 377 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 377 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
| 378 DCHECK(message_loop_->BelongsToCurrentThread()); | 378 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 379 url_protocol_.Abort(); | 379 url_protocol_.Abort(); |
| 380 data_source_->Stop(BindToCurrentLoop(base::Bind( | 380 data_source_->Stop(BindToCurrentLoop(base::Bind( |
| 381 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, | 381 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, |
| 382 BindToCurrentLoop(callback)))); | 382 BindToCurrentLoop(callback)))); |
| 383 data_source_ = NULL; | 383 data_source_ = NULL; |
| 384 } | 384 } |
| 385 | 385 |
| 386 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 386 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
| 387 DCHECK(message_loop_->BelongsToCurrentThread()); | 387 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 388 CHECK(!pending_seek_); | 388 CHECK(!pending_seek_); |
| 389 | 389 |
| 390 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 390 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| 391 // otherwise we can end up waiting for a pre-seek read to complete even though | 391 // otherwise we can end up waiting for a pre-seek read to complete even though |
| 392 // we know we're going to drop it on the floor. | 392 // we know we're going to drop it on the floor. |
| 393 | 393 |
| 394 // Always seek to a timestamp less than or equal to the desired timestamp. | 394 // Always seek to a timestamp less than or equal to the desired timestamp. |
| 395 int flags = AVSEEK_FLAG_BACKWARD; | 395 int flags = AVSEEK_FLAG_BACKWARD; |
| 396 | 396 |
| 397 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 397 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
| 398 // will attempt to use the lowest-index video stream, if present, followed by | 398 // will attempt to use the lowest-index video stream, if present, followed by |
| 399 // the lowest-index audio stream. | 399 // the lowest-index audio stream. |
| 400 pending_seek_ = true; | 400 pending_seek_ = true; |
| 401 base::PostTaskAndReplyWithResult( | 401 base::PostTaskAndReplyWithResult( |
| 402 blocking_thread_.message_loop_proxy().get(), | 402 blocking_thread_.message_loop_proxy().get(), |
| 403 FROM_HERE, | 403 FROM_HERE, |
| 404 base::Bind(&av_seek_frame, | 404 base::Bind(&av_seek_frame, |
| 405 glue_->format_context(), | 405 glue_->format_context(), |
| 406 -1, | 406 -1, |
| 407 time.InMicroseconds(), | 407 time.InMicroseconds(), |
| 408 flags), | 408 flags), |
| 409 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); | 409 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); |
| 410 } | 410 } |
| 411 | 411 |
| 412 void FFmpegDemuxer::OnAudioRendererDisabled() { | 412 void FFmpegDemuxer::OnAudioRendererDisabled() { |
| 413 DCHECK(message_loop_->BelongsToCurrentThread()); | 413 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 414 audio_disabled_ = true; | 414 audio_disabled_ = true; |
| 415 StreamVector::iterator iter; | 415 StreamVector::iterator iter; |
| 416 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 416 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 417 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 417 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| 418 (*iter)->Stop(); | 418 (*iter)->Stop(); |
| 419 } | 419 } |
| 420 } | 420 } |
| 421 } | 421 } |
| 422 | 422 |
| 423 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 423 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| 424 const PipelineStatusCB& status_cb, | 424 const PipelineStatusCB& status_cb, |
| 425 bool enable_text_tracks) { | 425 bool enable_text_tracks) { |
| 426 DCHECK(message_loop_->BelongsToCurrentThread()); | 426 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 427 host_ = host; | 427 host_ = host; |
| 428 weak_this_ = weak_factory_.GetWeakPtr(); | 428 weak_this_ = weak_factory_.GetWeakPtr(); |
| 429 text_enabled_ = enable_text_tracks; | 429 text_enabled_ = enable_text_tracks; |
| 430 | 430 |
| 431 // TODO(scherkus): DataSource should have a host by this point, | 431 // TODO(scherkus): DataSource should have a host by this point, |
| 432 // see http://crbug.com/122071 | 432 // see http://crbug.com/122071 |
| 433 data_source_->set_host(host); | 433 data_source_->set_host(host); |
| 434 | 434 |
| 435 glue_.reset(new FFmpegGlue(&url_protocol_)); | 435 glue_.reset(new FFmpegGlue(&url_protocol_)); |
| 436 AVFormatContext* format_context = glue_->format_context(); | 436 AVFormatContext* format_context = glue_->format_context(); |
| 437 | 437 |
| 438 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 438 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| 439 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 439 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| 440 // available, so add a metadata entry to ensure some is always present. | 440 // available, so add a metadata entry to ensure some is always present. |
| 441 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 441 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| 442 | 442 |
| 443 // Open the AVFormatContext using our glue layer. | 443 // Open the AVFormatContext using our glue layer. |
| 444 CHECK(blocking_thread_.Start()); | 444 CHECK(blocking_thread_.Start()); |
| 445 base::PostTaskAndReplyWithResult( | 445 base::PostTaskAndReplyWithResult( |
| 446 blocking_thread_.message_loop_proxy().get(), | 446 blocking_thread_.message_loop_proxy().get(), |
| 447 FROM_HERE, | 447 FROM_HERE, |
| 448 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 448 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| 449 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); | 449 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); |
| 450 } | 450 } |
| 451 | 451 |
| 452 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { | 452 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { |
| 453 DCHECK(message_loop_->BelongsToCurrentThread()); | 453 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 454 return GetFFmpegStream(type); | 454 return GetFFmpegStream(type); |
| 455 } | 455 } |
| 456 | 456 |
| 457 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( | 457 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( |
| 458 DemuxerStream::Type type) const { | 458 DemuxerStream::Type type) const { |
| 459 StreamVector::const_iterator iter; | 459 StreamVector::const_iterator iter; |
| 460 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 460 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 461 if (*iter && (*iter)->type() == type) { | 461 if (*iter && (*iter)->type() == type) { |
| 462 return *iter; | 462 return *iter; |
| 463 } | 463 } |
| 464 } | 464 } |
| 465 return NULL; | 465 return NULL; |
| 466 } | 466 } |
| 467 | 467 |
| 468 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | 468 base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
| 469 DCHECK(message_loop_->BelongsToCurrentThread()); | 469 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 470 return start_time_; | 470 return start_time_; |
| 471 } | 471 } |
| 472 | 472 |
| 473 void FFmpegDemuxer::AddTextStreams() { | 473 void FFmpegDemuxer::AddTextStreams() { |
| 474 DCHECK(message_loop_->BelongsToCurrentThread()); | 474 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 475 | 475 |
| 476 for (StreamVector::size_type idx = 0; idx < streams_.size(); ++idx) { | 476 for (StreamVector::size_type idx = 0; idx < streams_.size(); ++idx) { |
| 477 FFmpegDemuxerStream* stream = streams_[idx]; | 477 FFmpegDemuxerStream* stream = streams_[idx]; |
| 478 if (stream == NULL || stream->type() != DemuxerStream::TEXT) | 478 if (stream == NULL || stream->type() != DemuxerStream::TEXT) |
| 479 continue; | 479 continue; |
| 480 | 480 |
| 481 TextKind kind = stream->GetTextKind(); | 481 TextKind kind = stream->GetTextKind(); |
| 482 std::string title = stream->GetMetadata("title"); | 482 std::string title = stream->GetMetadata("title"); |
| 483 std::string language = stream->GetMetadata("language"); | 483 std::string language = stream->GetMetadata("language"); |
| 484 | 484 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 520 | 520 |
| 521 // Do math in floating point as we'd overflow an int64 if the filesize was | 521 // Do math in floating point as we'd overflow an int64 if the filesize was |
| 522 // larger than ~1073GB. | 522 // larger than ~1073GB. |
| 523 double bytes = filesize_in_bytes; | 523 double bytes = filesize_in_bytes; |
| 524 double duration_us = duration.InMicroseconds(); | 524 double duration_us = duration.InMicroseconds(); |
| 525 return bytes * 8000000.0 / duration_us; | 525 return bytes * 8000000.0 / duration_us; |
| 526 } | 526 } |
| 527 | 527 |
| 528 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, | 528 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, |
| 529 bool result) { | 529 bool result) { |
| 530 DCHECK(message_loop_->BelongsToCurrentThread()); | 530 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 531 if (!blocking_thread_.IsRunning()) { | 531 if (!blocking_thread_.IsRunning()) { |
| 532 status_cb.Run(PIPELINE_ERROR_ABORT); | 532 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 533 return; | 533 return; |
| 534 } | 534 } |
| 535 | 535 |
| 536 if (!result) { | 536 if (!result) { |
| 537 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 537 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 538 return; | 538 return; |
| 539 } | 539 } |
| 540 | 540 |
| 541 // Fully initialize AVFormatContext by parsing the stream a little. | 541 // Fully initialize AVFormatContext by parsing the stream a little. |
| 542 base::PostTaskAndReplyWithResult( | 542 base::PostTaskAndReplyWithResult( |
| 543 blocking_thread_.message_loop_proxy().get(), | 543 blocking_thread_.message_loop_proxy().get(), |
| 544 FROM_HERE, | 544 FROM_HERE, |
| 545 base::Bind(&avformat_find_stream_info, | 545 base::Bind(&avformat_find_stream_info, |
| 546 glue_->format_context(), | 546 glue_->format_context(), |
| 547 static_cast<AVDictionary**>(NULL)), | 547 static_cast<AVDictionary**>(NULL)), |
| 548 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); | 548 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); |
| 549 } | 549 } |
| 550 | 550 |
| 551 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 551 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
| 552 int result) { | 552 int result) { |
| 553 DCHECK(message_loop_->BelongsToCurrentThread()); | 553 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 554 if (!blocking_thread_.IsRunning() || !data_source_) { | 554 if (!blocking_thread_.IsRunning() || !data_source_) { |
| 555 status_cb.Run(PIPELINE_ERROR_ABORT); | 555 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 556 return; | 556 return; |
| 557 } | 557 } |
| 558 | 558 |
| 559 if (result < 0) { | 559 if (result < 0) { |
| 560 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); | 560 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); |
| 561 return; | 561 return; |
| 562 } | 562 } |
| 563 | 563 |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 721 | 721 |
| 722 | 722 |
| 723 media_log_->SetDoubleProperty("max_duration", max_duration.InSecondsF()); | 723 media_log_->SetDoubleProperty("max_duration", max_duration.InSecondsF()); |
| 724 media_log_->SetDoubleProperty("start_time", start_time_.InSecondsF()); | 724 media_log_->SetDoubleProperty("start_time", start_time_.InSecondsF()); |
| 725 media_log_->SetIntegerProperty("bitrate", bitrate_); | 725 media_log_->SetIntegerProperty("bitrate", bitrate_); |
| 726 | 726 |
| 727 status_cb.Run(PIPELINE_OK); | 727 status_cb.Run(PIPELINE_OK); |
| 728 } | 728 } |
| 729 | 729 |
| 730 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { | 730 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
| 731 DCHECK(message_loop_->BelongsToCurrentThread()); | 731 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 732 CHECK(pending_seek_); | 732 CHECK(pending_seek_); |
| 733 pending_seek_ = false; | 733 pending_seek_ = false; |
| 734 | 734 |
| 735 if (!blocking_thread_.IsRunning()) { | 735 if (!blocking_thread_.IsRunning()) { |
| 736 cb.Run(PIPELINE_ERROR_ABORT); | 736 cb.Run(PIPELINE_ERROR_ABORT); |
| 737 return; | 737 return; |
| 738 } | 738 } |
| 739 | 739 |
| 740 if (result < 0) { | 740 if (result < 0) { |
| 741 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 741 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
| (...skipping 10 matching lines...) Expand all Loading... |
| 752 } | 752 } |
| 753 | 753 |
| 754 // Resume reading until capacity. | 754 // Resume reading until capacity. |
| 755 ReadFrameIfNeeded(); | 755 ReadFrameIfNeeded(); |
| 756 | 756 |
| 757 // Notify we're finished seeking. | 757 // Notify we're finished seeking. |
| 758 cb.Run(PIPELINE_OK); | 758 cb.Run(PIPELINE_OK); |
| 759 } | 759 } |
| 760 | 760 |
| 761 void FFmpegDemuxer::ReadFrameIfNeeded() { | 761 void FFmpegDemuxer::ReadFrameIfNeeded() { |
| 762 DCHECK(message_loop_->BelongsToCurrentThread()); | 762 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 763 | 763 |
| 764 // Make sure we have work to do before reading. | 764 // Make sure we have work to do before reading. |
| 765 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || | 765 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || |
| 766 pending_read_ || pending_seek_) { | 766 pending_read_ || pending_seek_) { |
| 767 return; | 767 return; |
| 768 } | 768 } |
| 769 | 769 |
| 770 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 770 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
| 771 // evaluation order of packet.get() and base::Passed(&packet) is | 771 // evaluation order of packet.get() and base::Passed(&packet) is |
| 772 // undefined. | 772 // undefined. |
| 773 ScopedAVPacket packet(new AVPacket()); | 773 ScopedAVPacket packet(new AVPacket()); |
| 774 AVPacket* packet_ptr = packet.get(); | 774 AVPacket* packet_ptr = packet.get(); |
| 775 | 775 |
| 776 pending_read_ = true; | 776 pending_read_ = true; |
| 777 base::PostTaskAndReplyWithResult( | 777 base::PostTaskAndReplyWithResult( |
| 778 blocking_thread_.message_loop_proxy().get(), | 778 blocking_thread_.message_loop_proxy().get(), |
| 779 FROM_HERE, | 779 FROM_HERE, |
| 780 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 780 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
| 781 base::Bind( | 781 base::Bind( |
| 782 &FFmpegDemuxer::OnReadFrameDone, weak_this_, base::Passed(&packet))); | 782 &FFmpegDemuxer::OnReadFrameDone, weak_this_, base::Passed(&packet))); |
| 783 } | 783 } |
| 784 | 784 |
| 785 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 785 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
| 786 DCHECK(message_loop_->BelongsToCurrentThread()); | 786 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 787 DCHECK(pending_read_); | 787 DCHECK(pending_read_); |
| 788 pending_read_ = false; | 788 pending_read_ = false; |
| 789 | 789 |
| 790 if (!blocking_thread_.IsRunning() || pending_seek_) { | 790 if (!blocking_thread_.IsRunning() || pending_seek_) { |
| 791 return; | 791 return; |
| 792 } | 792 } |
| 793 | 793 |
| 794 if (result < 0) { | 794 if (result < 0) { |
| 795 // Update the duration based on the audio stream if | 795 // Update the duration based on the audio stream if |
| 796 // it was previously unknown http://crbug.com/86830 | 796 // it was previously unknown http://crbug.com/86830 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 864 | 864 |
| 865 // Keep reading until we've reached capacity. | 865 // Keep reading until we've reached capacity. |
| 866 ReadFrameIfNeeded(); | 866 ReadFrameIfNeeded(); |
| 867 } | 867 } |
| 868 | 868 |
| 869 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { | 869 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { |
| 870 // This will block until all tasks complete. Note that after this returns it's | 870 // This will block until all tasks complete. Note that after this returns it's |
| 871 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this | 871 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this |
| 872 // thread. Each of the reply task methods must check whether we've stopped the | 872 // thread. Each of the reply task methods must check whether we've stopped the |
| 873 // thread and drop their results on the floor. | 873 // thread and drop their results on the floor. |
| 874 DCHECK(message_loop_->BelongsToCurrentThread()); | 874 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 875 blocking_thread_.Stop(); | 875 blocking_thread_.Stop(); |
| 876 | 876 |
| 877 StreamVector::iterator iter; | 877 StreamVector::iterator iter; |
| 878 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 878 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 879 if (*iter) | 879 if (*iter) |
| 880 (*iter)->Stop(); | 880 (*iter)->Stop(); |
| 881 } | 881 } |
| 882 | 882 |
| 883 callback.Run(); | 883 callback.Run(); |
| 884 } | 884 } |
| 885 | 885 |
| 886 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { | 886 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { |
| 887 DCHECK(message_loop_->BelongsToCurrentThread()); | 887 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 888 StreamVector::iterator iter; | 888 StreamVector::iterator iter; |
| 889 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 889 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 890 if (*iter && (*iter)->HasAvailableCapacity()) { | 890 if (*iter && (*iter)->HasAvailableCapacity()) { |
| 891 return true; | 891 return true; |
| 892 } | 892 } |
| 893 } | 893 } |
| 894 return false; | 894 return false; |
| 895 } | 895 } |
| 896 | 896 |
| 897 void FFmpegDemuxer::StreamHasEnded() { | 897 void FFmpegDemuxer::StreamHasEnded() { |
| 898 DCHECK(message_loop_->BelongsToCurrentThread()); | 898 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 899 StreamVector::iterator iter; | 899 StreamVector::iterator iter; |
| 900 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 900 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 901 if (!*iter || | 901 if (!*iter || |
| 902 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { | 902 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { |
| 903 continue; | 903 continue; |
| 904 } | 904 } |
| 905 (*iter)->SetEndOfStream(); | 905 (*iter)->SetEndOfStream(); |
| 906 } | 906 } |
| 907 } | 907 } |
| 908 | 908 |
| 909 void FFmpegDemuxer::FireNeedKey(const std::string& init_data_type, | 909 void FFmpegDemuxer::FireNeedKey(const std::string& init_data_type, |
| 910 const std::string& encryption_key_id) { | 910 const std::string& encryption_key_id) { |
| 911 std::vector<uint8> key_id_local(encryption_key_id.begin(), | 911 std::vector<uint8> key_id_local(encryption_key_id.begin(), |
| 912 encryption_key_id.end()); | 912 encryption_key_id.end()); |
| 913 need_key_cb_.Run(init_data_type, key_id_local); | 913 need_key_cb_.Run(init_data_type, key_id_local); |
| 914 } | 914 } |
| 915 | 915 |
| 916 void FFmpegDemuxer::NotifyCapacityAvailable() { | 916 void FFmpegDemuxer::NotifyCapacityAvailable() { |
| 917 DCHECK(message_loop_->BelongsToCurrentThread()); | 917 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 918 ReadFrameIfNeeded(); | 918 ReadFrameIfNeeded(); |
| 919 } | 919 } |
| 920 | 920 |
| 921 void FFmpegDemuxer::NotifyBufferingChanged() { | 921 void FFmpegDemuxer::NotifyBufferingChanged() { |
| 922 DCHECK(message_loop_->BelongsToCurrentThread()); | 922 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 923 Ranges<base::TimeDelta> buffered; | 923 Ranges<base::TimeDelta> buffered; |
| 924 FFmpegDemuxerStream* audio = | 924 FFmpegDemuxerStream* audio = |
| 925 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 925 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
| 926 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); | 926 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); |
| 927 if (audio && video) { | 927 if (audio && video) { |
| 928 buffered = audio->GetBufferedRanges().IntersectionWith( | 928 buffered = audio->GetBufferedRanges().IntersectionWith( |
| 929 video->GetBufferedRanges()); | 929 video->GetBufferedRanges()); |
| 930 } else if (audio) { | 930 } else if (audio) { |
| 931 buffered = audio->GetBufferedRanges(); | 931 buffered = audio->GetBufferedRanges(); |
| 932 } else if (video) { | 932 } else if (video) { |
| 933 buffered = video->GetBufferedRanges(); | 933 buffered = video->GetBufferedRanges(); |
| 934 } | 934 } |
| 935 for (size_t i = 0; i < buffered.size(); ++i) | 935 for (size_t i = 0; i < buffered.size(); ++i) |
| 936 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 936 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 937 } | 937 } |
| 938 | 938 |
| 939 void FFmpegDemuxer::OnDataSourceError() { | 939 void FFmpegDemuxer::OnDataSourceError() { |
| 940 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 940 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 941 } | 941 } |
| 942 | 942 |
| 943 } // namespace media | 943 } // namespace media |
| OLD | NEW |