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/bind.h" | 11 #include "base/bind.h" |
| 11 #include "base/callback.h" | 12 #include "base/callback.h" |
| 12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 13 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/message_loop.h" | 15 #include "base/message_loop.h" |
| 15 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 16 #include "base/string_util.h" | 17 #include "base/string_util.h" |
| 17 #include "base/time.h" | 18 #include "base/time.h" |
| 18 #include "media/base/audio_decoder_config.h" | 19 #include "media/base/audio_decoder_config.h" |
| 19 #include "media/base/decoder_buffer.h" | 20 #include "media/base/decoder_buffer.h" |
| 20 #include "media/base/limits.h" | 21 #include "media/base/limits.h" |
| 21 #include "media/base/media_switches.h" | 22 #include "media/base/media_switches.h" |
| 22 #include "media/base/video_decoder_config.h" | 23 #include "media/base/video_decoder_config.h" |
| 24 #include "media/crypto/decryptor_helpers.h" | |
| 23 #include "media/ffmpeg/ffmpeg_common.h" | 25 #include "media/ffmpeg/ffmpeg_common.h" |
| 24 #include "media/filters/ffmpeg_glue.h" | 26 #include "media/filters/ffmpeg_glue.h" |
| 25 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" | 27 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" |
| 28 #include "media/webm/webm_crypt_helpers.h" | |
| 26 | 29 |
| 27 namespace media { | 30 namespace media { |
| 28 | 31 |
| 29 // | 32 // |
| 30 // FFmpegDemuxerStream | 33 // FFmpegDemuxerStream |
| 31 // | 34 // |
| 32 FFmpegDemuxerStream::FFmpegDemuxerStream( | 35 FFmpegDemuxerStream::FFmpegDemuxerStream( |
| 33 FFmpegDemuxer* demuxer, | 36 FFmpegDemuxer* demuxer, |
| 34 AVStream* stream) | 37 AVStream* stream) |
| 35 : demuxer_(demuxer), | 38 : demuxer_(demuxer), |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 49 type_ = VIDEO; | 52 type_ = VIDEO; |
| 50 AVStreamToVideoDecoderConfig(stream, &video_config_); | 53 AVStreamToVideoDecoderConfig(stream, &video_config_); |
| 51 break; | 54 break; |
| 52 default: | 55 default: |
| 53 NOTREACHED(); | 56 NOTREACHED(); |
| 54 break; | 57 break; |
| 55 } | 58 } |
| 56 | 59 |
| 57 // Calculate the duration. | 60 // Calculate the duration. |
| 58 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration); | 61 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration); |
| 62 | |
| 63 // *** DEBUG Until FFmpeg metadata "enck_key_id" is added for windows. *** | |
| 64 //av_dict_set(stream->metadata, "enc_key_id", "DEBUG_KEY_ID_REMOVE", 0); | |
| 65 // *** DEBUG Until FFmpeg metadata "enck_key_id" is added for windows. *** | |
| 66 | |
| 67 // Only supports WebM for now. | |
| 68 AVDictionaryEntry *key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0); | |
| 69 if (key) { | |
| 70 CHECK(key->value); | |
|
xhwang
2012/08/29 05:08:14
Can a malformed file generate a |key| without valu
fgalligan1
2013/03/09 01:10:59
Done.
| |
| 71 base::StringPiece base64_key_id(key->value); | |
| 72 std::string enc_key_id; | |
| 73 base::Base64Decode(base64_key_id, &enc_key_id); | |
| 74 CHECK(!enc_key_id.empty()); | |
|
xhwang
2012/08/29 05:08:14
Same as above, add error handling code instead of
fgalligan1
2013/03/09 01:10:59
Done.
fgalligan1
2013/03/09 01:10:59
Done.
| |
| 75 demuxer_->NeedKey(enc_key_id); | |
| 76 current_key_id_.assign(enc_key_id); | |
| 77 } | |
| 59 } | 78 } |
| 60 | 79 |
| 61 bool FFmpegDemuxerStream::HasPendingReads() { | 80 bool FFmpegDemuxerStream::HasPendingReads() { |
| 62 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); | 81 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
| 63 base::AutoLock auto_lock(lock_); | 82 base::AutoLock auto_lock(lock_); |
| 64 DCHECK(!stopped_ || read_queue_.empty()) | 83 DCHECK(!stopped_ || read_queue_.empty()) |
| 65 << "Read queue should have been emptied if demuxing stream is stopped"; | 84 << "Read queue should have been emptied if demuxing stream is stopped"; |
| 66 return !read_queue_.empty(); | 85 return !read_queue_.empty(); |
| 67 } | 86 } |
| 68 | 87 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 79 scoped_refptr<DecoderBuffer> buffer; | 98 scoped_refptr<DecoderBuffer> buffer; |
| 80 if (!packet.get()) { | 99 if (!packet.get()) { |
| 81 buffer = DecoderBuffer::CreateEOSBuffer(); | 100 buffer = DecoderBuffer::CreateEOSBuffer(); |
| 82 } else { | 101 } else { |
| 83 // Convert the packet if there is a bitstream filter. | 102 // Convert the packet if there is a bitstream filter. |
| 84 if (packet->data && bitstream_converter_.get() && | 103 if (packet->data && bitstream_converter_.get() && |
| 85 !bitstream_converter_->ConvertPacket(packet.get())) { | 104 !bitstream_converter_->ConvertPacket(packet.get())) { |
| 86 LOG(ERROR) << "Format converstion failed."; | 105 LOG(ERROR) << "Format converstion failed."; |
| 87 } | 106 } |
| 88 | 107 |
| 89 // If a packet is returned by FFmpeg's av_parser_parse2() the packet will | 108 // TODO (fgalligan): Remove the WebM specific code to process an encrypted |
| 90 // reference inner memory of FFmpeg. As such we should transfer the packet | 109 // buffer. Create a generalized class or function that can be called that |
| 91 // into memory we control. | 110 // will work for all encrypted formats. |
| 92 buffer = DecoderBuffer::CopyFrom(packet->data, packet->size); | 111 buffer = WebMCopyBufferCheckIfEncrypted( |
| 112 packet->data, | |
| 113 packet->size, | |
| 114 reinterpret_cast<const uint8*>(current_key_id_.data()), | |
| 115 current_key_id_.size()); | |
| 93 buffer->SetTimestamp(ConvertStreamTimestamp( | 116 buffer->SetTimestamp(ConvertStreamTimestamp( |
| 94 stream_->time_base, packet->pts)); | 117 stream_->time_base, packet->pts)); |
| 95 buffer->SetDuration(ConvertStreamTimestamp( | 118 buffer->SetDuration(ConvertStreamTimestamp( |
| 96 stream_->time_base, packet->duration)); | 119 stream_->time_base, packet->duration)); |
| 97 if (buffer->GetTimestamp() != kNoTimestamp() && | 120 if (buffer->GetTimestamp() != kNoTimestamp() && |
| 98 last_packet_timestamp_ != kNoTimestamp() && | 121 last_packet_timestamp_ != kNoTimestamp() && |
| 99 last_packet_timestamp_ < buffer->GetTimestamp()) { | 122 last_packet_timestamp_ < buffer->GetTimestamp()) { |
| 100 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp()); | 123 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp()); |
| 101 demuxer_->message_loop()->PostTask(FROM_HERE, base::Bind( | 124 demuxer_->message_loop()->PostTask(FROM_HERE, base::Bind( |
| 102 &FFmpegDemuxer::NotifyBufferingChanged, demuxer_)); | 125 &FFmpegDemuxer::NotifyBufferingChanged, demuxer_)); |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 return kNoTimestamp(); | 273 return kNoTimestamp(); |
| 251 | 274 |
| 252 return ConvertFromTimeBase(time_base, timestamp); | 275 return ConvertFromTimeBase(time_base, timestamp); |
| 253 } | 276 } |
| 254 | 277 |
| 255 // | 278 // |
| 256 // FFmpegDemuxer | 279 // FFmpegDemuxer |
| 257 // | 280 // |
| 258 FFmpegDemuxer::FFmpegDemuxer( | 281 FFmpegDemuxer::FFmpegDemuxer( |
| 259 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 282 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 260 const scoped_refptr<DataSource>& data_source) | 283 const scoped_refptr<DataSource>& data_source, |
| 284 const FFmpegNeedKeyCB& need_key_cb) | |
| 261 : host_(NULL), | 285 : host_(NULL), |
| 262 message_loop_(message_loop), | 286 message_loop_(message_loop), |
| 263 format_context_(NULL), | 287 format_context_(NULL), |
| 264 data_source_(data_source), | 288 data_source_(data_source), |
| 265 read_event_(false, false), | 289 read_event_(false, false), |
| 266 read_has_failed_(false), | 290 read_has_failed_(false), |
| 267 last_read_bytes_(0), | 291 last_read_bytes_(0), |
| 268 read_position_(0), | 292 read_position_(0), |
| 269 bitrate_(0), | 293 bitrate_(0), |
| 270 start_time_(kNoTimestamp()), | 294 start_time_(kNoTimestamp()), |
| 271 audio_disabled_(false), | 295 audio_disabled_(false), |
| 272 duration_known_(false) { | 296 duration_known_(false), |
| 297 need_key_cb_(need_key_cb) { | |
| 273 DCHECK(message_loop_); | 298 DCHECK(message_loop_); |
| 274 DCHECK(data_source_); | 299 DCHECK(data_source_); |
| 275 } | 300 } |
| 276 | 301 |
| 277 FFmpegDemuxer::~FFmpegDemuxer() { | 302 FFmpegDemuxer::~FFmpegDemuxer() { |
| 278 // In this destructor, we clean up resources held by FFmpeg. It is ugly to | 303 // In this destructor, we clean up resources held by FFmpeg. It is ugly to |
| 279 // close the codec contexts here because the corresponding codecs are opened | 304 // close the codec contexts here because the corresponding codecs are opened |
| 280 // in the decoder filters. By reaching this point, all filters should have | 305 // in the decoder filters. By reaching this point, all filters should have |
| 281 // stopped, so this is the only safe place to do the global clean up. | 306 // stopped, so this is the only safe place to do the global clean up. |
| 282 // TODO(hclam): close the codecs in the corresponding decoders. | 307 // TODO(hclam): close the codecs in the corresponding decoders. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 309 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 334 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
| 310 DCHECK(data_source_.get()); | 335 DCHECK(data_source_.get()); |
| 311 data_source_->SetPlaybackRate(playback_rate); | 336 data_source_->SetPlaybackRate(playback_rate); |
| 312 } | 337 } |
| 313 | 338 |
| 314 void FFmpegDemuxer::OnAudioRendererDisabled() { | 339 void FFmpegDemuxer::OnAudioRendererDisabled() { |
| 315 message_loop_->PostTask(FROM_HERE, base::Bind( | 340 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 316 &FFmpegDemuxer::DisableAudioStreamTask, this)); | 341 &FFmpegDemuxer::DisableAudioStreamTask, this)); |
| 317 } | 342 } |
| 318 | 343 |
| 344 void FFmpegDemuxer::NeedKey(const std::string& key_id) { | |
| 345 int key_id_size = key_id.size(); | |
| 346 scoped_array<uint8> key_id_local(new uint8[key_id_size]); | |
| 347 memcpy(key_id_local.get(), key_id.data(), key_id_size); | |
| 348 need_key_cb_.Run(key_id_local.Pass(), key_id_size); | |
|
xhwang
2012/08/29 05:08:14
Sad to see this conversion again. I think we need
| |
| 349 } | |
| 350 | |
| 319 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 351 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| 320 const PipelineStatusCB& status_cb) { | 352 const PipelineStatusCB& status_cb) { |
| 321 message_loop_->PostTask(FROM_HERE, base::Bind( | 353 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 322 &FFmpegDemuxer::InitializeTask, this, host, status_cb)); | 354 &FFmpegDemuxer::InitializeTask, this, host, status_cb)); |
| 323 } | 355 } |
| 324 | 356 |
| 325 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 357 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
| 326 DemuxerStream::Type type) { | 358 DemuxerStream::Type type) { |
| 327 return GetFFmpegStream(type); | 359 return GetFFmpegStream(type); |
| 328 } | 360 } |
| (...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 727 } else if (audio) { | 759 } else if (audio) { |
| 728 buffered = audio->GetBufferedRanges(); | 760 buffered = audio->GetBufferedRanges(); |
| 729 } else if (video) { | 761 } else if (video) { |
| 730 buffered = video->GetBufferedRanges(); | 762 buffered = video->GetBufferedRanges(); |
| 731 } | 763 } |
| 732 for (size_t i = 0; i < buffered.size(); ++i) | 764 for (size_t i = 0; i < buffered.size(); ++i) |
| 733 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 765 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 734 } | 766 } |
| 735 | 767 |
| 736 } // namespace media | 768 } // namespace media |
| OLD | NEW |