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 |