OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "base/callback.h" | 5 #include "base/callback.h" |
6 #include "base/command_line.h" | 6 #include "base/command_line.h" |
7 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
8 #include "base/scoped_ptr.h" | 8 #include "base/scoped_ptr.h" |
9 #include "base/stl_util-inl.h" | 9 #include "base/stl_util-inl.h" |
10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 const AVRational& time_base, int64 timestamp) { | 232 const AVRational& time_base, int64 timestamp) { |
233 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) | 233 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) |
234 return kNoTimestamp; | 234 return kNoTimestamp; |
235 | 235 |
236 return ConvertTimestamp(time_base, timestamp); | 236 return ConvertTimestamp(time_base, timestamp); |
237 } | 237 } |
238 | 238 |
239 // | 239 // |
240 // FFmpegDemuxer | 240 // FFmpegDemuxer |
241 // | 241 // |
242 FFmpegDemuxer::FFmpegDemuxer() | 242 FFmpegDemuxer::FFmpegDemuxer(MessageLoop* message_loop) |
243 : format_context_(NULL), | 243 : message_loop_(message_loop), |
| 244 format_context_(NULL), |
244 read_event_(false, false), | 245 read_event_(false, false), |
245 read_has_failed_(false), | 246 read_has_failed_(false), |
246 last_read_bytes_(0), | 247 last_read_bytes_(0), |
247 read_position_(0) { | 248 read_position_(0) { |
| 249 DCHECK(message_loop_); |
248 } | 250 } |
249 | 251 |
250 FFmpegDemuxer::~FFmpegDemuxer() { | 252 FFmpegDemuxer::~FFmpegDemuxer() { |
251 // In this destructor, we clean up resources held by FFmpeg. It is ugly to | 253 // In this destructor, we clean up resources held by FFmpeg. It is ugly to |
252 // close the codec contexts here because the corresponding codecs are opened | 254 // close the codec contexts here because the corresponding codecs are opened |
253 // in the decoder filters. By reaching this point, all filters should have | 255 // in the decoder filters. By reaching this point, all filters should have |
254 // stopped, so this is the only safe place to do the global clean up. | 256 // stopped, so this is the only safe place to do the global clean up. |
255 // TODO(hclam): close the codecs in the corresponding decoders. | 257 // TODO(hclam): close the codecs in the corresponding decoders. |
256 if (!format_context_) | 258 if (!format_context_) |
257 return; | 259 return; |
(...skipping 14 matching lines...) Expand all Loading... |
272 avcodec_close(stream->codec); | 274 avcodec_close(stream->codec); |
273 } | 275 } |
274 } | 276 } |
275 | 277 |
276 // Then finally cleanup the format context. | 278 // Then finally cleanup the format context. |
277 av_close_input_file(format_context_); | 279 av_close_input_file(format_context_); |
278 format_context_ = NULL; | 280 format_context_ = NULL; |
279 } | 281 } |
280 | 282 |
281 void FFmpegDemuxer::PostDemuxTask() { | 283 void FFmpegDemuxer::PostDemuxTask() { |
282 message_loop()->PostTask(FROM_HERE, | 284 message_loop_->PostTask(FROM_HERE, |
283 NewRunnableMethod(this, &FFmpegDemuxer::DemuxTask)); | 285 NewRunnableMethod(this, &FFmpegDemuxer::DemuxTask)); |
284 } | 286 } |
285 | 287 |
286 void FFmpegDemuxer::Stop(FilterCallback* callback) { | 288 void FFmpegDemuxer::Stop(FilterCallback* callback) { |
287 // Post a task to notify the streams to stop as well. | 289 // Post a task to notify the streams to stop as well. |
288 message_loop()->PostTask(FROM_HERE, | 290 message_loop_->PostTask(FROM_HERE, |
289 NewRunnableMethod(this, &FFmpegDemuxer::StopTask, callback)); | 291 NewRunnableMethod(this, &FFmpegDemuxer::StopTask, callback)); |
290 | 292 |
291 // Then wakes up the thread from reading. | 293 // Then wakes up the thread from reading. |
292 SignalReadCompleted(DataSource::kReadError); | 294 SignalReadCompleted(DataSource::kReadError); |
293 } | 295 } |
294 | 296 |
295 void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) { | 297 void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) { |
296 // TODO(hclam): by returning from this method, it is assumed that the seek | 298 // TODO(hclam): by returning from this method, it is assumed that the seek |
297 // operation is completed and filters behind the demuxer is good to issue | 299 // operation is completed and filters behind the demuxer is good to issue |
298 // more reads, but we are posting a task here, which makes the seek operation | 300 // more reads, but we are posting a task here, which makes the seek operation |
299 // asynchronous, should change how seek works to make it fully asynchronous. | 301 // asynchronous, should change how seek works to make it fully asynchronous. |
300 message_loop()->PostTask(FROM_HERE, | 302 message_loop_->PostTask(FROM_HERE, |
301 NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback)); | 303 NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback)); |
302 } | 304 } |
303 | 305 |
304 void FFmpegDemuxer::OnAudioRendererDisabled() { | 306 void FFmpegDemuxer::OnAudioRendererDisabled() { |
305 message_loop()->PostTask(FROM_HERE, | 307 message_loop_->PostTask(FROM_HERE, |
306 NewRunnableMethod(this, &FFmpegDemuxer::DisableAudioStreamTask)); | 308 NewRunnableMethod(this, &FFmpegDemuxer::DisableAudioStreamTask)); |
307 } | 309 } |
308 | 310 |
309 void FFmpegDemuxer::Initialize(DataSource* data_source, | 311 void FFmpegDemuxer::Initialize(DataSource* data_source, |
310 FilterCallback* callback) { | 312 FilterCallback* callback) { |
311 message_loop()->PostTask( | 313 message_loop_->PostTask( |
312 FROM_HERE, | 314 FROM_HERE, |
313 NewRunnableMethod(this, | 315 NewRunnableMethod(this, |
314 &FFmpegDemuxer::InitializeTask, | 316 &FFmpegDemuxer::InitializeTask, |
315 make_scoped_refptr(data_source), | 317 make_scoped_refptr(data_source), |
316 callback)); | 318 callback)); |
317 } | 319 } |
318 | 320 |
319 size_t FFmpegDemuxer::GetNumberOfStreams() { | 321 size_t FFmpegDemuxer::GetNumberOfStreams() { |
320 return streams_.size(); | 322 return streams_.size(); |
321 } | 323 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 | 386 |
385 return data_source_->GetSize(size_out); | 387 return data_source_->GetSize(size_out); |
386 } | 388 } |
387 | 389 |
388 bool FFmpegDemuxer::IsStreaming() { | 390 bool FFmpegDemuxer::IsStreaming() { |
389 DCHECK(data_source_); | 391 DCHECK(data_source_); |
390 | 392 |
391 return data_source_->IsStreaming(); | 393 return data_source_->IsStreaming(); |
392 } | 394 } |
393 | 395 |
| 396 MessageLoop* FFmpegDemuxer::message_loop() { |
| 397 return message_loop_; |
| 398 } |
| 399 |
394 void FFmpegDemuxer::InitializeTask(DataSource* data_source, | 400 void FFmpegDemuxer::InitializeTask(DataSource* data_source, |
395 FilterCallback* callback) { | 401 FilterCallback* callback) { |
396 DCHECK_EQ(MessageLoop::current(), message_loop()); | 402 DCHECK_EQ(MessageLoop::current(), message_loop_); |
397 scoped_ptr<FilterCallback> c(callback); | 403 scoped_ptr<FilterCallback> c(callback); |
398 | 404 |
399 data_source_ = data_source; | 405 data_source_ = data_source; |
400 | 406 |
401 // Add ourself to Protocol list and get our unique key. | 407 // Add ourself to Protocol list and get our unique key. |
402 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); | 408 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); |
403 | 409 |
404 // Open FFmpeg AVFormatContext. | 410 // Open FFmpeg AVFormatContext. |
405 DCHECK(!format_context_); | 411 DCHECK(!format_context_); |
406 AVFormatContext* context = NULL; | 412 AVFormatContext* context = NULL; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
470 max_duration = base::TimeDelta::FromMicroseconds( | 476 max_duration = base::TimeDelta::FromMicroseconds( |
471 Limits::kMaxTimeInMicroseconds); | 477 Limits::kMaxTimeInMicroseconds); |
472 } | 478 } |
473 | 479 |
474 // Good to go: set the duration and notify we're done initializing. | 480 // Good to go: set the duration and notify we're done initializing. |
475 host()->SetDuration(max_duration); | 481 host()->SetDuration(max_duration); |
476 callback->Run(); | 482 callback->Run(); |
477 } | 483 } |
478 | 484 |
479 void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { | 485 void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { |
480 DCHECK_EQ(MessageLoop::current(), message_loop()); | 486 DCHECK_EQ(MessageLoop::current(), message_loop_); |
481 scoped_ptr<FilterCallback> c(callback); | 487 scoped_ptr<FilterCallback> c(callback); |
482 | 488 |
483 // Tell streams to flush buffers due to seeking. | 489 // Tell streams to flush buffers due to seeking. |
484 StreamVector::iterator iter; | 490 StreamVector::iterator iter; |
485 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 491 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
486 (*iter)->FlushBuffers(); | 492 (*iter)->FlushBuffers(); |
487 } | 493 } |
488 | 494 |
489 // Always seek to a timestamp less than or equal to the desired timestamp. | 495 // Always seek to a timestamp less than or equal to the desired timestamp. |
490 int flags = AVSEEK_FLAG_BACKWARD; | 496 int flags = AVSEEK_FLAG_BACKWARD; |
491 | 497 |
492 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 498 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
493 // will attempt to use the lowest-index video stream, if present, followed by | 499 // will attempt to use the lowest-index video stream, if present, followed by |
494 // the lowest-index audio stream. | 500 // the lowest-index audio stream. |
495 if (av_seek_frame(format_context_, -1, time.InMicroseconds(), flags) < 0) { | 501 if (av_seek_frame(format_context_, -1, time.InMicroseconds(), flags) < 0) { |
496 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 502 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
497 // captured from stdout and contaminates testing. | 503 // captured from stdout and contaminates testing. |
498 // TODO(scherkus): Implement this properly and signal error (BUG=23447). | 504 // TODO(scherkus): Implement this properly and signal error (BUG=23447). |
499 VLOG(1) << "Not implemented"; | 505 VLOG(1) << "Not implemented"; |
500 } | 506 } |
501 | 507 |
502 // Notify we're finished seeking. | 508 // Notify we're finished seeking. |
503 callback->Run(); | 509 callback->Run(); |
504 } | 510 } |
505 | 511 |
506 void FFmpegDemuxer::DemuxTask() { | 512 void FFmpegDemuxer::DemuxTask() { |
507 DCHECK_EQ(MessageLoop::current(), message_loop()); | 513 DCHECK_EQ(MessageLoop::current(), message_loop_); |
508 | 514 |
509 // Make sure we have work to do before demuxing. | 515 // Make sure we have work to do before demuxing. |
510 if (!StreamsHavePendingReads()) { | 516 if (!StreamsHavePendingReads()) { |
511 return; | 517 return; |
512 } | 518 } |
513 | 519 |
514 // Allocate and read an AVPacket from the media. | 520 // Allocate and read an AVPacket from the media. |
515 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet(new AVPacket()); | 521 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet(new AVPacket()); |
516 int result = av_read_frame(format_context_, packet.get()); | 522 int result = av_read_frame(format_context_, packet.get()); |
517 if (result < 0) { | 523 if (result < 0) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
549 } | 555 } |
550 | 556 |
551 // Create a loop by posting another task. This allows seek and message loop | 557 // Create a loop by posting another task. This allows seek and message loop |
552 // quit tasks to get processed. | 558 // quit tasks to get processed. |
553 if (StreamsHavePendingReads()) { | 559 if (StreamsHavePendingReads()) { |
554 PostDemuxTask(); | 560 PostDemuxTask(); |
555 } | 561 } |
556 } | 562 } |
557 | 563 |
558 void FFmpegDemuxer::StopTask(FilterCallback* callback) { | 564 void FFmpegDemuxer::StopTask(FilterCallback* callback) { |
559 DCHECK_EQ(MessageLoop::current(), message_loop()); | 565 DCHECK_EQ(MessageLoop::current(), message_loop_); |
560 StreamVector::iterator iter; | 566 StreamVector::iterator iter; |
561 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 567 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
562 (*iter)->Stop(); | 568 (*iter)->Stop(); |
563 } | 569 } |
564 if (callback) { | 570 if (callback) { |
565 callback->Run(); | 571 callback->Run(); |
566 delete callback; | 572 delete callback; |
567 } | 573 } |
568 } | 574 } |
569 | 575 |
570 void FFmpegDemuxer::DisableAudioStreamTask() { | 576 void FFmpegDemuxer::DisableAudioStreamTask() { |
571 DCHECK_EQ(MessageLoop::current(), message_loop()); | 577 DCHECK_EQ(MessageLoop::current(), message_loop_); |
572 | 578 |
573 StreamVector::iterator iter; | 579 StreamVector::iterator iter; |
574 for (size_t i = 0; i < packet_streams_.size(); ++i) { | 580 for (size_t i = 0; i < packet_streams_.size(); ++i) { |
575 if (!packet_streams_[i]) | 581 if (!packet_streams_[i]) |
576 continue; | 582 continue; |
577 | 583 |
578 // If the codec type is audio, remove the reference. DemuxTask() will | 584 // If the codec type is audio, remove the reference. DemuxTask() will |
579 // look for such reference, and this will result in deleting the | 585 // look for such reference, and this will result in deleting the |
580 // audio packets after they are demuxed. | 586 // audio packets after they are demuxed. |
581 if (packet_streams_[i]->GetAVStream()->codec->codec_type == | 587 if (packet_streams_[i]->GetAVStream()->codec->codec_type == |
582 CODEC_TYPE_AUDIO) { | 588 CODEC_TYPE_AUDIO) { |
583 packet_streams_[i] = NULL; | 589 packet_streams_[i] = NULL; |
584 } | 590 } |
585 } | 591 } |
586 } | 592 } |
587 | 593 |
588 bool FFmpegDemuxer::StreamsHavePendingReads() { | 594 bool FFmpegDemuxer::StreamsHavePendingReads() { |
589 DCHECK_EQ(MessageLoop::current(), message_loop()); | 595 DCHECK_EQ(MessageLoop::current(), message_loop_); |
590 StreamVector::iterator iter; | 596 StreamVector::iterator iter; |
591 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 597 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
592 if ((*iter)->HasPendingReads()) { | 598 if ((*iter)->HasPendingReads()) { |
593 return true; | 599 return true; |
594 } | 600 } |
595 } | 601 } |
596 return false; | 602 return false; |
597 } | 603 } |
598 | 604 |
599 void FFmpegDemuxer::StreamHasEnded() { | 605 void FFmpegDemuxer::StreamHasEnded() { |
600 DCHECK_EQ(MessageLoop::current(), message_loop()); | 606 DCHECK_EQ(MessageLoop::current(), message_loop_); |
601 StreamVector::iterator iter; | 607 StreamVector::iterator iter; |
602 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 608 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
603 AVPacket* packet = new AVPacket(); | 609 AVPacket* packet = new AVPacket(); |
604 memset(packet, 0, sizeof(*packet)); | 610 memset(packet, 0, sizeof(*packet)); |
605 (*iter)->EnqueuePacket(packet); | 611 (*iter)->EnqueuePacket(packet); |
606 } | 612 } |
607 } | 613 } |
608 | 614 |
609 void FFmpegDemuxer::OnReadCompleted(size_t size) { | 615 void FFmpegDemuxer::OnReadCompleted(size_t size) { |
610 SignalReadCompleted(size); | 616 SignalReadCompleted(size); |
611 } | 617 } |
612 | 618 |
613 size_t FFmpegDemuxer::WaitForRead() { | 619 size_t FFmpegDemuxer::WaitForRead() { |
614 read_event_.Wait(); | 620 read_event_.Wait(); |
615 return last_read_bytes_; | 621 return last_read_bytes_; |
616 } | 622 } |
617 | 623 |
618 void FFmpegDemuxer::SignalReadCompleted(size_t size) { | 624 void FFmpegDemuxer::SignalReadCompleted(size_t size) { |
619 last_read_bytes_ = size; | 625 last_read_bytes_ = size; |
620 read_event_.Signal(); | 626 read_event_.Signal(); |
621 } | 627 } |
622 | 628 |
623 } // namespace media | 629 } // namespace media |
OLD | NEW |