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 |