Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: media/filters/ffmpeg_demuxer.cc

Issue 10829470: Support for parsing encrypted WebM streams by src. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressing comments. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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"
18 #include "base/sys_byteorder.h"
17 #include "base/time.h" 19 #include "base/time.h"
18 #include "media/base/audio_decoder_config.h" 20 #include "media/base/audio_decoder_config.h"
19 #include "media/base/decoder_buffer.h" 21 #include "media/base/decoder_buffer.h"
20 #include "media/base/limits.h" 22 #include "media/base/limits.h"
21 #include "media/base/media_switches.h" 23 #include "media/base/media_switches.h"
22 #include "media/base/video_decoder_config.h" 24 #include "media/base/video_decoder_config.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_constants.h"
26 29
27 namespace media { 30 namespace media {
28 31
32 // Generates a 16 byte CTR counter block. The CTR counter block format is a
33 // CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV.
34 // Always returns a valid pointer to a buffer of kDecryptionKeySize bytes.
35 static scoped_array<uint8> GenerateCounterBlock(uint64 iv) {
36 scoped_array<uint8> counter_block_data(
37 new uint8[DecryptConfig::kDecryptionKeySize]);
38
39 // Set the IV.
40 memcpy(counter_block_data.get(), &iv, sizeof(iv));
41
42 // Set block counter to all 0's.
43 memset(counter_block_data.get() + sizeof(iv),
44 0,
45 DecryptConfig::kDecryptionKeySize - sizeof(iv));
46
47 return counter_block_data.Pass();
48 }
49
29 // 50 //
30 // FFmpegDemuxerStream 51 // FFmpegDemuxerStream
31 // 52 //
32 FFmpegDemuxerStream::FFmpegDemuxerStream( 53 FFmpegDemuxerStream::FFmpegDemuxerStream(
33 FFmpegDemuxer* demuxer, 54 FFmpegDemuxer* demuxer,
34 AVStream* stream) 55 AVStream* stream)
35 : demuxer_(demuxer), 56 : demuxer_(demuxer),
36 stream_(stream), 57 stream_(stream),
37 type_(UNKNOWN), 58 type_(UNKNOWN),
38 stopped_(false), 59 stopped_(false),
39 last_packet_timestamp_(kNoTimestamp()) { 60 last_packet_timestamp_(kNoTimestamp()),
61 need_decryption_key_(false) {
40 DCHECK(demuxer_); 62 DCHECK(demuxer_);
41 63
42 // Determine our media format. 64 // Determine our media format.
43 switch (stream->codec->codec_type) { 65 switch (stream->codec->codec_type) {
44 case AVMEDIA_TYPE_AUDIO: 66 case AVMEDIA_TYPE_AUDIO:
45 type_ = AUDIO; 67 type_ = AUDIO;
46 AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_); 68 AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_);
47 break; 69 break;
48 case AVMEDIA_TYPE_VIDEO: 70 case AVMEDIA_TYPE_VIDEO:
49 type_ = VIDEO; 71 type_ = VIDEO;
50 AVStreamToVideoDecoderConfig(stream, &video_config_); 72 AVStreamToVideoDecoderConfig(stream, &video_config_);
51 break; 73 break;
52 default: 74 default:
53 NOTREACHED(); 75 NOTREACHED();
54 break; 76 break;
55 } 77 }
56 78
57 // Calculate the duration. 79 // Calculate the duration.
58 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration); 80 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration);
81
82 // *** DEBUG Until FFmpeg metadata "enck_key_id" is added for windows. ***
83 //av_dict_set(stream->metadata, "enc_key_id", "DEBUG_KEY_ID_REMOVE", 0);
84 // *** DEBUG Until FFmpeg metadata "enck_key_id" is added for windows. ***
85
86 AVDictionaryEntry *key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0);
ddorwin 2012/08/24 00:20:30 // Only supports WebM for now.
fgalligan1 2012/08/24 20:01:26 Done.
87 if (key) {
88 DCHECK(key->value);
89 base::StringPiece base64_key_id(key->value);
90 std::string enc_key_id;
91 base::Base64Decode(base64_key_id, &enc_key_id);
92 CHECK(!enc_key_id.empty());
93 need_decryption_key_ = true;
94 demuxer_->NeedKey(enc_key_id);
95 current_key_id_.assign(enc_key_id);
96 }
59 } 97 }
60 98
61 bool FFmpegDemuxerStream::HasPendingReads() { 99 bool FFmpegDemuxerStream::HasPendingReads() {
62 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); 100 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread());
63 base::AutoLock auto_lock(lock_); 101 base::AutoLock auto_lock(lock_);
64 DCHECK(!stopped_ || read_queue_.empty()) 102 DCHECK(!stopped_ || read_queue_.empty())
65 << "Read queue should have been emptied if demuxing stream is stopped"; 103 << "Read queue should have been emptied if demuxing stream is stopped";
66 return !read_queue_.empty(); 104 return !read_queue_.empty();
67 } 105 }
68 106
(...skipping 10 matching lines...) Expand all
79 scoped_refptr<DecoderBuffer> buffer; 117 scoped_refptr<DecoderBuffer> buffer;
80 if (!packet.get()) { 118 if (!packet.get()) {
81 buffer = DecoderBuffer::CreateEOSBuffer(); 119 buffer = DecoderBuffer::CreateEOSBuffer();
82 } else { 120 } else {
83 // Convert the packet if there is a bitstream filter. 121 // Convert the packet if there is a bitstream filter.
84 if (packet->data && bitstream_converter_.get() && 122 if (packet->data && bitstream_converter_.get() &&
85 !bitstream_converter_->ConvertPacket(packet.get())) { 123 !bitstream_converter_->ConvertPacket(packet.get())) {
86 LOG(ERROR) << "Format converstion failed."; 124 LOG(ERROR) << "Format converstion failed.";
87 } 125 }
88 126
127 // TODO (fgalligan): Remove the WebM specific code to process an encrypted
128 // buffer. Create a generalized class or function that can be called that
129 // will work for all encrypted formats.
130 // Every encrypted Block has an HMAC and IV prepended to it. Current
131 // encrypted WebM request for comments specification is here
132 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
133 // If encrypted, skip past the HMAC to get the buffer. Encrypted buffers
134 // must include the IV and the encrypted frame because the decryptor will
135 // verify this data before decryption. The HMAC and IV will be copied into
136 // DecryptConfig.
137 int offset = (current_key_id_.empty()) ? 0 : kWebMHmacSize;
138
89 // If a packet is returned by FFmpeg's av_parser_parse2() the packet will 139 // If a packet is returned by FFmpeg's av_parser_parse2() the packet will
90 // reference inner memory of FFmpeg. As such we should transfer the packet 140 // reference inner memory of FFmpeg. As such we should transfer the packet
91 // into memory we control. 141 // into memory we control.
92 buffer = DecoderBuffer::CopyFrom(packet->data, packet->size); 142 buffer = DecoderBuffer::CopyFrom(packet->data + offset,
143 packet->size - offset);
144 if (!current_key_id_.empty()) {
145 uint64 network_iv;
146 memcpy(&network_iv, packet->data + kWebMHmacSize, sizeof(network_iv));
147 const uint64 iv = base::NetToHost64(network_iv);
148
149 scoped_array<uint8> counter_block(GenerateCounterBlock(iv));
150 buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
151 current_key_id_,
152 std::string(reinterpret_cast<const char*>(counter_block.get()),
153 DecryptConfig::kDecryptionKeySize),
154 std::string(reinterpret_cast<const char*>(packet->data),
155 kWebMHmacSize),
156 sizeof(iv),
157 std::vector<SubsampleEntry>())));
158 }
159
93 buffer->SetTimestamp(ConvertStreamTimestamp( 160 buffer->SetTimestamp(ConvertStreamTimestamp(
94 stream_->time_base, packet->pts)); 161 stream_->time_base, packet->pts));
95 buffer->SetDuration(ConvertStreamTimestamp( 162 buffer->SetDuration(ConvertStreamTimestamp(
96 stream_->time_base, packet->duration)); 163 stream_->time_base, packet->duration));
97 if (buffer->GetTimestamp() != kNoTimestamp() && 164 if (buffer->GetTimestamp() != kNoTimestamp() &&
98 last_packet_timestamp_ != kNoTimestamp() && 165 last_packet_timestamp_ != kNoTimestamp() &&
99 last_packet_timestamp_ < buffer->GetTimestamp()) { 166 last_packet_timestamp_ < buffer->GetTimestamp()) {
100 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp()); 167 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp());
101 demuxer_->message_loop()->PostTask(FROM_HERE, base::Bind( 168 demuxer_->message_loop()->PostTask(FROM_HERE, base::Bind(
102 &FFmpegDemuxer::NotifyBufferingChanged, demuxer_)); 169 &FFmpegDemuxer::NotifyBufferingChanged, demuxer_));
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 read_cb.Run(DemuxerStream::kOk, 245 read_cb.Run(DemuxerStream::kOk,
179 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); 246 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
180 return; 247 return;
181 } 248 }
182 249
183 // Enqueue the callback and attempt to satisfy it immediately. 250 // Enqueue the callback and attempt to satisfy it immediately.
184 read_queue_.push_back(read_cb); 251 read_queue_.push_back(read_cb);
185 FulfillPendingRead(); 252 FulfillPendingRead();
186 253
187 // Check if there are still pending reads, demux some more. 254 // Check if there are still pending reads, demux some more.
188 if (!read_queue_.empty()) { 255 if (!read_queue_.empty() && !need_decryption_key_) {
189 demuxer_->PostDemuxTask(); 256 demuxer_->PostDemuxTask();
190 } 257 }
191 } 258 }
192 259
193 void FFmpegDemuxerStream::FulfillPendingRead() { 260 void FFmpegDemuxerStream::FulfillPendingRead() {
194 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); 261 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread());
195 lock_.AssertAcquired(); 262 lock_.AssertAcquired();
196 if (buffer_queue_.empty() || read_queue_.empty()) { 263 if (buffer_queue_.empty() || read_queue_.empty()) {
197 return; 264 return;
198 } 265 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 base::AutoLock auto_lock(lock_); 298 base::AutoLock auto_lock(lock_);
232 DCHECK(stopped_); 299 DCHECK(stopped_);
233 DCHECK(read_queue_.empty()); 300 DCHECK(read_queue_.empty());
234 DCHECK(buffer_queue_.empty()); 301 DCHECK(buffer_queue_.empty());
235 } 302 }
236 303
237 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { 304 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const {
238 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); 305 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts);
239 } 306 }
240 307
308 void FFmpegDemuxerStream::KeyAdded() {
309 need_decryption_key_ = false;
310 }
311
241 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { 312 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
242 base::AutoLock auto_lock(lock_); 313 base::AutoLock auto_lock(lock_);
243 return buffered_ranges_; 314 return buffered_ranges_;
244 } 315 }
245 316
246 // static 317 // static
247 base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp( 318 base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp(
248 const AVRational& time_base, int64 timestamp) { 319 const AVRational& time_base, int64 timestamp) {
249 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) 320 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE))
250 return kNoTimestamp(); 321 return kNoTimestamp();
251 322
252 return ConvertFromTimeBase(time_base, timestamp); 323 return ConvertFromTimeBase(time_base, timestamp);
253 } 324 }
254 325
255 // 326 //
256 // FFmpegDemuxer 327 // FFmpegDemuxer
257 // 328 //
258 FFmpegDemuxer::FFmpegDemuxer( 329 FFmpegDemuxer::FFmpegDemuxer(
259 const scoped_refptr<base::MessageLoopProxy>& message_loop, 330 const scoped_refptr<base::MessageLoopProxy>& message_loop,
260 const scoped_refptr<DataSource>& data_source) 331 const scoped_refptr<DataSource>& data_source,
332 const FFmpegNeedKeyCB& need_key_cb)
261 : host_(NULL), 333 : host_(NULL),
262 message_loop_(message_loop), 334 message_loop_(message_loop),
263 format_context_(NULL), 335 format_context_(NULL),
264 data_source_(data_source), 336 data_source_(data_source),
265 read_event_(false, false), 337 read_event_(false, false),
266 read_has_failed_(false), 338 read_has_failed_(false),
267 last_read_bytes_(0), 339 last_read_bytes_(0),
268 read_position_(0), 340 read_position_(0),
269 bitrate_(0), 341 bitrate_(0),
270 start_time_(kNoTimestamp()), 342 start_time_(kNoTimestamp()),
271 audio_disabled_(false), 343 audio_disabled_(false),
272 duration_known_(false) { 344 duration_known_(false),
345 need_key_cb_(need_key_cb) {
273 DCHECK(message_loop_); 346 DCHECK(message_loop_);
274 DCHECK(data_source_); 347 DCHECK(data_source_);
275 } 348 }
276 349
277 FFmpegDemuxer::~FFmpegDemuxer() { 350 FFmpegDemuxer::~FFmpegDemuxer() {
278 // In this destructor, we clean up resources held by FFmpeg. It is ugly to 351 // 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 352 // close the codec contexts here because the corresponding codecs are opened
280 // in the decoder filters. By reaching this point, all filters should have 353 // 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. 354 // stopped, so this is the only safe place to do the global clean up.
282 // TODO(hclam): close the codecs in the corresponding decoders. 355 // TODO(hclam): close the codecs in the corresponding decoders.
(...skipping 26 matching lines...) Expand all
309 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { 382 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) {
310 DCHECK(data_source_.get()); 383 DCHECK(data_source_.get());
311 data_source_->SetPlaybackRate(playback_rate); 384 data_source_->SetPlaybackRate(playback_rate);
312 } 385 }
313 386
314 void FFmpegDemuxer::OnAudioRendererDisabled() { 387 void FFmpegDemuxer::OnAudioRendererDisabled() {
315 message_loop_->PostTask(FROM_HERE, base::Bind( 388 message_loop_->PostTask(FROM_HERE, base::Bind(
316 &FFmpegDemuxer::DisableAudioStreamTask, this)); 389 &FFmpegDemuxer::DisableAudioStreamTask, this));
317 } 390 }
318 391
392 void FFmpegDemuxer::KeyAdded() {
393 message_loop_->PostTask(FROM_HERE,
394 base::Bind(&FFmpegDemuxer::KeyAddedTask, this));
395 }
396
397 void FFmpegDemuxer::NeedKey(const std::string& key_id) {
398 int key_id_size = key_id.size();
399 scoped_array<uint8> key_id_local(new uint8[key_id_size]);
400 memcpy(key_id_local.get(), key_id.data(), key_id_size);
401 need_key_cb_.Run(key_id_local.Pass(), key_id_size);
402 }
403
319 void FFmpegDemuxer::Initialize(DemuxerHost* host, 404 void FFmpegDemuxer::Initialize(DemuxerHost* host,
320 const PipelineStatusCB& status_cb) { 405 const PipelineStatusCB& status_cb) {
321 message_loop_->PostTask(FROM_HERE, base::Bind( 406 message_loop_->PostTask(FROM_HERE, base::Bind(
322 &FFmpegDemuxer::InitializeTask, this, host, status_cb)); 407 &FFmpegDemuxer::InitializeTask, this, host, status_cb));
323 } 408 }
324 409
325 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( 410 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(
326 DemuxerStream::Type type) { 411 DemuxerStream::Type type) {
327 return GetFFmpegStream(type); 412 return GetFFmpegStream(type);
328 } 413 }
(...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after
673 DCHECK(message_loop_->BelongsToCurrentThread()); 758 DCHECK(message_loop_->BelongsToCurrentThread());
674 audio_disabled_ = true; 759 audio_disabled_ = true;
675 StreamVector::iterator iter; 760 StreamVector::iterator iter;
676 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 761 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
677 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { 762 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
678 (*iter)->Stop(); 763 (*iter)->Stop();
679 } 764 }
680 } 765 }
681 } 766 }
682 767
768 void FFmpegDemuxer::KeyAddedTask() {
769 DCHECK(message_loop_->BelongsToCurrentThread());
770
771 StreamVector::iterator iter;
772 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
773 if (*iter && (*iter)->type() == DemuxerStream::VIDEO) {
774 (*iter)->KeyAdded();
775 }
776 }
777
778 PostDemuxTask();
779 }
780
683 bool FFmpegDemuxer::StreamsHavePendingReads() { 781 bool FFmpegDemuxer::StreamsHavePendingReads() {
684 DCHECK(message_loop_->BelongsToCurrentThread()); 782 DCHECK(message_loop_->BelongsToCurrentThread());
685 StreamVector::iterator iter; 783 StreamVector::iterator iter;
686 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 784 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
687 if (*iter && (*iter)->HasPendingReads()) { 785 if (*iter && (*iter)->HasPendingReads()) {
688 return true; 786 return true;
689 } 787 }
690 } 788 }
691 return false; 789 return false;
692 } 790 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
727 } else if (audio) { 825 } else if (audio) {
728 buffered = audio->GetBufferedRanges(); 826 buffered = audio->GetBufferedRanges();
729 } else if (video) { 827 } else if (video) {
730 buffered = video->GetBufferedRanges(); 828 buffered = video->GetBufferedRanges();
731 } 829 }
732 for (size_t i = 0; i < buffered.size(); ++i) 830 for (size_t i = 0; i < buffered.size(); ++i)
733 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); 831 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i));
734 } 832 }
735 833
736 } // namespace media 834 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698