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

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

Powered by Google App Engine
This is Rietveld 408576698