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

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

Issue 7203002: Adding ChunkDemuxer implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More cleanup & commenting Created 9 years, 6 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
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Implements a Demuxer that can switch among different data sources mid-stream.
6 // Uses FFmpegDemuxer under the covers, so see the caveats at the top of
7 // ffmpeg_demuxer.h.
8
9 #include "media/filters/chunk_demuxer.h"
10
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/message_loop.h"
14 #include "media/base/filter_host.h"
15 #include "media/ffmpeg/ffmpeg_common.h"
16 #include "media/filters/ffmpeg_glue.h"
17
18 namespace media {
19
20 // WebM File Header. This is prepended to the INFO & TRACKS
21 // data passed to Init() before handing it to FFmpeg. Essentially
22 // we are making the INFO & TRACKS data look like a small WebM
23 // file so we can use FFmpeg to initialize the AVFormatContext.
24 static const uint8 kWebMHeader[] = {
25 0x1A, 0x45, 0xDF, 0xA3, 0x9F, // EBML (size = 0x1f)
26 0x42, 0x86, 0x81, 0x01, // EBMLVersion = 1
27 0x42, 0xF7, 0x81, 0x01, // EBMLReadVersion = 1
28 0x42, 0xF2, 0x81, 0x04, // EBMLMaxIDLength = 4
29 0x42, 0xF3, 0x81, 0x08, // EBMLMaxSizeLength = 8
30 0x42, 0x82, 0x84, 0x77, 0x65, 0x62, 0x6D, // DocType = "webm"
31 0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2
32 0x42, 0x85, 0x81, 0x02, // DocTypeReadVersion = 2
33 // EBML end
34 0x18, 0x53, 0x80, 0x67, // Segment
35 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment(size = 0)
36 // INFO goes here.
37 };
38
39 // Offset of the segment size field in kWebMHeader. Used to update
40 // the segment size field before handing the buffer to FFmpeg.
41 static const int kSegmentSizeOffset = sizeof(kWebMHeader) - 8;
42
43 static const uint8 kEmptyCluster[] = {
44 0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0)
45 };
46
47 MediaDataSink::MediaDataSink(const scoped_refptr<ChunkDemuxer>& demuxer)
48 : demuxer_(demuxer) {
49 }
50
51 MediaDataSink::~MediaDataSink() {}
52
53 void MediaDataSink::Flush() {
54 demuxer_->FlushData();
55 }
56
57 bool MediaDataSink::AddData(const uint8* data, unsigned length) {
58 return demuxer_->AddData(data, length);
59 }
60
61 void MediaDataSink::Shutdown() {
62 demuxer_->Shutdown();
63 }
64
65 class ChunkDemuxerStream : public DemuxerStream {
66 public:
67 typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
68 typedef std::deque<ReadCallback> ReadCBQueue;
69
70 ChunkDemuxerStream(Type type, AVStream* stream);
71 virtual ~ChunkDemuxerStream();
72
73 void Flush();
74 void AddBuffers(const BufferQueue& buffers);
75 void Shutdown();
76
77 bool GetLastBufferTimestamp(base::TimeDelta* timestamp) const;
78
79 // DemuxerStream methods.
80 virtual void Read(const ReadCallback& read_callback);
81 virtual Type type();
82 virtual const MediaFormat& media_format();
83 virtual void EnableBitstreamConverter();
84 virtual AVStream* GetAVStream();
85
86 private:
87 static void RunCallback(ReadCallback cb, scoped_refptr<Buffer> buffer);
88
89 Type type_;
90 MediaFormat media_format_;
91 AVStream* av_stream_;
92
93 mutable base::Lock lock_;
94 ReadCBQueue read_cbs_;
95 BufferQueue buffers_;
96 bool shutdown_called_;
97
98 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
99 };
100
101 // Helper class used to parse WebM clusters.
102 class ClusterWebMParserClient : public WebMParserClient {
103 public:
104 ClusterWebMParserClient(int64 timecode_scale,
105 int audio_track_num,
106 int audio_default_duration,
107 int video_track_num,
108 int video_default_duration)
109 : timecode_multiplier_(timecode_scale / 1000.0), // will convert timecodes
110 // to microseconds.
111 audio_track_num_(audio_track_num),
112 audio_default_duration_(audio_default_duration),
113 video_track_num_(video_track_num),
114 video_default_duration_(video_default_duration),
115 cluster_timecode_(-1) {
116 VLOG(1) << "tc_s " << timecode_multiplier_
117 << " atn " << audio_track_num_
118 << " add " << audio_default_duration_
119 << " vtn " << video_track_num_
120 << " vdd " << video_default_duration_;
121 }
122
123 const ChunkDemuxerStream::BufferQueue& audio_buffers() const {
124 return audio_buffers_;
125 };
126
127 const ChunkDemuxerStream::BufferQueue& video_buffers() const {
128 return video_buffers_;
129 };
130
131 virtual bool OnListStart(int id) {
132 if (id == kWebMIdCluster)
133 cluster_timecode_ = -1;
134
135 return true;
136 }
137
138 virtual bool OnListEnd(int id) {
139 if (id == kWebMIdCluster)
140 cluster_timecode_ = -1;
141
142 return true;
143 }
144
145 virtual bool OnUInt(int id, int64 val) {
146 if (id == kWebMIdTimecode) {
147 if (cluster_timecode_ != -1)
148 return false;
149
150 cluster_timecode_ = val;
151 }
152
153 return true;
154 }
155
156 virtual bool OnFloat(int id, double val) {
157 VLOG(1) << "Unexpected float element with ID " << std::hex << id;
158 return false;
159 }
160
161 virtual bool OnBinary(int id, const uint8* data, int size) {
162 VLOG(1) << "Unexpected binary element with ID " << std::hex << id;
163 return false;
164 }
165
166 virtual bool OnString(int id, const std::string& str) {
167 VLOG(1) << "Unexpected string element with ID " << std::hex << id;
168 return false;
169 }
170
171 virtual bool OnSimpleBlock(int track_num, int timecode,
172 int flags,
173 const uint8* data, int size) {
174 if (cluster_timecode_ == -1) {
175 VLOG(1) << "Got SimpleBlock before cluster timecode.";
176 return false;
177 }
178
179 //VLOG(1) << "SimpleBlock : tn " << track_num
180 // << " tc " << (cluster_timecode_ + timecode)
181 // << "(" << timecode << ")"
182 // << " flags " << std::hex << flags << std::dec
183 // << " size " << size;
184
185 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
186 (cluster_timecode_ + timecode) * timecode_multiplier_);
187
188 if (track_num == audio_track_num_) {
189 base::TimeDelta audio_duration = base::TimeDelta::FromMicroseconds(
190 audio_default_duration_ / 1000.0);
191
192 audio_buffers_.push_back(new BufferImpl(timestamp, audio_duration,
193 data, size));
194 return true;
195 }
196
197 if (track_num == video_track_num_) {
198 base::TimeDelta video_duration = base::TimeDelta::FromMicroseconds(
199 video_default_duration_ / 1000.0);
200
201 video_buffers_.push_back(new BufferImpl(timestamp, video_duration,
202 data, size));
203 return true;
204 }
205
206 VLOG(1) << "Unexpected track number " << track_num;
207 return false;
208 }
209
210 private:
211 double timecode_multiplier_;
212 int audio_track_num_;
213 int audio_default_duration_;
214 int video_track_num_;
215 int video_default_duration_;
216
217 int64 cluster_timecode_;
218
219 ChunkDemuxerStream::BufferQueue audio_buffers_;
220 ChunkDemuxerStream::BufferQueue video_buffers_;
221 };
222
223 ChunkDemuxerStream::ChunkDemuxerStream(Type type, AVStream* stream)
224 : type_(type),
225 av_stream_(stream),
226 shutdown_called_(false) {
227 }
228
229 ChunkDemuxerStream::~ChunkDemuxerStream() {}
230
231 void ChunkDemuxerStream::Flush() {
232 VLOG(1) << "Flush()";
233 base::AutoLock auto_lock(lock_);
234 buffers_.clear();
235 }
236
237 void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) {
238 std::deque<base::Closure> callbacks;
239 {
240 base::AutoLock auto_lock(lock_);
241
242 for (BufferQueue::const_iterator itr = buffers.begin();
243 itr != buffers.end(); itr++) {
244 buffers_.push_back(*itr);
245 }
246
247 while (!buffers_.empty() && !read_cbs_.empty()) {
248 callbacks.push_back(base::Bind(&ChunkDemuxerStream::RunCallback,
249 read_cbs_.front(),
250 buffers_.front()));
251 buffers_.pop_front();
252 read_cbs_.pop_front();
253 }
254
255 if (!buffers_.empty()) {
256 base::TimeDelta fts = buffers_.front()->GetTimestamp();
257 base::TimeDelta bts = buffers_.back()->GetTimestamp();
258 base::TimeDelta dlt = bts - fts;
259 VLOG(1) << "AddBuffers() : ct " << buffers_.size()
260 << " fts " << fts.InSecondsF()
261 << " bts " << bts.InSecondsF()
262 << " dlt " << dlt.InSecondsF();
263 }
264 }
265
266 while (!callbacks.empty()) {
267 callbacks.front().Run();
268 callbacks.pop_front();
269 }
270 }
271
272 void ChunkDemuxerStream::Shutdown() {
273 std::deque<ReadCallback> callbacks;
274 {
275 base::AutoLock auto_lock(lock_);
276 shutdown_called_ = true;
277
278 // Collect all the pending Read() callbacks.
279 while (!read_cbs_.empty()) {
280 callbacks.push_back(read_cbs_.front());
281 read_cbs_.pop_front();
282 }
283 }
284
285 // Pass NULL to all callbacks to signify read failure.
286 while (!callbacks.empty()) {
287 callbacks.front().Run(NULL);
288 callbacks.pop_front();
289 }
290 }
291
292 bool ChunkDemuxerStream::GetLastBufferTimestamp(
293 base::TimeDelta* timestamp) const {
294 base::AutoLock auto_lock(lock_);
295
296 if (buffers_.empty())
297 return false;
298
299 *timestamp = buffers_.back()->GetTimestamp();
300 return true;
301 }
302
303 // Helper function used to make Closures for ReadCallbacks.
304 //static
305 void ChunkDemuxerStream::RunCallback(ReadCallback cb,
306 scoped_refptr<Buffer> buffer) {
307 cb.Run(buffer);
308 }
309
310 // Helper function that makes sure |read_callback| runs on |message_loop|.
311 static void RunOnMessageLoop(const DemuxerStream::ReadCallback& read_callback,
312 MessageLoop* message_loop,
313 scoped_refptr<Buffer> buffer) {
314 if (MessageLoop::current() != message_loop) {
315 message_loop->PostTask(FROM_HERE,
316 NewRunnableFunction(&RunOnMessageLoop,
317 read_callback,
318 message_loop,
319 buffer));
320 return;
321 }
322
323 read_callback.Run(buffer);
324 }
325
326 // DemuxerStream methods.
327 void ChunkDemuxerStream::Read(const ReadCallback& read_callback) {
328
329 scoped_refptr<Buffer> buffer;
330
331 {
332 base::AutoLock auto_lock(lock_);
333
334 if (!shutdown_called_) {
335 if (buffers_.empty()) {
336 // Wrap & store |read_callback| so that it will
337 // get called on the current MessageLoop.
338 read_cbs_.push_back(base::Bind(&RunOnMessageLoop,
339 read_callback,
340 MessageLoop::current()));
341 return;
342 }
343
344 if (!read_cbs_.empty()) {
345 // Wrap & store |read_callback| so that it will
346 // get called on the current MessageLoop.
347 read_cbs_.push_back(base::Bind(&RunOnMessageLoop,
348 read_callback,
349 MessageLoop::current()));
350 return;
351 }
352
353 buffer = buffers_.front();
354 buffers_.pop_front();
355 }
356 }
357
358 read_callback.Run(buffer);
359 }
360
361 DemuxerStream::Type ChunkDemuxerStream::type() { return type_; }
362
363 const MediaFormat& ChunkDemuxerStream::media_format() { return media_format_; }
364
365 void ChunkDemuxerStream::EnableBitstreamConverter() {}
366
367 AVStream* ChunkDemuxerStream::GetAVStream() { return av_stream_; }
368
369 ChunkDemuxer::ChunkDemuxer()
370 : state_(WAITING_FOR_INIT),
371 format_context_(NULL),
372 buffered_bytes_(0),
373 first_seek_(true) {
374 }
375
376 ChunkDemuxer::~ChunkDemuxer() {
377 DCHECK_NE(state_, INITIALIZED);
378
379 DestroyAVFormatContext(format_context_);
380 format_context_ = NULL;
381 }
382
383 bool ChunkDemuxer::Init(const uint8* data, int size) {
384 DCHECK(data);
385 DCHECK_GT(size, 0);
386 VLOG(1) << "Init(" << size << ")";
387
388 base::AutoLock auto_lock(lock_);
389 DCHECK_EQ(state_, WAITING_FOR_INIT);
390
391 int res = webm_parse_headers(&info_, data, size);
392
393 if (res < 0) {
394 ChangeState(INIT_ERROR);
395 return false;
396 }
397
398 format_context_ = CreateFormatContext(data, size);
399
400 if (!format_context_ || !SetupStreams() || !ParsePendingBuffers()) {
401 ChangeState(INIT_ERROR);
402 return false;
403 }
404
405 ChangeState(INITIALIZED);
406 return true;
407 }
408
409 // Filter implementation.
410 void ChunkDemuxer::set_host(FilterHost* filter_host) {
411 Demuxer::set_host(filter_host);
412 double mult = info_.timecode_scale() / 1000.0;
413
414 base::TimeDelta duration =
415 base::TimeDelta::FromMicroseconds(info_.duration() * mult);
416
417 filter_host->SetDuration(duration);
418 filter_host->SetCurrentReadPosition(0);
419 }
420
421 void ChunkDemuxer::Stop(FilterCallback* callback) {
422 VLOG(1) << "Stop()";
423
424 callback->Run();
425 delete callback;
426 }
427
428 void ChunkDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
429 VLOG(1) << "Seek(" << time.InSecondsF() << ")";
430
431 bool run_callback = false;
432 {
433 base::AutoLock auto_lock(lock_);
434
435 if (first_seek_) {
436 first_seek_ = false;
437 run_callback = true;
438 } else {
439 seek_cb_ = cb;
440 seek_time_ = time;
441 }
442 }
443
444 if (run_callback)
445 cb.Run(PIPELINE_OK);
446 }
447
448 void ChunkDemuxer::OnAudioRendererDisabled() {
449 base::AutoLock auto_lock(lock_);
450 audio_ = NULL;
451 }
452
453 void ChunkDemuxer::SetPreload(Preload preload) {}
454
455 // Demuxer implementation.
456 scoped_refptr<DemuxerStream> ChunkDemuxer::GetStream(
457 DemuxerStream::Type type) {
458 VLOG(1) << "GetStream(" << type << ")";
459
460 if (type == DemuxerStream::VIDEO)
461 return video_;
462
463 if (type == DemuxerStream::AUDIO)
464 return audio_;
465
466 return NULL;
467 }
468
469 base::TimeDelta ChunkDemuxer::GetStartTime() const {
470 VLOG(1) << "GetStartTime()";
471 // TODO(acolwell) : Fix this so it uses the time on the first packet.
472 return base::TimeDelta();
473 }
474
475 void ChunkDemuxer::FlushData() {
476 base::AutoLock auto_lock(lock_);
477 if (audio_.get())
478 audio_->Flush();
479
480 if (video_.get())
481 video_->Flush();
482
483 pending_buffers_.clear();
484 }
485
486 bool ChunkDemuxer::AddData(const uint8* data, unsigned length) {
487 VLOG(1) << "AddData(" << length << ")";
488
489 int64 buffered_bytes = 0;
490 base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1);
491
492 FilterStatusCB cb;
493 {
494 base::AutoLock auto_lock(lock_);
495
496 switch(state_) {
497 case WAITING_FOR_INIT:
498 pending_buffers_.push_back(new media::BufferImpl(base::TimeDelta(),
499 base::TimeDelta(),
500 data,
501 length));
502 return false;
503 break;
504
505 case INITIALIZED:
506 if (!ParseAndAddData_Locked(data, length)) {
507 VLOG(1) << "AddData(): parsing data failed";
508 return false;
509 }
510 break;
511
512 case INIT_ERROR:
513 case SHUTDOWN:
514 VLOG(1) << "AddData(): called in unexpected state " << state_;
515 return false;
516 break;
517 }
518
519 base::TimeDelta tmp;
520 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) &&
521 tmp > buffered_ts) {
522 buffered_ts = tmp;
523 }
524
525 if (video_.get() && video_->GetLastBufferTimestamp(&tmp) &&
526 tmp > buffered_ts) {
527 buffered_ts = tmp;
528 }
529
530 buffered_bytes = buffered_bytes_;
531
532 if (!seek_cb_.is_null())
533 std::swap(cb, seek_cb_);
534 }
535
536 // Notify the host of 'network activity' because we got data.
537 if (host()) {
538 host()->SetBufferedBytes(buffered_bytes);
539
540 if (buffered_ts.InSeconds() >= 0) {
541 host()->SetBufferedTime(buffered_ts);
542 }
543
544 host()->SetNetworkActivity(true);
545 }
546
547 if (!cb.is_null())
548 cb.Run(PIPELINE_OK);
549
550 return true;
551 }
552
553 void ChunkDemuxer::Shutdown() {
554 FilterStatusCB cb;
555 {
556 base::AutoLock auto_lock(lock_);
557
558 std::swap(cb, seek_cb_);
559
560 if (audio_.get())
561 audio_->Shutdown();
562
563 if (video_.get())
564 video_->Shutdown();
565
566 ChangeState(SHUTDOWN);
567 }
568
569 if (!cb.is_null())
570 cb.Run(PIPELINE_OK);
571 }
572
573 void ChunkDemuxer::ChangeState(State new_state) {
574 state_ = new_state;
575 }
576
577 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data,
578 int size) const {
579 int segment_size = size + sizeof(kEmptyCluster);
580 int buf_size = sizeof(kWebMHeader) + segment_size;
581 uint8* buf = new uint8[buf_size];
scherkus (not reviewing) 2011/06/22 17:31:09 scoped_ptr seems nice here considering this isn't
acolwell GONE FROM CHROMIUM 2011/06/23 16:51:28 Done. Changed this to scoped_array<uint8>
582 memcpy(buf, kWebMHeader, sizeof(kWebMHeader));
583 memcpy(buf + sizeof(kWebMHeader), data, size);
584 memcpy(buf + sizeof(kWebMHeader) + size, kEmptyCluster,
585 sizeof(kEmptyCluster));
586
587 // Update the segment size in the buffer.
588 int64 tmp = (segment_size & GG_LONGLONG(0x00FFFFFFFFFFFFFF)) |
589 GG_LONGLONG(0x0100000000000000);
590 for (int i = 0; i < 8; i++) {
591 buf[kSegmentSizeOffset + i] = (tmp >> (8 * (7 - i))) & 0xff;
592 }
593
594 BufferUrlProtocol bup(buf, buf_size, true);
595 std::string key = FFmpegGlue::GetInstance()->AddProtocol(&bup);
596
597 // Open FFmpeg AVFormatContext.
598 AVFormatContext* context = NULL;
599 int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL);
600
601 // Remove ourself from protocol list.
602 FFmpegGlue::GetInstance()->RemoveProtocol(&bup);
603
604 delete buf;
605
606 if (result < 0)
607 return NULL;
608
609 return context;
610 }
611
612 bool ChunkDemuxer::SetupStreams() {
613 int result = av_find_stream_info(format_context_);
614
615 if (result < 0)
616 return false;
617
618 bool no_supported_streams = true;
619 for (size_t i = 0; i < format_context_->nb_streams; ++i) {
620 AVStream* stream = format_context_->streams[i];
621 AVCodecContext* codec_context = stream->codec;
622 CodecType codec_type = codec_context->codec_type;
623
624 if (codec_type == CODEC_TYPE_AUDIO &&
625 stream->codec->codec_id == CODEC_ID_VORBIS &&
626 !audio_.get()) {
627 audio_ = new ChunkDemuxerStream(DemuxerStream::AUDIO, stream);
628 no_supported_streams = false;
629 continue;
630 }
631
632 if (codec_type == CODEC_TYPE_VIDEO &&
633 stream->codec->codec_id == CODEC_ID_VP8 &&
634 !video_.get()) {
635 video_ = new ChunkDemuxerStream(DemuxerStream::VIDEO, stream);
636 no_supported_streams = false;
637 continue;
638 }
639 }
640
641 return !no_supported_streams;
642 }
643
644 bool ChunkDemuxer::ParsePendingBuffers() {
645 // Handle any buffers that came in between the time the pipeline was
646 // started and Init() was called.
647 while(!pending_buffers_.empty()) {
648 scoped_refptr<media::Buffer> buf = pending_buffers_.front();
649 pending_buffers_.pop_front();
650
651 if (!ParseAndAddData_Locked(buf->GetData(), buf->GetDataSize())) {
652 pending_buffers_.clear();
653 ChangeState(INIT_ERROR);
654 return false;
655 }
656 }
657
658 return true;
659 }
660
661 bool ChunkDemuxer::ParseAndAddData_Locked(const uint8* data, int length) {
662 ClusterWebMParserClient client(info_.timecode_scale(),
663 info_.audio_track_num(),
664 info_.audio_default_duration(),
665 info_.video_track_num(),
666 info_.video_default_duration());
667 int res = webm_parse_cluster(&client, data, length);
668
669 if (res <= 0)
670 return false;
671
672 if (audio_.get())
673 audio_->AddBuffers(client.audio_buffers());
674
675 if (video_.get())
676 video_->AddBuffers(client.video_buffers());
677
678 // TODO(acolwell) : make this more representative of what is actually
679 // buffered.
680 buffered_bytes_ += length;
681
682 return true;
683 }
684
685 ChunkDemuxer::InfoTrackWebMParserClient::InfoTrackWebMParserClient()
686 : timecode_scale_(-1),
687 duration_(-1),
688 track_type_(-1),
689 track_num_(-1),
690 track_default_duration_(-1),
691 audio_track_num_(-1),
692 audio_default_duration_(-1),
693 video_track_num_(-1),
694 video_default_duration_(-1) {
695 }
696
697 int64 ChunkDemuxer::InfoTrackWebMParserClient::timecode_scale() const {
698 return timecode_scale_;
699 }
700
701 double ChunkDemuxer::InfoTrackWebMParserClient::duration() const {
702 return duration_;
703 }
704
705 int64 ChunkDemuxer::InfoTrackWebMParserClient::audio_track_num() const {
706 return audio_track_num_;
707 }
708
709 int64 ChunkDemuxer::InfoTrackWebMParserClient::audio_default_duration() const {
710 return audio_default_duration_;
711 }
712
713 int64 ChunkDemuxer::InfoTrackWebMParserClient::video_track_num() const {
714 return video_track_num_;
715 }
716
717 int64 ChunkDemuxer::InfoTrackWebMParserClient::video_default_duration() const {
718 return video_default_duration_;
719 }
720
721 bool ChunkDemuxer::InfoTrackWebMParserClient::OnListStart(int id) {
722 if (id == kWebMIdTrackEntry) {
723 track_type_ = -1;
724 track_num_ = -1;
725 }
726
727 return true;
728 }
729
730 bool ChunkDemuxer::InfoTrackWebMParserClient::OnListEnd(int id) {
731 if (id == kWebMIdTrackEntry) {
732 if (track_type_ == -1 || track_num_ == -1) {
733 VLOG(1) << "Missing TrackEntry data"
734 << " TrackType " << track_type_
735 << " TrackNum " << track_num_;
736 return false;
737 }
738
739 if (track_type_ == 1) {
740 video_track_num_ = track_num_;
741 video_default_duration_ = track_default_duration_;
742 } else if (track_type_ == 2) {
743 audio_track_num_ = track_num_;
744 audio_default_duration_ = track_default_duration_;
745 } else {
746 VLOG(1) << "Unexpected TrackType " << track_type_;
747 return false;
748 }
749
750 track_type_ = -1;
751 track_num_ = -1;
752 } else if (id == kWebMIdInfo && timecode_scale_ == -1) {
753 // Set timecode scale to default value if it isn't present in
754 // the Info element.
755 timecode_scale_ = 1000000;
756 }
757 return true;
758 }
759
760 bool ChunkDemuxer::InfoTrackWebMParserClient::OnUInt(int id, int64 val) {
761 int64* dst = NULL;
762
763 switch (id) {
764 case kWebMIdTimecodeScale:
765 dst = &timecode_scale_;
766 break;
767 case kWebMIdTrackNumber:
768 dst = &track_num_;
769 break;
770 case kWebMIdTrackType:
771 dst = &track_type_;
772 break;
773 case kWebMIdDefaultDuration:
774 dst = &track_default_duration_;
775 break;
776 default:
777 return true;
778 }
779
780 if (*dst != -1) {
781 VLOG(1) << "Multiple values for id " << std::hex << id << " specified";
782 return false;
783 }
784
785 *dst = val;
786 return true;
787 }
788
789 bool ChunkDemuxer::InfoTrackWebMParserClient::OnFloat(int id, double val) {
790 if (id != kWebMIdDuration) {
791 VLOG(1) << "Unexpected float for id" << std::hex << id;
792 return false;
793 }
794
795 if (duration_ != -1) {
796 VLOG(1) << "Multiple values for duration.";
797 return false;
798 }
799
800 duration_ = val;
801 return true;
802 }
803
804 bool ChunkDemuxer::InfoTrackWebMParserClient::OnBinary(int id, const
805 uint8* data,
806 int size) {
807 return true;
808 }
809
810 bool ChunkDemuxer::InfoTrackWebMParserClient::OnString(int id,
811 const std::string& str) {
812 if (id != kWebMIdCodecID)
813 return false;
814
815 if (str != "A_VORBIS" && str != "V_VP8") {
816 VLOG(1) << "Unexpected CodecID " << str;
817 return false;
818 }
819
820 return true;
821 }
822
823 bool ChunkDemuxer::InfoTrackWebMParserClient::OnSimpleBlock(
824 int track_num, int timecode, int flags, const uint8* data, int size) {
825 return false;
826 }
827
828 const char ChunkDemuxerFactory::kURLPrefix[] = "x-media-chunks:";
829
830 class ChunkDemuxerFactory::BuildState
831 : public base::RefCountedThreadSafe<BuildState> {
832 public:
833 static const int64 kMaxInfoSize = 32678;
834
835 BuildState(const std::string& url, BuildCallback* cb,
836 const scoped_refptr<ChunkDemuxer>& demuxer)
837 : url_(url),
838 cb_(cb),
839 demuxer_(demuxer),
840 read_buffer_(NULL),
841 read_size_(0),
842 message_loop_(MessageLoop::current()) {
843 AddRef();
844 }
845
846 ~BuildState() { delete read_buffer_; }
847
848 void BuildDone(PipelineStatus status, DataSource* data_source) {
849 if (message_loop_ != MessageLoop::current()) {
850 message_loop_->PostTask(
851 FROM_HERE,
852 NewRunnableMethod(this,
853 &BuildState::BuildDone,
854 status,
855 scoped_refptr<DataSource>(data_source)));
856 return;
857 }
858
859 if (status != PIPELINE_OK) {
860 cb_->Run(status, static_cast<Demuxer*>(NULL));
861 Release();
862 return;
863 }
864
865 data_source_ = data_source;
866
867 int64 size = 0;
868
869 if (!data_source_->GetSize(&size) || size >= kMaxInfoSize) {
870 cb_->Run(DEMUXER_ERROR_COULD_NOT_OPEN,
871 static_cast<Demuxer*>(NULL));
872 data_source_->Stop(NewCallback(this, &BuildState::StopDone));
873 return;
874 }
875
876 DCHECK(!read_buffer_);
877 read_size_ = size;
878 read_buffer_ = new uint8[read_size_];
879 data_source_->Read(0, read_size_, read_buffer_,
880 NewCallback(this, &BuildState::ReadDone));
881 }
882
883 void ReadDone(size_t size) {
884 if (message_loop_ != MessageLoop::current()) {
885 message_loop_->PostTask(FROM_HERE,
886 NewRunnableMethod(this,
887 &BuildState::ReadDone,
888 size));
889 return;
890 }
891
892 if (size == DataSource::kReadError) {
893 cb_->Run(PIPELINE_ERROR_READ, static_cast<Demuxer*>(NULL));
894 data_source_->Stop(NewCallback(this, &BuildState::StopDone));
895 return;
896 }
897
898 if (demuxer_->Init(read_buffer_, size)) {
899 cb_->Run(PIPELINE_OK, demuxer_.get());
900 } else {
901 cb_->Run(DEMUXER_ERROR_COULD_NOT_OPEN, static_cast<Demuxer*>(NULL));
902 }
903
904 data_source_->Stop(NewCallback(this, &BuildState::StopDone));
905 }
906
907 void StopDone() { Release(); }
908
909 private:
910 std::string url_;
911 scoped_ptr<BuildCallback> cb_;
912 scoped_refptr<ChunkDemuxer> demuxer_;
913
914 scoped_refptr<DataSource> data_source_;
915 uint8* read_buffer_;
916 int read_size_;
917 MessageLoop* message_loop_;
918 };
919
920 ChunkDemuxerFactory::ChunkDemuxerFactory(
921 DataSourceFactory* data_source_factory)
922 : data_source_factory_(data_source_factory) {
923 }
924
925 ChunkDemuxerFactory::~ChunkDemuxerFactory() {}
926
927 bool ChunkDemuxerFactory::IsUrlSupported(const std::string& url) const {
928 return (url.find(kURLPrefix) == 0);
929 }
930
931 MediaDataSink* ChunkDemuxerFactory::CreateMediaDataSink() {
932 demuxer_ = new ChunkDemuxer();
933 return new MediaDataSink(demuxer_);
934 }
935
936 void ChunkDemuxerFactory::Build(const std::string& url, BuildCallback* cb) {
937 if (!IsUrlSupported(url) || !demuxer_.get()) {
938 cb->Run(DEMUXER_ERROR_COULD_NOT_OPEN, static_cast<Demuxer*>(NULL));
939 delete cb;
940 return;
941 }
942
943 std::string info_url = url.substr(strlen(kURLPrefix));
944
945 data_source_factory_->Build(
946 info_url,
947 NewCallback(new BuildState(info_url, cb, demuxer_),
948 &ChunkDemuxerFactory::BuildState::BuildDone));
949 demuxer_ = NULL;
950 }
951
952 DemuxerFactory* ChunkDemuxerFactory::Clone() const {
953 return new ChunkDemuxerFactory(data_source_factory_->Clone());
954 }
955
956 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698