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

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

Powered by Google App Engine
This is Rietveld 408576698