| 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 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 | 245 |
| 246 // | 246 // |
| 247 // FFmpegDemuxer | 247 // FFmpegDemuxer |
| 248 // | 248 // |
| 249 FFmpegDemuxer::FFmpegDemuxer(MessageLoop* message_loop) | 249 FFmpegDemuxer::FFmpegDemuxer(MessageLoop* message_loop) |
| 250 : message_loop_(message_loop), | 250 : message_loop_(message_loop), |
| 251 format_context_(NULL), | 251 format_context_(NULL), |
| 252 read_event_(false, false), | 252 read_event_(false, false), |
| 253 read_has_failed_(false), | 253 read_has_failed_(false), |
| 254 last_read_bytes_(0), | 254 last_read_bytes_(0), |
| 255 read_position_(0) { | 255 read_position_(0), |
| 256 max_duration_(base::TimeDelta::FromMicroseconds(-1)), |
| 257 deferred_status_(PIPELINE_OK) { |
| 256 DCHECK(message_loop_); | 258 DCHECK(message_loop_); |
| 257 } | 259 } |
| 258 | 260 |
| 259 FFmpegDemuxer::~FFmpegDemuxer() { | 261 FFmpegDemuxer::~FFmpegDemuxer() { |
| 260 // In this destructor, we clean up resources held by FFmpeg. It is ugly to | 262 // In this destructor, we clean up resources held by FFmpeg. It is ugly to |
| 261 // close the codec contexts here because the corresponding codecs are opened | 263 // close the codec contexts here because the corresponding codecs are opened |
| 262 // in the decoder filters. By reaching this point, all filters should have | 264 // in the decoder filters. By reaching this point, all filters should have |
| 263 // stopped, so this is the only safe place to do the global clean up. | 265 // stopped, so this is the only safe place to do the global clean up. |
| 264 // TODO(hclam): close the codecs in the corresponding decoders. | 266 // TODO(hclam): close the codecs in the corresponding decoders. |
| 265 if (!format_context_) | 267 if (!format_context_) |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 // asynchronous, should change how seek works to make it fully asynchronous. | 310 // asynchronous, should change how seek works to make it fully asynchronous. |
| 309 message_loop_->PostTask(FROM_HERE, | 311 message_loop_->PostTask(FROM_HERE, |
| 310 NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback)); | 312 NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback)); |
| 311 } | 313 } |
| 312 | 314 |
| 313 void FFmpegDemuxer::OnAudioRendererDisabled() { | 315 void FFmpegDemuxer::OnAudioRendererDisabled() { |
| 314 message_loop_->PostTask(FROM_HERE, | 316 message_loop_->PostTask(FROM_HERE, |
| 315 NewRunnableMethod(this, &FFmpegDemuxer::DisableAudioStreamTask)); | 317 NewRunnableMethod(this, &FFmpegDemuxer::DisableAudioStreamTask)); |
| 316 } | 318 } |
| 317 | 319 |
| 320 void FFmpegDemuxer::set_host(FilterHost* filter_host) { |
| 321 Demuxer::set_host(filter_host); |
| 322 if (data_source_) |
| 323 data_source_->set_host(filter_host); |
| 324 if (max_duration_.InMicroseconds() >= 0) |
| 325 host()->SetDuration(max_duration_); |
| 326 if (read_position_ > 0) |
| 327 host()->SetCurrentReadPosition(read_position_); |
| 328 if (deferred_status_ != PIPELINE_OK) |
| 329 host()->SetError(deferred_status_); |
| 330 } |
| 331 |
| 318 void FFmpegDemuxer::Initialize(DataSource* data_source, | 332 void FFmpegDemuxer::Initialize(DataSource* data_source, |
| 319 FilterCallback* callback) { | 333 PipelineStatusCallback* callback) { |
| 320 message_loop_->PostTask( | 334 message_loop_->PostTask( |
| 321 FROM_HERE, | 335 FROM_HERE, |
| 322 NewRunnableMethod(this, | 336 NewRunnableMethod(this, |
| 323 &FFmpegDemuxer::InitializeTask, | 337 &FFmpegDemuxer::InitializeTask, |
| 324 make_scoped_refptr(data_source), | 338 make_scoped_refptr(data_source), |
| 325 callback)); | 339 callback)); |
| 326 } | 340 } |
| 327 | 341 |
| 328 size_t FFmpegDemuxer::GetNumberOfStreams() { | 342 size_t FFmpegDemuxer::GetNumberOfStreams() { |
| 329 return streams_.size(); | 343 return streams_.size(); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 351 | 365 |
| 352 // Asynchronous read from data source. | 366 // Asynchronous read from data source. |
| 353 data_source_->Read(read_position_, size, data, | 367 data_source_->Read(read_position_, size, data, |
| 354 NewCallback(this, &FFmpegDemuxer::OnReadCompleted)); | 368 NewCallback(this, &FFmpegDemuxer::OnReadCompleted)); |
| 355 | 369 |
| 356 // TODO(hclam): The method is called on the demuxer thread and this method | 370 // TODO(hclam): The method is called on the demuxer thread and this method |
| 357 // call will block the thread. We need to implemented an additional thread to | 371 // call will block the thread. We need to implemented an additional thread to |
| 358 // let FFmpeg demuxer methods to run on. | 372 // let FFmpeg demuxer methods to run on. |
| 359 size_t last_read_bytes = WaitForRead(); | 373 size_t last_read_bytes = WaitForRead(); |
| 360 if (last_read_bytes == DataSource::kReadError) { | 374 if (last_read_bytes == DataSource::kReadError) { |
| 361 host()->SetError(PIPELINE_ERROR_READ); | 375 if (host()) |
| 376 host()->SetError(PIPELINE_ERROR_READ); |
| 377 else |
| 378 deferred_status_ = PIPELINE_ERROR_READ; |
| 362 | 379 |
| 363 // Returns with a negative number to signal an error to FFmpeg. | 380 // Returns with a negative number to signal an error to FFmpeg. |
| 364 read_has_failed_ = true; | 381 read_has_failed_ = true; |
| 365 return AVERROR_IO; | 382 return AVERROR_IO; |
| 366 } | 383 } |
| 367 read_position_ += last_read_bytes; | 384 read_position_ += last_read_bytes; |
| 368 | 385 |
| 369 host()->SetCurrentReadPosition(read_position_); | 386 if (host()) |
| 387 host()->SetCurrentReadPosition(read_position_); |
| 370 | 388 |
| 371 return last_read_bytes; | 389 return last_read_bytes; |
| 372 } | 390 } |
| 373 | 391 |
| 374 bool FFmpegDemuxer::GetPosition(int64* position_out) { | 392 bool FFmpegDemuxer::GetPosition(int64* position_out) { |
| 375 *position_out = read_position_; | 393 *position_out = read_position_; |
| 376 return true; | 394 return true; |
| 377 } | 395 } |
| 378 | 396 |
| 379 bool FFmpegDemuxer::SetPosition(int64 position) { | 397 bool FFmpegDemuxer::SetPosition(int64 position) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 398 DCHECK(data_source_); | 416 DCHECK(data_source_); |
| 399 | 417 |
| 400 return data_source_->IsStreaming(); | 418 return data_source_->IsStreaming(); |
| 401 } | 419 } |
| 402 | 420 |
| 403 MessageLoop* FFmpegDemuxer::message_loop() { | 421 MessageLoop* FFmpegDemuxer::message_loop() { |
| 404 return message_loop_; | 422 return message_loop_; |
| 405 } | 423 } |
| 406 | 424 |
| 407 void FFmpegDemuxer::InitializeTask(DataSource* data_source, | 425 void FFmpegDemuxer::InitializeTask(DataSource* data_source, |
| 408 FilterCallback* callback) { | 426 PipelineStatusCallback* callback) { |
| 409 DCHECK_EQ(MessageLoop::current(), message_loop_); | 427 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 410 scoped_ptr<FilterCallback> c(callback); | 428 scoped_ptr<PipelineStatusCallback> callback_deleter(callback); |
| 411 | 429 |
| 412 data_source_ = data_source; | 430 data_source_ = data_source; |
| 431 if (host()) |
| 432 data_source_->set_host(host()); |
| 413 | 433 |
| 414 // Add ourself to Protocol list and get our unique key. | 434 // Add ourself to Protocol list and get our unique key. |
| 415 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); | 435 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); |
| 416 | 436 |
| 417 // Open FFmpeg AVFormatContext. | 437 // Open FFmpeg AVFormatContext. |
| 418 DCHECK(!format_context_); | 438 DCHECK(!format_context_); |
| 419 AVFormatContext* context = NULL; | 439 AVFormatContext* context = NULL; |
| 420 int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); | 440 int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); |
| 421 | 441 |
| 422 // Remove ourself from protocol list. | 442 // Remove ourself from protocol list. |
| 423 FFmpegGlue::GetInstance()->RemoveProtocol(this); | 443 FFmpegGlue::GetInstance()->RemoveProtocol(this); |
| 424 | 444 |
| 425 if (result < 0) { | 445 if (result < 0) { |
| 426 host()->SetError(DEMUXER_ERROR_COULD_NOT_OPEN); | 446 callback->Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 427 callback->Run(); | |
| 428 return; | 447 return; |
| 429 } | 448 } |
| 430 | 449 |
| 431 DCHECK(context); | 450 DCHECK(context); |
| 432 format_context_ = context; | 451 format_context_ = context; |
| 433 | 452 |
| 434 // Fully initialize AVFormatContext by parsing the stream a little. | 453 // Fully initialize AVFormatContext by parsing the stream a little. |
| 435 result = av_find_stream_info(format_context_); | 454 result = av_find_stream_info(format_context_); |
| 436 if (result < 0) { | 455 if (result < 0) { |
| 437 host()->SetError(DEMUXER_ERROR_COULD_NOT_PARSE); | 456 callback->Run(DEMUXER_ERROR_COULD_NOT_PARSE); |
| 438 callback->Run(); | |
| 439 return; | 457 return; |
| 440 } | 458 } |
| 441 | 459 |
| 442 // Create demuxer streams for all supported streams. | 460 // Create demuxer streams for all supported streams. |
| 443 base::TimeDelta max_duration; | 461 base::TimeDelta max_duration; |
| 444 const bool kDemuxerIsWebm = !strcmp("webm", format_context_->iformat->name); | 462 const bool kDemuxerIsWebm = !strcmp("webm", format_context_->iformat->name); |
| 445 for (size_t i = 0; i < format_context_->nb_streams; ++i) { | 463 for (size_t i = 0; i < format_context_->nb_streams; ++i) { |
| 446 AVCodecContext* codec_context = format_context_->streams[i]->codec; | 464 AVCodecContext* codec_context = format_context_->streams[i]->codec; |
| 447 CodecType codec_type = codec_context->codec_type; | 465 CodecType codec_type = codec_context->codec_type; |
| 448 if (codec_type == CODEC_TYPE_AUDIO || codec_type == CODEC_TYPE_VIDEO) { | 466 if (codec_type == CODEC_TYPE_AUDIO || codec_type == CODEC_TYPE_VIDEO) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 459 | 477 |
| 460 DCHECK(demuxer_stream); | 478 DCHECK(demuxer_stream); |
| 461 streams_.push_back(make_scoped_refptr(demuxer_stream)); | 479 streams_.push_back(make_scoped_refptr(demuxer_stream)); |
| 462 packet_streams_.push_back(make_scoped_refptr(demuxer_stream)); | 480 packet_streams_.push_back(make_scoped_refptr(demuxer_stream)); |
| 463 max_duration = std::max(max_duration, demuxer_stream->duration()); | 481 max_duration = std::max(max_duration, demuxer_stream->duration()); |
| 464 } else { | 482 } else { |
| 465 packet_streams_.push_back(NULL); | 483 packet_streams_.push_back(NULL); |
| 466 } | 484 } |
| 467 } | 485 } |
| 468 if (streams_.empty()) { | 486 if (streams_.empty()) { |
| 469 host()->SetError(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | 487 callback->Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| 470 callback->Run(); | |
| 471 return; | 488 return; |
| 472 } | 489 } |
| 473 if (format_context_->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 490 if (format_context_->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 474 // If there is a duration value in the container use that to find the | 491 // If there is a duration value in the container use that to find the |
| 475 // maximum between it and the duration from A/V streams. | 492 // maximum between it and the duration from A/V streams. |
| 476 const AVRational av_time_base = {1, AV_TIME_BASE}; | 493 const AVRational av_time_base = {1, AV_TIME_BASE}; |
| 477 max_duration = | 494 max_duration = |
| 478 std::max(max_duration, | 495 std::max(max_duration, |
| 479 ConvertTimestamp(av_time_base, format_context_->duration)); | 496 ConvertTimestamp(av_time_base, format_context_->duration)); |
| 480 } else { | 497 } else { |
| 481 // If the duration is not a valid value. Assume that this is a live stream | 498 // If the duration is not a valid value. Assume that this is a live stream |
| 482 // and we set duration to the maximum int64 number to represent infinity. | 499 // and we set duration to the maximum int64 number to represent infinity. |
| 483 max_duration = base::TimeDelta::FromMicroseconds( | 500 max_duration = base::TimeDelta::FromMicroseconds( |
| 484 Limits::kMaxTimeInMicroseconds); | 501 Limits::kMaxTimeInMicroseconds); |
| 485 } | 502 } |
| 486 | 503 |
| 487 // Good to go: set the duration and notify we're done initializing. | 504 // Good to go: set the duration and notify we're done initializing. |
| 488 host()->SetDuration(max_duration); | 505 if (host()) |
| 489 callback->Run(); | 506 host()->SetDuration(max_duration); |
| 507 max_duration_ = max_duration; |
| 508 callback->Run(PIPELINE_OK); |
| 490 } | 509 } |
| 491 | 510 |
| 492 void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { | 511 void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { |
| 493 DCHECK_EQ(MessageLoop::current(), message_loop_); | 512 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 494 scoped_ptr<FilterCallback> c(callback); | 513 scoped_ptr<FilterCallback> c(callback); |
| 495 | 514 |
| 496 // Tell streams to flush buffers due to seeking. | 515 // Tell streams to flush buffers due to seeking. |
| 497 StreamVector::iterator iter; | 516 StreamVector::iterator iter; |
| 498 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 517 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 499 (*iter)->FlushBuffers(); | 518 (*iter)->FlushBuffers(); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 567 PostDemuxTask(); | 586 PostDemuxTask(); |
| 568 } | 587 } |
| 569 } | 588 } |
| 570 | 589 |
| 571 void FFmpegDemuxer::StopTask(FilterCallback* callback) { | 590 void FFmpegDemuxer::StopTask(FilterCallback* callback) { |
| 572 DCHECK_EQ(MessageLoop::current(), message_loop_); | 591 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 573 StreamVector::iterator iter; | 592 StreamVector::iterator iter; |
| 574 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 593 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 575 (*iter)->Stop(); | 594 (*iter)->Stop(); |
| 576 } | 595 } |
| 577 if (callback) { | 596 if (data_source_) { |
| 597 data_source_->Stop(callback); |
| 598 } else { |
| 578 callback->Run(); | 599 callback->Run(); |
| 579 delete callback; | 600 delete callback; |
| 580 } | 601 } |
| 581 } | 602 } |
| 582 | 603 |
| 583 void FFmpegDemuxer::DisableAudioStreamTask() { | 604 void FFmpegDemuxer::DisableAudioStreamTask() { |
| 584 DCHECK_EQ(MessageLoop::current(), message_loop_); | 605 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 585 | 606 |
| 586 StreamVector::iterator iter; | 607 StreamVector::iterator iter; |
| 587 for (size_t i = 0; i < packet_streams_.size(); ++i) { | 608 for (size_t i = 0; i < packet_streams_.size(); ++i) { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 627 read_event_.Wait(); | 648 read_event_.Wait(); |
| 628 return last_read_bytes_; | 649 return last_read_bytes_; |
| 629 } | 650 } |
| 630 | 651 |
| 631 void FFmpegDemuxer::SignalReadCompleted(size_t size) { | 652 void FFmpegDemuxer::SignalReadCompleted(size_t size) { |
| 632 last_read_bytes_ = size; | 653 last_read_bytes_ = size; |
| 633 read_event_.Signal(); | 654 read_event_.Signal(); |
| 634 } | 655 } |
| 635 | 656 |
| 636 } // namespace media | 657 } // namespace media |
| OLD | NEW |