| 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 |