Chromium Code Reviews| 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/command_line.h" | 14 #include "base/command_line.h" |
| 15 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
| 16 #include "base/message_loop/message_loop.h" | |
| 17 #include "base/metrics/sparse_histogram.h" | 16 #include "base/metrics/sparse_histogram.h" |
| 18 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 19 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 20 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 21 #include "base/sys_byteorder.h" | 20 #include "base/sys_byteorder.h" |
| 22 #include "base/task_runner_util.h" | 21 #include "base/task_runner_util.h" |
| 23 #include "base/time/time.h" | 22 #include "base/time/time.h" |
| 24 #include "media/base/audio_decoder_config.h" | 23 #include "media/base/audio_decoder_config.h" |
| 25 #include "media/base/bind_to_loop.h" | 24 #include "media/base/bind_to_loop.h" |
| 26 #include "media/base/decoder_buffer.h" | 25 #include "media/base/decoder_buffer.h" |
| 27 #include "media/base/decrypt_config.h" | 26 #include "media/base/decrypt_config.h" |
| 28 #include "media/base/limits.h" | 27 #include "media/base/limits.h" |
| 29 #include "media/base/media_log.h" | 28 #include "media/base/media_log.h" |
| 30 #include "media/base/media_switches.h" | 29 #include "media/base/media_switches.h" |
| 31 #include "media/base/video_decoder_config.h" | 30 #include "media/base/video_decoder_config.h" |
| 32 #include "media/ffmpeg/ffmpeg_common.h" | 31 #include "media/ffmpeg/ffmpeg_common.h" |
| 33 #include "media/filters/ffmpeg_glue.h" | 32 #include "media/filters/ffmpeg_glue.h" |
| 34 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" | 33 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" |
| 35 #include "media/webm/webm_crypto_helpers.h" | 34 #include "media/webm/webm_crypto_helpers.h" |
| 36 | 35 |
| 37 namespace media { | 36 namespace media { |
| 38 | 37 |
| 39 // | 38 // |
| 40 // FFmpegDemuxerStream | 39 // FFmpegDemuxerStream |
| 41 // | 40 // |
| 42 FFmpegDemuxerStream::FFmpegDemuxerStream( | 41 FFmpegDemuxerStream::FFmpegDemuxerStream( |
| 43 FFmpegDemuxer* demuxer, | 42 FFmpegDemuxer* demuxer, |
| 43 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
| 44 AVStream* stream) | 44 AVStream* stream) |
| 45 : demuxer_(demuxer), | 45 : demuxer_(demuxer), |
| 46 message_loop_(base::MessageLoopProxy::current()), | 46 task_runner_(task_runner), |
|
Ami GONE FROM CHROMIUM
2013/11/08 16:54:46
I find this version slightly implying that task_ru
scherkus (not reviewing)
2013/11/08 21:51:39
This change was induced by globally removing all m
| |
| 47 stream_(stream), | 47 stream_(stream), |
| 48 type_(UNKNOWN), | 48 type_(UNKNOWN), |
| 49 end_of_stream_(false), | 49 end_of_stream_(false), |
| 50 last_packet_timestamp_(kNoTimestamp()), | 50 last_packet_timestamp_(kNoTimestamp()), |
| 51 bitstream_converter_enabled_(false) { | 51 bitstream_converter_enabled_(false) { |
| 52 DCHECK(demuxer_); | 52 DCHECK(demuxer_); |
| 53 | 53 |
| 54 bool is_encrypted = false; | 54 bool is_encrypted = false; |
| 55 | 55 |
| 56 // Determine our media format. | 56 // Determine our media format. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 DCHECK(!enc_key_id.empty()); | 91 DCHECK(!enc_key_id.empty()); |
| 92 if (enc_key_id.empty()) | 92 if (enc_key_id.empty()) |
| 93 return; | 93 return; |
| 94 | 94 |
| 95 encryption_key_id_.assign(enc_key_id); | 95 encryption_key_id_.assign(enc_key_id); |
| 96 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); | 96 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); |
| 97 } | 97 } |
| 98 } | 98 } |
| 99 | 99 |
| 100 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { | 100 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { |
| 101 DCHECK(message_loop_->BelongsToCurrentThread()); | 101 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 102 | 102 |
| 103 if (!demuxer_ || end_of_stream_) { | 103 if (!demuxer_ || end_of_stream_) { |
| 104 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; | 104 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; |
| 105 return; | 105 return; |
| 106 } | 106 } |
| 107 | 107 |
| 108 // Convert the packet if there is a bitstream filter. | 108 // Convert the packet if there is a bitstream filter. |
| 109 if (packet->data && bitstream_converter_enabled_ && | 109 if (packet->data && bitstream_converter_enabled_ && |
| 110 !bitstream_converter_->ConvertPacket(packet.get())) { | 110 !bitstream_converter_->ConvertPacket(packet.get())) { |
| 111 LOG(ERROR) << "Format conversion failed."; | 111 LOG(ERROR) << "Format conversion failed."; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); | 170 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
| 171 demuxer_->NotifyBufferingChanged(); | 171 demuxer_->NotifyBufferingChanged(); |
| 172 } | 172 } |
| 173 last_packet_timestamp_ = buffer->timestamp(); | 173 last_packet_timestamp_ = buffer->timestamp(); |
| 174 | 174 |
| 175 buffer_queue_.Push(buffer); | 175 buffer_queue_.Push(buffer); |
| 176 SatisfyPendingRead(); | 176 SatisfyPendingRead(); |
| 177 } | 177 } |
| 178 | 178 |
| 179 void FFmpegDemuxerStream::SetEndOfStream() { | 179 void FFmpegDemuxerStream::SetEndOfStream() { |
| 180 DCHECK(message_loop_->BelongsToCurrentThread()); | 180 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 181 end_of_stream_ = true; | 181 end_of_stream_ = true; |
| 182 SatisfyPendingRead(); | 182 SatisfyPendingRead(); |
| 183 } | 183 } |
| 184 | 184 |
| 185 void FFmpegDemuxerStream::FlushBuffers() { | 185 void FFmpegDemuxerStream::FlushBuffers() { |
| 186 DCHECK(message_loop_->BelongsToCurrentThread()); | 186 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 187 DCHECK(read_cb_.is_null()) << "There should be no pending read"; | 187 DCHECK(read_cb_.is_null()) << "There should be no pending read"; |
| 188 buffer_queue_.Clear(); | 188 buffer_queue_.Clear(); |
| 189 end_of_stream_ = false; | 189 end_of_stream_ = false; |
| 190 last_packet_timestamp_ = kNoTimestamp(); | 190 last_packet_timestamp_ = kNoTimestamp(); |
| 191 } | 191 } |
| 192 | 192 |
| 193 void FFmpegDemuxerStream::Stop() { | 193 void FFmpegDemuxerStream::Stop() { |
| 194 DCHECK(message_loop_->BelongsToCurrentThread()); | 194 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 195 buffer_queue_.Clear(); | 195 buffer_queue_.Clear(); |
| 196 if (!read_cb_.is_null()) { | 196 if (!read_cb_.is_null()) { |
| 197 base::ResetAndReturn(&read_cb_).Run( | 197 base::ResetAndReturn(&read_cb_).Run( |
| 198 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 198 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 199 } | 199 } |
| 200 demuxer_ = NULL; | 200 demuxer_ = NULL; |
| 201 stream_ = NULL; | 201 stream_ = NULL; |
| 202 end_of_stream_ = true; | 202 end_of_stream_ = true; |
| 203 } | 203 } |
| 204 | 204 |
| 205 base::TimeDelta FFmpegDemuxerStream::duration() { | 205 base::TimeDelta FFmpegDemuxerStream::duration() { |
| 206 return duration_; | 206 return duration_; |
| 207 } | 207 } |
| 208 | 208 |
| 209 DemuxerStream::Type FFmpegDemuxerStream::type() { | 209 DemuxerStream::Type FFmpegDemuxerStream::type() { |
| 210 DCHECK(message_loop_->BelongsToCurrentThread()); | 210 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 211 return type_; | 211 return type_; |
| 212 } | 212 } |
| 213 | 213 |
| 214 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { | 214 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
| 215 DCHECK(message_loop_->BelongsToCurrentThread()); | 215 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 216 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; | 216 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; |
| 217 read_cb_ = BindToCurrentLoop(read_cb); | 217 read_cb_ = BindToCurrentLoop(read_cb); |
| 218 | 218 |
| 219 // Don't accept any additional reads if we've been told to stop. | 219 // Don't accept any additional reads if we've been told to stop. |
| 220 // The |demuxer_| may have been destroyed in the pipeline thread. | 220 // The |demuxer_| may have been destroyed in the pipeline thread. |
| 221 // | 221 // |
| 222 // TODO(scherkus): it would be cleaner to reply with an error message. | 222 // TODO(scherkus): it would be cleaner to reply with an error message. |
| 223 if (!demuxer_) { | 223 if (!demuxer_) { |
| 224 base::ResetAndReturn(&read_cb_).Run( | 224 base::ResetAndReturn(&read_cb_).Run( |
| 225 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 225 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 226 return; | 226 return; |
| 227 } | 227 } |
| 228 | 228 |
| 229 SatisfyPendingRead(); | 229 SatisfyPendingRead(); |
| 230 } | 230 } |
| 231 | 231 |
| 232 void FFmpegDemuxerStream::EnableBitstreamConverter() { | 232 void FFmpegDemuxerStream::EnableBitstreamConverter() { |
| 233 DCHECK(message_loop_->BelongsToCurrentThread()); | 233 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 234 CHECK(bitstream_converter_.get()); | 234 CHECK(bitstream_converter_.get()); |
| 235 bitstream_converter_enabled_ = true; | 235 bitstream_converter_enabled_ = true; |
| 236 } | 236 } |
| 237 | 237 |
| 238 AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() { | 238 AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() { |
| 239 DCHECK(message_loop_->BelongsToCurrentThread()); | 239 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 240 CHECK_EQ(type_, AUDIO); | 240 CHECK_EQ(type_, AUDIO); |
| 241 return audio_config_; | 241 return audio_config_; |
| 242 } | 242 } |
| 243 | 243 |
| 244 VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() { | 244 VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() { |
| 245 DCHECK(message_loop_->BelongsToCurrentThread()); | 245 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 246 CHECK_EQ(type_, VIDEO); | 246 CHECK_EQ(type_, VIDEO); |
| 247 return video_config_; | 247 return video_config_; |
| 248 } | 248 } |
| 249 | 249 |
| 250 FFmpegDemuxerStream::~FFmpegDemuxerStream() { | 250 FFmpegDemuxerStream::~FFmpegDemuxerStream() { |
| 251 DCHECK(!demuxer_); | 251 DCHECK(!demuxer_); |
| 252 DCHECK(read_cb_.is_null()); | 252 DCHECK(read_cb_.is_null()); |
| 253 DCHECK(buffer_queue_.IsEmpty()); | 253 DCHECK(buffer_queue_.IsEmpty()); |
| 254 } | 254 } |
| 255 | 255 |
| 256 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { | 256 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { |
| 257 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); | 257 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); |
| 258 } | 258 } |
| 259 | 259 |
| 260 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { | 260 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { |
| 261 return buffered_ranges_; | 261 return buffered_ranges_; |
| 262 } | 262 } |
| 263 | 263 |
| 264 void FFmpegDemuxerStream::SatisfyPendingRead() { | 264 void FFmpegDemuxerStream::SatisfyPendingRead() { |
| 265 DCHECK(message_loop_->BelongsToCurrentThread()); | 265 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 266 if (!read_cb_.is_null()) { | 266 if (!read_cb_.is_null()) { |
| 267 if (!buffer_queue_.IsEmpty()) { | 267 if (!buffer_queue_.IsEmpty()) { |
| 268 base::ResetAndReturn(&read_cb_).Run( | 268 base::ResetAndReturn(&read_cb_).Run( |
| 269 DemuxerStream::kOk, buffer_queue_.Pop()); | 269 DemuxerStream::kOk, buffer_queue_.Pop()); |
| 270 } else if (end_of_stream_) { | 270 } else if (end_of_stream_) { |
| 271 base::ResetAndReturn(&read_cb_).Run( | 271 base::ResetAndReturn(&read_cb_).Run( |
| 272 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 272 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 273 } | 273 } |
| 274 } | 274 } |
| 275 | 275 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 296 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) | 296 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) |
| 297 return kNoTimestamp(); | 297 return kNoTimestamp(); |
| 298 | 298 |
| 299 return ConvertFromTimeBase(time_base, timestamp); | 299 return ConvertFromTimeBase(time_base, timestamp); |
| 300 } | 300 } |
| 301 | 301 |
| 302 // | 302 // |
| 303 // FFmpegDemuxer | 303 // FFmpegDemuxer |
| 304 // | 304 // |
| 305 FFmpegDemuxer::FFmpegDemuxer( | 305 FFmpegDemuxer::FFmpegDemuxer( |
| 306 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 306 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 307 DataSource* data_source, | 307 DataSource* data_source, |
| 308 const NeedKeyCB& need_key_cb, | 308 const NeedKeyCB& need_key_cb, |
| 309 const scoped_refptr<MediaLog>& media_log) | 309 const scoped_refptr<MediaLog>& media_log) |
| 310 : host_(NULL), | 310 : host_(NULL), |
| 311 message_loop_(message_loop), | 311 task_runner_(task_runner), |
| 312 weak_factory_(this), | 312 weak_factory_(this), |
| 313 blocking_thread_("FFmpegDemuxer"), | 313 blocking_thread_("FFmpegDemuxer"), |
| 314 pending_read_(false), | 314 pending_read_(false), |
| 315 pending_seek_(false), | 315 pending_seek_(false), |
| 316 data_source_(data_source), | 316 data_source_(data_source), |
| 317 media_log_(media_log), | 317 media_log_(media_log), |
| 318 bitrate_(0), | 318 bitrate_(0), |
| 319 start_time_(kNoTimestamp()), | 319 start_time_(kNoTimestamp()), |
| 320 audio_disabled_(false), | 320 audio_disabled_(false), |
| 321 duration_known_(false), | 321 duration_known_(false), |
| 322 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 322 url_protocol_(data_source, BindToLoop(task_runner_, base::Bind( |
| 323 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), | 323 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), |
| 324 need_key_cb_(need_key_cb) { | 324 need_key_cb_(need_key_cb) { |
| 325 DCHECK(message_loop_.get()); | 325 DCHECK(task_runner_.get()); |
| 326 DCHECK(data_source_); | 326 DCHECK(data_source_); |
| 327 } | 327 } |
| 328 | 328 |
| 329 FFmpegDemuxer::~FFmpegDemuxer() {} | 329 FFmpegDemuxer::~FFmpegDemuxer() {} |
| 330 | 330 |
| 331 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 331 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
| 332 DCHECK(message_loop_->BelongsToCurrentThread()); | 332 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 333 url_protocol_.Abort(); | 333 url_protocol_.Abort(); |
| 334 data_source_->Stop(BindToCurrentLoop(base::Bind( | 334 data_source_->Stop(BindToCurrentLoop(base::Bind( |
| 335 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, | 335 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, |
| 336 BindToCurrentLoop(callback)))); | 336 BindToCurrentLoop(callback)))); |
| 337 data_source_ = NULL; | 337 data_source_ = NULL; |
| 338 } | 338 } |
| 339 | 339 |
| 340 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 340 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
| 341 DCHECK(message_loop_->BelongsToCurrentThread()); | 341 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 342 CHECK(!pending_seek_); | 342 CHECK(!pending_seek_); |
| 343 | 343 |
| 344 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 344 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| 345 // otherwise we can end up waiting for a pre-seek read to complete even though | 345 // otherwise we can end up waiting for a pre-seek read to complete even though |
| 346 // we know we're going to drop it on the floor. | 346 // we know we're going to drop it on the floor. |
| 347 | 347 |
| 348 // Always seek to a timestamp less than or equal to the desired timestamp. | 348 // Always seek to a timestamp less than or equal to the desired timestamp. |
| 349 int flags = AVSEEK_FLAG_BACKWARD; | 349 int flags = AVSEEK_FLAG_BACKWARD; |
| 350 | 350 |
| 351 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 351 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
| 352 // will attempt to use the lowest-index video stream, if present, followed by | 352 // will attempt to use the lowest-index video stream, if present, followed by |
| 353 // the lowest-index audio stream. | 353 // the lowest-index audio stream. |
| 354 pending_seek_ = true; | 354 pending_seek_ = true; |
| 355 base::PostTaskAndReplyWithResult( | 355 base::PostTaskAndReplyWithResult( |
| 356 blocking_thread_.message_loop_proxy().get(), | 356 blocking_thread_.message_loop_proxy().get(), |
| 357 FROM_HERE, | 357 FROM_HERE, |
| 358 base::Bind(&av_seek_frame, | 358 base::Bind(&av_seek_frame, |
| 359 glue_->format_context(), | 359 glue_->format_context(), |
| 360 -1, | 360 -1, |
| 361 time.InMicroseconds(), | 361 time.InMicroseconds(), |
| 362 flags), | 362 flags), |
| 363 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); | 363 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); |
| 364 } | 364 } |
| 365 | 365 |
| 366 void FFmpegDemuxer::OnAudioRendererDisabled() { | 366 void FFmpegDemuxer::OnAudioRendererDisabled() { |
| 367 DCHECK(message_loop_->BelongsToCurrentThread()); | 367 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 368 audio_disabled_ = true; | 368 audio_disabled_ = true; |
| 369 StreamVector::iterator iter; | 369 StreamVector::iterator iter; |
| 370 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 370 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 371 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 371 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| 372 (*iter)->Stop(); | 372 (*iter)->Stop(); |
| 373 } | 373 } |
| 374 } | 374 } |
| 375 } | 375 } |
| 376 | 376 |
| 377 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 377 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| 378 const PipelineStatusCB& status_cb) { | 378 const PipelineStatusCB& status_cb) { |
| 379 DCHECK(message_loop_->BelongsToCurrentThread()); | 379 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 380 host_ = host; | 380 host_ = host; |
| 381 weak_this_ = weak_factory_.GetWeakPtr(); | 381 weak_this_ = weak_factory_.GetWeakPtr(); |
| 382 | 382 |
| 383 // TODO(scherkus): DataSource should have a host by this point, | 383 // TODO(scherkus): DataSource should have a host by this point, |
| 384 // see http://crbug.com/122071 | 384 // see http://crbug.com/122071 |
| 385 data_source_->set_host(host); | 385 data_source_->set_host(host); |
| 386 | 386 |
| 387 glue_.reset(new FFmpegGlue(&url_protocol_)); | 387 glue_.reset(new FFmpegGlue(&url_protocol_)); |
| 388 AVFormatContext* format_context = glue_->format_context(); | 388 AVFormatContext* format_context = glue_->format_context(); |
| 389 | 389 |
| 390 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 390 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| 391 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 391 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| 392 // available, so add a metadata entry to ensure some is always present. | 392 // available, so add a metadata entry to ensure some is always present. |
| 393 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 393 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| 394 | 394 |
| 395 // Open the AVFormatContext using our glue layer. | 395 // Open the AVFormatContext using our glue layer. |
| 396 CHECK(blocking_thread_.Start()); | 396 CHECK(blocking_thread_.Start()); |
| 397 base::PostTaskAndReplyWithResult( | 397 base::PostTaskAndReplyWithResult( |
| 398 blocking_thread_.message_loop_proxy().get(), | 398 blocking_thread_.message_loop_proxy().get(), |
| 399 FROM_HERE, | 399 FROM_HERE, |
| 400 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 400 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| 401 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); | 401 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); |
| 402 } | 402 } |
| 403 | 403 |
| 404 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { | 404 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { |
| 405 DCHECK(message_loop_->BelongsToCurrentThread()); | 405 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 406 return GetFFmpegStream(type); | 406 return GetFFmpegStream(type); |
| 407 } | 407 } |
| 408 | 408 |
| 409 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( | 409 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( |
| 410 DemuxerStream::Type type) const { | 410 DemuxerStream::Type type) const { |
| 411 StreamVector::const_iterator iter; | 411 StreamVector::const_iterator iter; |
| 412 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 412 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 413 if (*iter && (*iter)->type() == type) { | 413 if (*iter && (*iter)->type() == type) { |
| 414 return *iter; | 414 return *iter; |
| 415 } | 415 } |
| 416 } | 416 } |
| 417 return NULL; | 417 return NULL; |
| 418 } | 418 } |
| 419 | 419 |
| 420 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | 420 base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
| 421 DCHECK(message_loop_->BelongsToCurrentThread()); | 421 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 422 return start_time_; | 422 return start_time_; |
| 423 } | 423 } |
| 424 | 424 |
| 425 // Helper for calculating the bitrate of the media based on information stored | 425 // Helper for calculating the bitrate of the media based on information stored |
| 426 // in |format_context| or failing that the size and duration of the media. | 426 // in |format_context| or failing that the size and duration of the media. |
| 427 // | 427 // |
| 428 // Returns 0 if a bitrate could not be determined. | 428 // Returns 0 if a bitrate could not be determined. |
| 429 static int CalculateBitrate( | 429 static int CalculateBitrate( |
| 430 AVFormatContext* format_context, | 430 AVFormatContext* format_context, |
| 431 const base::TimeDelta& duration, | 431 const base::TimeDelta& duration, |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 453 | 453 |
| 454 // Do math in floating point as we'd overflow an int64 if the filesize was | 454 // Do math in floating point as we'd overflow an int64 if the filesize was |
| 455 // larger than ~1073GB. | 455 // larger than ~1073GB. |
| 456 double bytes = filesize_in_bytes; | 456 double bytes = filesize_in_bytes; |
| 457 double duration_us = duration.InMicroseconds(); | 457 double duration_us = duration.InMicroseconds(); |
| 458 return bytes * 8000000.0 / duration_us; | 458 return bytes * 8000000.0 / duration_us; |
| 459 } | 459 } |
| 460 | 460 |
| 461 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, | 461 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, |
| 462 bool result) { | 462 bool result) { |
| 463 DCHECK(message_loop_->BelongsToCurrentThread()); | 463 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 464 if (!blocking_thread_.IsRunning()) { | 464 if (!blocking_thread_.IsRunning()) { |
| 465 status_cb.Run(PIPELINE_ERROR_ABORT); | 465 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 466 return; | 466 return; |
| 467 } | 467 } |
| 468 | 468 |
| 469 if (!result) { | 469 if (!result) { |
| 470 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 470 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 471 return; | 471 return; |
| 472 } | 472 } |
| 473 | 473 |
| 474 // Fully initialize AVFormatContext by parsing the stream a little. | 474 // Fully initialize AVFormatContext by parsing the stream a little. |
| 475 base::PostTaskAndReplyWithResult( | 475 base::PostTaskAndReplyWithResult( |
| 476 blocking_thread_.message_loop_proxy().get(), | 476 blocking_thread_.message_loop_proxy().get(), |
| 477 FROM_HERE, | 477 FROM_HERE, |
| 478 base::Bind(&avformat_find_stream_info, | 478 base::Bind(&avformat_find_stream_info, |
| 479 glue_->format_context(), | 479 glue_->format_context(), |
| 480 static_cast<AVDictionary**>(NULL)), | 480 static_cast<AVDictionary**>(NULL)), |
| 481 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); | 481 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); |
| 482 } | 482 } |
| 483 | 483 |
| 484 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 484 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
| 485 int result) { | 485 int result) { |
| 486 DCHECK(message_loop_->BelongsToCurrentThread()); | 486 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 487 if (!blocking_thread_.IsRunning() || !data_source_) { | 487 if (!blocking_thread_.IsRunning() || !data_source_) { |
| 488 status_cb.Run(PIPELINE_ERROR_ABORT); | 488 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 489 return; | 489 return; |
| 490 } | 490 } |
| 491 | 491 |
| 492 if (result < 0) { | 492 if (result < 0) { |
| 493 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); | 493 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); |
| 494 return; | 494 return; |
| 495 } | 495 } |
| 496 | 496 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 537 // frame size and visible size are valid. | 537 // frame size and visible size are valid. |
| 538 AVStreamToVideoDecoderConfig(stream, &video_config, false); | 538 AVStreamToVideoDecoderConfig(stream, &video_config, false); |
| 539 | 539 |
| 540 if (!video_config.IsValidConfig()) | 540 if (!video_config.IsValidConfig()) |
| 541 continue; | 541 continue; |
| 542 video_stream = stream; | 542 video_stream = stream; |
| 543 } else { | 543 } else { |
| 544 continue; | 544 continue; |
| 545 } | 545 } |
| 546 | 546 |
| 547 streams_[i] = new FFmpegDemuxerStream(this, stream); | 547 streams_[i] = new FFmpegDemuxerStream(this, task_runner_, stream); |
| 548 max_duration = std::max(max_duration, streams_[i]->duration()); | 548 max_duration = std::max(max_duration, streams_[i]->duration()); |
| 549 | 549 |
| 550 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 550 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 551 const base::TimeDelta first_dts = ConvertFromTimeBase( | 551 const base::TimeDelta first_dts = ConvertFromTimeBase( |
| 552 stream->time_base, stream->first_dts); | 552 stream->time_base, stream->first_dts); |
| 553 if (start_time_ == kNoTimestamp() || first_dts < start_time_) | 553 if (start_time_ == kNoTimestamp() || first_dts < start_time_) |
| 554 start_time_ = first_dts; | 554 start_time_ = first_dts; |
| 555 } | 555 } |
| 556 } | 556 } |
| 557 | 557 |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 647 | 647 |
| 648 | 648 |
| 649 media_log_->SetDoubleProperty("max_duration", max_duration.InSecondsF()); | 649 media_log_->SetDoubleProperty("max_duration", max_duration.InSecondsF()); |
| 650 media_log_->SetDoubleProperty("start_time", start_time_.InSecondsF()); | 650 media_log_->SetDoubleProperty("start_time", start_time_.InSecondsF()); |
| 651 media_log_->SetIntegerProperty("bitrate", bitrate_); | 651 media_log_->SetIntegerProperty("bitrate", bitrate_); |
| 652 | 652 |
| 653 status_cb.Run(PIPELINE_OK); | 653 status_cb.Run(PIPELINE_OK); |
| 654 } | 654 } |
| 655 | 655 |
| 656 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { | 656 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
| 657 DCHECK(message_loop_->BelongsToCurrentThread()); | 657 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 658 CHECK(pending_seek_); | 658 CHECK(pending_seek_); |
| 659 pending_seek_ = false; | 659 pending_seek_ = false; |
| 660 | 660 |
| 661 if (!blocking_thread_.IsRunning()) { | 661 if (!blocking_thread_.IsRunning()) { |
| 662 cb.Run(PIPELINE_ERROR_ABORT); | 662 cb.Run(PIPELINE_ERROR_ABORT); |
| 663 return; | 663 return; |
| 664 } | 664 } |
| 665 | 665 |
| 666 if (result < 0) { | 666 if (result < 0) { |
| 667 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 667 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 678 } | 678 } |
| 679 | 679 |
| 680 // Resume reading until capacity. | 680 // Resume reading until capacity. |
| 681 ReadFrameIfNeeded(); | 681 ReadFrameIfNeeded(); |
| 682 | 682 |
| 683 // Notify we're finished seeking. | 683 // Notify we're finished seeking. |
| 684 cb.Run(PIPELINE_OK); | 684 cb.Run(PIPELINE_OK); |
| 685 } | 685 } |
| 686 | 686 |
| 687 void FFmpegDemuxer::ReadFrameIfNeeded() { | 687 void FFmpegDemuxer::ReadFrameIfNeeded() { |
| 688 DCHECK(message_loop_->BelongsToCurrentThread()); | 688 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 689 | 689 |
| 690 // Make sure we have work to do before reading. | 690 // Make sure we have work to do before reading. |
| 691 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || | 691 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || |
| 692 pending_read_ || pending_seek_) { | 692 pending_read_ || pending_seek_) { |
| 693 return; | 693 return; |
| 694 } | 694 } |
| 695 | 695 |
| 696 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 696 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
| 697 // evaluation order of packet.get() and base::Passed(&packet) is | 697 // evaluation order of packet.get() and base::Passed(&packet) is |
| 698 // undefined. | 698 // undefined. |
| 699 ScopedAVPacket packet(new AVPacket()); | 699 ScopedAVPacket packet(new AVPacket()); |
| 700 AVPacket* packet_ptr = packet.get(); | 700 AVPacket* packet_ptr = packet.get(); |
| 701 | 701 |
| 702 pending_read_ = true; | 702 pending_read_ = true; |
| 703 base::PostTaskAndReplyWithResult( | 703 base::PostTaskAndReplyWithResult( |
| 704 blocking_thread_.message_loop_proxy().get(), | 704 blocking_thread_.message_loop_proxy().get(), |
| 705 FROM_HERE, | 705 FROM_HERE, |
| 706 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 706 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
| 707 base::Bind( | 707 base::Bind( |
| 708 &FFmpegDemuxer::OnReadFrameDone, weak_this_, base::Passed(&packet))); | 708 &FFmpegDemuxer::OnReadFrameDone, weak_this_, base::Passed(&packet))); |
| 709 } | 709 } |
| 710 | 710 |
| 711 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 711 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
| 712 DCHECK(message_loop_->BelongsToCurrentThread()); | 712 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 713 DCHECK(pending_read_); | 713 DCHECK(pending_read_); |
| 714 pending_read_ = false; | 714 pending_read_ = false; |
| 715 | 715 |
| 716 if (!blocking_thread_.IsRunning() || pending_seek_) { | 716 if (!blocking_thread_.IsRunning() || pending_seek_) { |
| 717 return; | 717 return; |
| 718 } | 718 } |
| 719 | 719 |
| 720 if (result < 0) { | 720 if (result < 0) { |
| 721 // Update the duration based on the audio stream if | 721 // Update the duration based on the audio stream if |
| 722 // it was previously unknown http://crbug.com/86830 | 722 // it was previously unknown http://crbug.com/86830 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 777 | 777 |
| 778 // Keep reading until we've reached capacity. | 778 // Keep reading until we've reached capacity. |
| 779 ReadFrameIfNeeded(); | 779 ReadFrameIfNeeded(); |
| 780 } | 780 } |
| 781 | 781 |
| 782 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { | 782 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { |
| 783 // This will block until all tasks complete. Note that after this returns it's | 783 // This will block until all tasks complete. Note that after this returns it's |
| 784 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this | 784 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this |
| 785 // thread. Each of the reply task methods must check whether we've stopped the | 785 // thread. Each of the reply task methods must check whether we've stopped the |
| 786 // thread and drop their results on the floor. | 786 // thread and drop their results on the floor. |
| 787 DCHECK(message_loop_->BelongsToCurrentThread()); | 787 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 788 blocking_thread_.Stop(); | 788 blocking_thread_.Stop(); |
| 789 | 789 |
| 790 StreamVector::iterator iter; | 790 StreamVector::iterator iter; |
| 791 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 791 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 792 if (*iter) | 792 if (*iter) |
| 793 (*iter)->Stop(); | 793 (*iter)->Stop(); |
| 794 } | 794 } |
| 795 | 795 |
| 796 callback.Run(); | 796 callback.Run(); |
| 797 } | 797 } |
| 798 | 798 |
| 799 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { | 799 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { |
| 800 DCHECK(message_loop_->BelongsToCurrentThread()); | 800 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 801 StreamVector::iterator iter; | 801 StreamVector::iterator iter; |
| 802 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 802 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 803 if (*iter && (*iter)->HasAvailableCapacity()) { | 803 if (*iter && (*iter)->HasAvailableCapacity()) { |
| 804 return true; | 804 return true; |
| 805 } | 805 } |
| 806 } | 806 } |
| 807 return false; | 807 return false; |
| 808 } | 808 } |
| 809 | 809 |
| 810 void FFmpegDemuxer::StreamHasEnded() { | 810 void FFmpegDemuxer::StreamHasEnded() { |
| 811 DCHECK(message_loop_->BelongsToCurrentThread()); | 811 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 812 StreamVector::iterator iter; | 812 StreamVector::iterator iter; |
| 813 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 813 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 814 if (!*iter || | 814 if (!*iter || |
| 815 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { | 815 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { |
| 816 continue; | 816 continue; |
| 817 } | 817 } |
| 818 (*iter)->SetEndOfStream(); | 818 (*iter)->SetEndOfStream(); |
| 819 } | 819 } |
| 820 } | 820 } |
| 821 | 821 |
| 822 void FFmpegDemuxer::FireNeedKey(const std::string& init_data_type, | 822 void FFmpegDemuxer::FireNeedKey(const std::string& init_data_type, |
| 823 const std::string& encryption_key_id) { | 823 const std::string& encryption_key_id) { |
| 824 std::vector<uint8> key_id_local(encryption_key_id.begin(), | 824 std::vector<uint8> key_id_local(encryption_key_id.begin(), |
| 825 encryption_key_id.end()); | 825 encryption_key_id.end()); |
| 826 need_key_cb_.Run(init_data_type, key_id_local); | 826 need_key_cb_.Run(init_data_type, key_id_local); |
| 827 } | 827 } |
| 828 | 828 |
| 829 void FFmpegDemuxer::NotifyCapacityAvailable() { | 829 void FFmpegDemuxer::NotifyCapacityAvailable() { |
| 830 DCHECK(message_loop_->BelongsToCurrentThread()); | 830 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 831 ReadFrameIfNeeded(); | 831 ReadFrameIfNeeded(); |
| 832 } | 832 } |
| 833 | 833 |
| 834 void FFmpegDemuxer::NotifyBufferingChanged() { | 834 void FFmpegDemuxer::NotifyBufferingChanged() { |
| 835 DCHECK(message_loop_->BelongsToCurrentThread()); | 835 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 836 Ranges<base::TimeDelta> buffered; | 836 Ranges<base::TimeDelta> buffered; |
| 837 FFmpegDemuxerStream* audio = | 837 FFmpegDemuxerStream* audio = |
| 838 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 838 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
| 839 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); | 839 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); |
| 840 if (audio && video) { | 840 if (audio && video) { |
| 841 buffered = audio->GetBufferedRanges().IntersectionWith( | 841 buffered = audio->GetBufferedRanges().IntersectionWith( |
| 842 video->GetBufferedRanges()); | 842 video->GetBufferedRanges()); |
| 843 } else if (audio) { | 843 } else if (audio) { |
| 844 buffered = audio->GetBufferedRanges(); | 844 buffered = audio->GetBufferedRanges(); |
| 845 } else if (video) { | 845 } else if (video) { |
| 846 buffered = video->GetBufferedRanges(); | 846 buffered = video->GetBufferedRanges(); |
| 847 } | 847 } |
| 848 for (size_t i = 0; i < buffered.size(); ++i) | 848 for (size_t i = 0; i < buffered.size(); ++i) |
| 849 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 849 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 850 } | 850 } |
| 851 | 851 |
| 852 void FFmpegDemuxer::OnDataSourceError() { | 852 void FFmpegDemuxer::OnDataSourceError() { |
| 853 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 853 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 854 } | 854 } |
| 855 | 855 |
| 856 } // namespace media | 856 } // namespace media |
| OLD | NEW |