| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "media/filters/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 return base::Time(); | 62 return base::Time(); |
| 63 } | 63 } |
| 64 | 64 |
| 65 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) { | 65 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) { |
| 66 return base::TimeDelta::FromMicroseconds( | 66 return base::TimeDelta::FromMicroseconds( |
| 67 frames * base::Time::kMicrosecondsPerSecond / sample_rate); | 67 frames * base::Time::kMicrosecondsPerSecond / sample_rate); |
| 68 } | 68 } |
| 69 | 69 |
| 70 static base::TimeDelta ExtractStartTime(AVStream* stream, | 70 static base::TimeDelta ExtractStartTime(AVStream* stream, |
| 71 base::TimeDelta start_time_estimate) { | 71 base::TimeDelta start_time_estimate) { |
| 72 DCHECK(start_time_estimate != kNoTimestamp()); | 72 DCHECK(start_time_estimate != kNoTimestamp); |
| 73 if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) { | 73 if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 74 return start_time_estimate == kInfiniteDuration() ? kNoTimestamp() | 74 return start_time_estimate == kInfiniteDuration ? kNoTimestamp |
| 75 : start_time_estimate; | 75 : start_time_estimate; |
| 76 } | 76 } |
| 77 | 77 |
| 78 // First try the lower of the estimate and the |start_time| value. | 78 // First try the lower of the estimate and the |start_time| value. |
| 79 base::TimeDelta start_time = | 79 base::TimeDelta start_time = |
| 80 std::min(ConvertFromTimeBase(stream->time_base, stream->start_time), | 80 std::min(ConvertFromTimeBase(stream->time_base, stream->start_time), |
| 81 start_time_estimate); | 81 start_time_estimate); |
| 82 | 82 |
| 83 // Next see if the first buffered pts value is usable. | 83 // Next see if the first buffered pts value is usable. |
| 84 if (stream->pts_buffer[0] != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 84 if (stream->pts_buffer[0] != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 85 const base::TimeDelta buffered_pts = | 85 const base::TimeDelta buffered_pts = |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 return context->codec_descriptor->name; | 187 return context->codec_descriptor->name; |
| 188 const AVCodecDescriptor* codec_descriptor = | 188 const AVCodecDescriptor* codec_descriptor = |
| 189 avcodec_descriptor_get(context->codec_id); | 189 avcodec_descriptor_get(context->codec_id); |
| 190 // If the codec name can't be determined, return none for tracking. | 190 // If the codec name can't be determined, return none for tracking. |
| 191 return codec_descriptor ? codec_descriptor->name : kCodecNone; | 191 return codec_descriptor ? codec_descriptor->name : kCodecNone; |
| 192 } | 192 } |
| 193 | 193 |
| 194 static void SetTimeProperty(MediaLogEvent* event, | 194 static void SetTimeProperty(MediaLogEvent* event, |
| 195 const std::string& key, | 195 const std::string& key, |
| 196 base::TimeDelta value) { | 196 base::TimeDelta value) { |
| 197 if (value == kInfiniteDuration()) | 197 if (value == kInfiniteDuration) |
| 198 event->params.SetString(key, "kInfiniteDuration"); | 198 event->params.SetString(key, "kInfiniteDuration"); |
| 199 else if (value == kNoTimestamp()) | 199 else if (value == kNoTimestamp) |
| 200 event->params.SetString(key, "kNoTimestamp"); | 200 event->params.SetString(key, "kNoTimestamp"); |
| 201 else | 201 else |
| 202 event->params.SetDouble(key, value.InSecondsF()); | 202 event->params.SetDouble(key, value.InSecondsF()); |
| 203 } | 203 } |
| 204 | 204 |
| 205 std::unique_ptr<FFmpegDemuxerStream> FFmpegDemuxerStream::Create( | 205 std::unique_ptr<FFmpegDemuxerStream> FFmpegDemuxerStream::Create( |
| 206 FFmpegDemuxer* demuxer, | 206 FFmpegDemuxer* demuxer, |
| 207 AVStream* stream, | 207 AVStream* stream, |
| 208 const scoped_refptr<MediaLog>& media_log) { | 208 const scoped_refptr<MediaLog>& media_log) { |
| 209 if (!demuxer || !stream) | 209 if (!demuxer || !stream) |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 std::unique_ptr<AudioDecoderConfig> audio_config, | 262 std::unique_ptr<AudioDecoderConfig> audio_config, |
| 263 std::unique_ptr<VideoDecoderConfig> video_config) | 263 std::unique_ptr<VideoDecoderConfig> video_config) |
| 264 : demuxer_(demuxer), | 264 : demuxer_(demuxer), |
| 265 task_runner_(base::ThreadTaskRunnerHandle::Get()), | 265 task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 266 stream_(stream), | 266 stream_(stream), |
| 267 audio_config_(audio_config.release()), | 267 audio_config_(audio_config.release()), |
| 268 video_config_(video_config.release()), | 268 video_config_(video_config.release()), |
| 269 type_(UNKNOWN), | 269 type_(UNKNOWN), |
| 270 liveness_(LIVENESS_UNKNOWN), | 270 liveness_(LIVENESS_UNKNOWN), |
| 271 end_of_stream_(false), | 271 end_of_stream_(false), |
| 272 last_packet_timestamp_(kNoTimestamp()), | 272 last_packet_timestamp_(kNoTimestamp), |
| 273 last_packet_duration_(kNoTimestamp()), | 273 last_packet_duration_(kNoTimestamp), |
| 274 video_rotation_(VIDEO_ROTATION_0), | 274 video_rotation_(VIDEO_ROTATION_0), |
| 275 is_enabled_(true), | 275 is_enabled_(true), |
| 276 waiting_for_keyframe_(false), | 276 waiting_for_keyframe_(false), |
| 277 fixup_negative_timestamps_(false) { | 277 fixup_negative_timestamps_(false) { |
| 278 DCHECK(demuxer_); | 278 DCHECK(demuxer_); |
| 279 | 279 |
| 280 bool is_encrypted = false; | 280 bool is_encrypted = false; |
| 281 int rotation = 0; | 281 int rotation = 0; |
| 282 AVDictionaryEntry* rotation_entry = NULL; | 282 AVDictionaryEntry* rotation_entry = NULL; |
| 283 | 283 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 const uint32_t* skip_samples_ptr = | 433 const uint32_t* skip_samples_ptr = |
| 434 reinterpret_cast<const uint32_t*>(av_packet_get_side_data( | 434 reinterpret_cast<const uint32_t*>(av_packet_get_side_data( |
| 435 packet.get(), AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size)); | 435 packet.get(), AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size)); |
| 436 const int kSkipSamplesValidSize = 10; | 436 const int kSkipSamplesValidSize = 10; |
| 437 const int kSkipEndSamplesOffset = 1; | 437 const int kSkipEndSamplesOffset = 1; |
| 438 if (skip_samples_size >= kSkipSamplesValidSize) { | 438 if (skip_samples_size >= kSkipSamplesValidSize) { |
| 439 // Because FFmpeg rolls codec delay and skip samples into one we can only | 439 // Because FFmpeg rolls codec delay and skip samples into one we can only |
| 440 // allow front discard padding on the first buffer. Otherwise the discard | 440 // allow front discard padding on the first buffer. Otherwise the discard |
| 441 // helper can't figure out which data to discard. See AudioDiscardHelper. | 441 // helper can't figure out which data to discard. See AudioDiscardHelper. |
| 442 int discard_front_samples = base::ByteSwapToLE32(*skip_samples_ptr); | 442 int discard_front_samples = base::ByteSwapToLE32(*skip_samples_ptr); |
| 443 if (last_packet_timestamp_ != kNoTimestamp() && discard_front_samples) { | 443 if (last_packet_timestamp_ != kNoTimestamp && discard_front_samples) { |
| 444 DLOG(ERROR) << "Skip samples are only allowed for the first packet."; | 444 DLOG(ERROR) << "Skip samples are only allowed for the first packet."; |
| 445 discard_front_samples = 0; | 445 discard_front_samples = 0; |
| 446 } | 446 } |
| 447 | 447 |
| 448 const int discard_end_samples = | 448 const int discard_end_samples = |
| 449 base::ByteSwapToLE32(*(skip_samples_ptr + kSkipEndSamplesOffset)); | 449 base::ByteSwapToLE32(*(skip_samples_ptr + kSkipEndSamplesOffset)); |
| 450 const int samples_per_second = | 450 const int samples_per_second = |
| 451 audio_decoder_config().samples_per_second(); | 451 audio_decoder_config().samples_per_second(); |
| 452 buffer->set_discard_padding(std::make_pair( | 452 buffer->set_discard_padding(std::make_pair( |
| 453 FramesToTimeDelta(discard_front_samples, samples_per_second), | 453 FramesToTimeDelta(discard_front_samples, samples_per_second), |
| 454 FramesToTimeDelta(discard_end_samples, samples_per_second))); | 454 FramesToTimeDelta(discard_end_samples, samples_per_second))); |
| 455 } | 455 } |
| 456 | 456 |
| 457 if (decrypt_config) | 457 if (decrypt_config) |
| 458 buffer->set_decrypt_config(std::move(decrypt_config)); | 458 buffer->set_decrypt_config(std::move(decrypt_config)); |
| 459 } | 459 } |
| 460 | 460 |
| 461 if (packet->duration >= 0) { | 461 if (packet->duration >= 0) { |
| 462 buffer->set_duration( | 462 buffer->set_duration( |
| 463 ConvertStreamTimestamp(stream_->time_base, packet->duration)); | 463 ConvertStreamTimestamp(stream_->time_base, packet->duration)); |
| 464 } else { | 464 } else { |
| 465 // TODO(wolenetz): Remove when FFmpeg stops returning negative durations. | 465 // TODO(wolenetz): Remove when FFmpeg stops returning negative durations. |
| 466 // https://crbug.com/394418 | 466 // https://crbug.com/394418 |
| 467 DVLOG(1) << "FFmpeg returned a buffer with a negative duration! " | 467 DVLOG(1) << "FFmpeg returned a buffer with a negative duration! " |
| 468 << packet->duration; | 468 << packet->duration; |
| 469 buffer->set_duration(kNoTimestamp()); | 469 buffer->set_duration(kNoTimestamp); |
| 470 } | 470 } |
| 471 | 471 |
| 472 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). | 472 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp. |
| 473 const base::TimeDelta stream_timestamp = | 473 const base::TimeDelta stream_timestamp = |
| 474 ConvertStreamTimestamp(stream_->time_base, packet->pts); | 474 ConvertStreamTimestamp(stream_->time_base, packet->pts); |
| 475 | 475 |
| 476 if (stream_timestamp != kNoTimestamp()) { | 476 if (stream_timestamp != kNoTimestamp) { |
| 477 const bool is_audio = type() == AUDIO; | 477 const bool is_audio = type() == AUDIO; |
| 478 | 478 |
| 479 // If this file has negative timestamps don't rebase any other stream types | 479 // If this file has negative timestamps don't rebase any other stream types |
| 480 // against the negative starting time. | 480 // against the negative starting time. |
| 481 base::TimeDelta start_time = demuxer_->start_time(); | 481 base::TimeDelta start_time = demuxer_->start_time(); |
| 482 if (fixup_negative_timestamps_ && !is_audio && | 482 if (fixup_negative_timestamps_ && !is_audio && |
| 483 start_time < base::TimeDelta()) { | 483 start_time < base::TimeDelta()) { |
| 484 start_time = base::TimeDelta(); | 484 start_time = base::TimeDelta(); |
| 485 } | 485 } |
| 486 | 486 |
| 487 // Don't rebase timestamps for positive start times, the HTML Media Spec | 487 // Don't rebase timestamps for positive start times, the HTML Media Spec |
| 488 // details this in section "4.8.10.6 Offsets into the media resource." We | 488 // details this in section "4.8.10.6 Offsets into the media resource." We |
| 489 // will still need to rebase timestamps before seeking with FFmpeg though. | 489 // will still need to rebase timestamps before seeking with FFmpeg though. |
| 490 if (start_time > base::TimeDelta()) | 490 if (start_time > base::TimeDelta()) |
| 491 start_time = base::TimeDelta(); | 491 start_time = base::TimeDelta(); |
| 492 | 492 |
| 493 buffer->set_timestamp(stream_timestamp - start_time); | 493 buffer->set_timestamp(stream_timestamp - start_time); |
| 494 | 494 |
| 495 // If enabled, and no codec delay is present, mark audio packets with | 495 // If enabled, and no codec delay is present, mark audio packets with |
| 496 // negative timestamps for post-decode discard. | 496 // negative timestamps for post-decode discard. |
| 497 if (fixup_negative_timestamps_ && is_audio && | 497 if (fixup_negative_timestamps_ && is_audio && |
| 498 stream_timestamp < base::TimeDelta() && | 498 stream_timestamp < base::TimeDelta() && |
| 499 buffer->duration() != kNoTimestamp()) { | 499 buffer->duration() != kNoTimestamp) { |
| 500 if (!stream_->codec->delay) { | 500 if (!stream_->codec->delay) { |
| 501 DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta()); | 501 DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta()); |
| 502 | 502 |
| 503 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { | 503 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { |
| 504 DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta()); | 504 DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta()); |
| 505 | 505 |
| 506 // Discard the entire packet if it's entirely before zero. | 506 // Discard the entire packet if it's entirely before zero. |
| 507 buffer->set_discard_padding( | 507 buffer->set_discard_padding( |
| 508 std::make_pair(kInfiniteDuration(), base::TimeDelta())); | 508 std::make_pair(kInfiniteDuration, base::TimeDelta())); |
| 509 } else { | 509 } else { |
| 510 // Only discard part of the frame if it overlaps zero. | 510 // Only discard part of the frame if it overlaps zero. |
| 511 buffer->set_discard_padding(std::make_pair( | 511 buffer->set_discard_padding(std::make_pair( |
| 512 -stream_timestamp, buffer->discard_padding().second)); | 512 -stream_timestamp, buffer->discard_padding().second)); |
| 513 } | 513 } |
| 514 } else { | 514 } else { |
| 515 // Verify that codec delay would cover discard and that we don't need to | 515 // Verify that codec delay would cover discard and that we don't need to |
| 516 // mark the packet for post decode discard. Since timestamps may be in | 516 // mark the packet for post decode discard. Since timestamps may be in |
| 517 // milliseconds and codec delay in nanosecond precision, round up to the | 517 // milliseconds and codec delay in nanosecond precision, round up to the |
| 518 // nearest millisecond. See enable_negative_timestamp_fixups(). | 518 // nearest millisecond. See enable_negative_timestamp_fixups(). |
| 519 DCHECK_LE(-std::ceil(FramesToTimeDelta( | 519 DCHECK_LE(-std::ceil(FramesToTimeDelta( |
| 520 audio_decoder_config().codec_delay(), | 520 audio_decoder_config().codec_delay(), |
| 521 audio_decoder_config().samples_per_second()) | 521 audio_decoder_config().samples_per_second()) |
| 522 .InMillisecondsF()), | 522 .InMillisecondsF()), |
| 523 stream_timestamp.InMillisecondsF()); | 523 stream_timestamp.InMillisecondsF()); |
| 524 } | 524 } |
| 525 } | 525 } |
| 526 } else { | 526 } else { |
| 527 // If this happens on the first packet, decoders will throw an error. | 527 // If this happens on the first packet, decoders will throw an error. |
| 528 buffer->set_timestamp(kNoTimestamp()); | 528 buffer->set_timestamp(kNoTimestamp); |
| 529 } | 529 } |
| 530 | 530 |
| 531 if (last_packet_timestamp_ != kNoTimestamp()) { | 531 if (last_packet_timestamp_ != kNoTimestamp) { |
| 532 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing | 532 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing |
| 533 // continuity across links in the chain it uses the timestamp information | 533 // continuity across links in the chain it uses the timestamp information |
| 534 // from each link directly. Doing so can lead to timestamps which appear to | 534 // from each link directly. Doing so can lead to timestamps which appear to |
| 535 // go backwards in time. | 535 // go backwards in time. |
| 536 // | 536 // |
| 537 // If the new link starts with a negative timestamp or a timestamp less than | 537 // If the new link starts with a negative timestamp or a timestamp less than |
| 538 // the original (positive) |start_time|, we will get a negative timestamp | 538 // the original (positive) |start_time|, we will get a negative timestamp |
| 539 // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not | 539 // here. It's also possible FFmpeg returns kNoTimestamp here if it's not |
| 540 // able to work out a timestamp using the previous link and the next. | 540 // able to work out a timestamp using the previous link and the next. |
| 541 // | 541 // |
| 542 // Fixing chained ogg is non-trivial, so for now just reuse the last good | 542 // Fixing chained ogg is non-trivial, so for now just reuse the last good |
| 543 // timestamp. The decoder will rewrite the timestamps to be sample accurate | 543 // timestamp. The decoder will rewrite the timestamps to be sample accurate |
| 544 // later. See http://crbug.com/396864. | 544 // later. See http://crbug.com/396864. |
| 545 if (fixup_negative_timestamps_ && | 545 if (fixup_negative_timestamps_ && |
| 546 (buffer->timestamp() == kNoTimestamp() || | 546 (buffer->timestamp() == kNoTimestamp || |
| 547 buffer->timestamp() < last_packet_timestamp_)) { | 547 buffer->timestamp() < last_packet_timestamp_)) { |
| 548 buffer->set_timestamp(last_packet_timestamp_ + | 548 buffer->set_timestamp(last_packet_timestamp_ + |
| 549 (last_packet_duration_ != kNoTimestamp() | 549 (last_packet_duration_ != kNoTimestamp |
| 550 ? last_packet_duration_ | 550 ? last_packet_duration_ |
| 551 : base::TimeDelta::FromMicroseconds(1))); | 551 : base::TimeDelta::FromMicroseconds(1))); |
| 552 } | 552 } |
| 553 | 553 |
| 554 // The demuxer should always output positive timestamps. | 554 // The demuxer should always output positive timestamps. |
| 555 DCHECK(buffer->timestamp() >= base::TimeDelta()); | 555 DCHECK(buffer->timestamp() >= base::TimeDelta()); |
| 556 DCHECK(buffer->timestamp() != kNoTimestamp()); | 556 DCHECK(buffer->timestamp() != kNoTimestamp); |
| 557 | 557 |
| 558 if (last_packet_timestamp_ < buffer->timestamp()) { | 558 if (last_packet_timestamp_ < buffer->timestamp()) { |
| 559 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); | 559 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
| 560 demuxer_->NotifyBufferingChanged(); | 560 demuxer_->NotifyBufferingChanged(); |
| 561 } | 561 } |
| 562 } | 562 } |
| 563 | 563 |
| 564 if (packet.get()->flags & AV_PKT_FLAG_KEY) | 564 if (packet.get()->flags & AV_PKT_FLAG_KEY) |
| 565 buffer->set_is_key_frame(true); | 565 buffer->set_is_key_frame(true); |
| 566 | 566 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 581 DCHECK(task_runner_->BelongsToCurrentThread()); | 581 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 582 DCHECK(read_cb_.is_null()) << "There should be no pending read"; | 582 DCHECK(read_cb_.is_null()) << "There should be no pending read"; |
| 583 | 583 |
| 584 // H264 and AAC require that we resend the header after flush. | 584 // H264 and AAC require that we resend the header after flush. |
| 585 // Reset bitstream for converter to do so. | 585 // Reset bitstream for converter to do so. |
| 586 // This is related to chromium issue 140371 (http://crbug.com/140371). | 586 // This is related to chromium issue 140371 (http://crbug.com/140371). |
| 587 ResetBitstreamConverter(); | 587 ResetBitstreamConverter(); |
| 588 | 588 |
| 589 buffer_queue_.Clear(); | 589 buffer_queue_.Clear(); |
| 590 end_of_stream_ = false; | 590 end_of_stream_ = false; |
| 591 last_packet_timestamp_ = kNoTimestamp(); | 591 last_packet_timestamp_ = kNoTimestamp; |
| 592 last_packet_duration_ = kNoTimestamp(); | 592 last_packet_duration_ = kNoTimestamp; |
| 593 } | 593 } |
| 594 | 594 |
| 595 void FFmpegDemuxerStream::Stop() { | 595 void FFmpegDemuxerStream::Stop() { |
| 596 DCHECK(task_runner_->BelongsToCurrentThread()); | 596 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 597 buffer_queue_.Clear(); | 597 buffer_queue_.Clear(); |
| 598 if (!read_cb_.is_null()) { | 598 if (!read_cb_.is_null()) { |
| 599 base::ResetAndReturn(&read_cb_).Run( | 599 base::ResetAndReturn(&read_cb_).Run( |
| 600 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 600 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| 601 } | 601 } |
| 602 demuxer_ = NULL; | 602 demuxer_ = NULL; |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 794 const AVDictionaryEntry* entry = | 794 const AVDictionaryEntry* entry = |
| 795 av_dict_get(stream_->metadata, key, NULL, 0); | 795 av_dict_get(stream_->metadata, key, NULL, 0); |
| 796 return (entry == NULL || entry->value == NULL) ? "" : entry->value; | 796 return (entry == NULL || entry->value == NULL) ? "" : entry->value; |
| 797 } | 797 } |
| 798 | 798 |
| 799 // static | 799 // static |
| 800 base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp( | 800 base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp( |
| 801 const AVRational& time_base, | 801 const AVRational& time_base, |
| 802 int64_t timestamp) { | 802 int64_t timestamp) { |
| 803 if (timestamp == static_cast<int64_t>(AV_NOPTS_VALUE)) | 803 if (timestamp == static_cast<int64_t>(AV_NOPTS_VALUE)) |
| 804 return kNoTimestamp(); | 804 return kNoTimestamp; |
| 805 | 805 |
| 806 return ConvertFromTimeBase(time_base, timestamp); | 806 return ConvertFromTimeBase(time_base, timestamp); |
| 807 } | 807 } |
| 808 | 808 |
| 809 // | 809 // |
| 810 // FFmpegDemuxer | 810 // FFmpegDemuxer |
| 811 // | 811 // |
| 812 FFmpegDemuxer::FFmpegDemuxer( | 812 FFmpegDemuxer::FFmpegDemuxer( |
| 813 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 813 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 814 DataSource* data_source, | 814 DataSource* data_source, |
| 815 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | 815 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, |
| 816 const MediaTracksUpdatedCB& media_tracks_updated_cb, | 816 const MediaTracksUpdatedCB& media_tracks_updated_cb, |
| 817 const scoped_refptr<MediaLog>& media_log) | 817 const scoped_refptr<MediaLog>& media_log) |
| 818 : host_(NULL), | 818 : host_(NULL), |
| 819 task_runner_(task_runner), | 819 task_runner_(task_runner), |
| 820 blocking_thread_("FFmpegDemuxer"), | 820 blocking_thread_("FFmpegDemuxer"), |
| 821 pending_read_(false), | 821 pending_read_(false), |
| 822 pending_seek_(false), | 822 pending_seek_(false), |
| 823 data_source_(data_source), | 823 data_source_(data_source), |
| 824 media_log_(media_log), | 824 media_log_(media_log), |
| 825 bitrate_(0), | 825 bitrate_(0), |
| 826 start_time_(kNoTimestamp()), | 826 start_time_(kNoTimestamp), |
| 827 preferred_stream_for_seeking_(-1, kNoTimestamp()), | 827 preferred_stream_for_seeking_(-1, kNoTimestamp), |
| 828 fallback_stream_for_seeking_(-1, kNoTimestamp()), | 828 fallback_stream_for_seeking_(-1, kNoTimestamp), |
| 829 text_enabled_(false), | 829 text_enabled_(false), |
| 830 duration_known_(false), | 830 duration_known_(false), |
| 831 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), | 831 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), |
| 832 media_tracks_updated_cb_(media_tracks_updated_cb), | 832 media_tracks_updated_cb_(media_tracks_updated_cb), |
| 833 weak_factory_(this) { | 833 weak_factory_(this) { |
| 834 DCHECK(task_runner_.get()); | 834 DCHECK(task_runner_.get()); |
| 835 DCHECK(data_source_); | 835 DCHECK(data_source_); |
| 836 DCHECK(!media_tracks_updated_cb_.is_null()); | 836 DCHECK(!media_tracks_updated_cb_.is_null()); |
| 837 } | 837 } |
| 838 | 838 |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 943 | 943 |
| 944 // Choose the seeking stream based on whether it contains the seek time, if no | 944 // Choose the seeking stream based on whether it contains the seek time, if no |
| 945 // match can be found prefer the preferred stream. | 945 // match can be found prefer the preferred stream. |
| 946 // | 946 // |
| 947 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a | 947 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a |
| 948 // given container will demux all packets after the seek point. Instead it | 948 // given container will demux all packets after the seek point. Instead it |
| 949 // only guarantees that all packets after the file position of the seek will | 949 // only guarantees that all packets after the file position of the seek will |
| 950 // be demuxed. It's an open question whether FFmpeg should fix this: | 950 // be demuxed. It's an open question whether FFmpeg should fix this: |
| 951 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html | 951 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html |
| 952 // Tracked by http://crbug.com/387996. | 952 // Tracked by http://crbug.com/387996. |
| 953 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); | 953 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp); |
| 954 const int stream_index = | 954 const int stream_index = |
| 955 seek_time < preferred_stream_for_seeking_.second && | 955 seek_time < preferred_stream_for_seeking_.second && |
| 956 seek_time >= fallback_stream_for_seeking_.second | 956 seek_time >= fallback_stream_for_seeking_.second |
| 957 ? fallback_stream_for_seeking_.first | 957 ? fallback_stream_for_seeking_.first |
| 958 : preferred_stream_for_seeking_.first; | 958 : preferred_stream_for_seeking_.first; |
| 959 DCHECK_NE(stream_index, -1); | 959 DCHECK_NE(stream_index, -1); |
| 960 | 960 |
| 961 const AVStream* seeking_stream = | 961 const AVStream* seeking_stream = |
| 962 glue_->format_context()->streams[stream_index]; | 962 glue_->format_context()->streams[stream_index]; |
| 963 | 963 |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1071 int bitrate = 0; | 1071 int bitrate = 0; |
| 1072 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 1072 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 1073 AVCodecContext* codec_context = format_context->streams[i]->codec; | 1073 AVCodecContext* codec_context = format_context->streams[i]->codec; |
| 1074 bitrate += codec_context->bit_rate; | 1074 bitrate += codec_context->bit_rate; |
| 1075 } | 1075 } |
| 1076 if (bitrate > 0) | 1076 if (bitrate > 0) |
| 1077 return bitrate; | 1077 return bitrate; |
| 1078 | 1078 |
| 1079 // See if we can approximate the bitrate as long as we have a filesize and | 1079 // See if we can approximate the bitrate as long as we have a filesize and |
| 1080 // valid duration. | 1080 // valid duration. |
| 1081 if (duration.InMicroseconds() <= 0 || | 1081 if (duration.InMicroseconds() <= 0 || duration == kInfiniteDuration || |
| 1082 duration == kInfiniteDuration() || | |
| 1083 filesize_in_bytes == 0) { | 1082 filesize_in_bytes == 0) { |
| 1084 return 0; | 1083 return 0; |
| 1085 } | 1084 } |
| 1086 | 1085 |
| 1087 // Do math in floating point as we'd overflow an int64_t if the filesize was | 1086 // Do math in floating point as we'd overflow an int64_t if the filesize was |
| 1088 // larger than ~1073GB. | 1087 // larger than ~1073GB. |
| 1089 double bytes = filesize_in_bytes; | 1088 double bytes = filesize_in_bytes; |
| 1090 double duration_us = duration.InMicroseconds(); | 1089 double duration_us = duration.InMicroseconds(); |
| 1091 return bytes * 8000000.0 / duration_us; | 1090 return bytes * 8000000.0 / duration_us; |
| 1092 } | 1091 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1141 AVFormatContext* format_context = glue_->format_context(); | 1140 AVFormatContext* format_context = glue_->format_context(); |
| 1142 streams_.resize(format_context->nb_streams); | 1141 streams_.resize(format_context->nb_streams); |
| 1143 | 1142 |
| 1144 // Estimate the start time for each stream by looking through the packets | 1143 // Estimate the start time for each stream by looking through the packets |
| 1145 // buffered during avformat_find_stream_info(). These values will be | 1144 // buffered during avformat_find_stream_info(). These values will be |
| 1146 // considered later when determining the actual stream start time. | 1145 // considered later when determining the actual stream start time. |
| 1147 // | 1146 // |
| 1148 // These packets haven't been completely processed yet, so only look through | 1147 // These packets haven't been completely processed yet, so only look through |
| 1149 // these values if the AVFormatContext has a valid start time. | 1148 // these values if the AVFormatContext has a valid start time. |
| 1150 // | 1149 // |
| 1151 // If no estimate is found, the stream entry will be kInfiniteDuration(). | 1150 // If no estimate is found, the stream entry will be kInfiniteDuration. |
| 1152 std::vector<base::TimeDelta> start_time_estimates(format_context->nb_streams, | 1151 std::vector<base::TimeDelta> start_time_estimates(format_context->nb_streams, |
| 1153 kInfiniteDuration()); | 1152 kInfiniteDuration); |
| 1154 const AVFormatInternal* internal = format_context->internal; | 1153 const AVFormatInternal* internal = format_context->internal; |
| 1155 if (internal && internal->packet_buffer && | 1154 if (internal && internal->packet_buffer && |
| 1156 format_context->start_time != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 1155 format_context->start_time != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 1157 struct AVPacketList* packet_buffer = internal->packet_buffer; | 1156 struct AVPacketList* packet_buffer = internal->packet_buffer; |
| 1158 while (packet_buffer != internal->packet_buffer_end) { | 1157 while (packet_buffer != internal->packet_buffer_end) { |
| 1159 DCHECK_LT(static_cast<size_t>(packet_buffer->pkt.stream_index), | 1158 DCHECK_LT(static_cast<size_t>(packet_buffer->pkt.stream_index), |
| 1160 start_time_estimates.size()); | 1159 start_time_estimates.size()); |
| 1161 const AVStream* stream = | 1160 const AVStream* stream = |
| 1162 format_context->streams[packet_buffer->pkt.stream_index]; | 1161 format_context->streams[packet_buffer->pkt.stream_index]; |
| 1163 if (packet_buffer->pkt.pts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 1162 if (packet_buffer->pkt.pts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 1164 const base::TimeDelta packet_pts = | 1163 const base::TimeDelta packet_pts = |
| 1165 ConvertFromTimeBase(stream->time_base, packet_buffer->pkt.pts); | 1164 ConvertFromTimeBase(stream->time_base, packet_buffer->pkt.pts); |
| 1166 if (packet_pts < start_time_estimates[stream->index]) | 1165 if (packet_pts < start_time_estimates[stream->index]) |
| 1167 start_time_estimates[stream->index] = packet_pts; | 1166 start_time_estimates[stream->index] = packet_pts; |
| 1168 } | 1167 } |
| 1169 packet_buffer = packet_buffer->next; | 1168 packet_buffer = packet_buffer->next; |
| 1170 } | 1169 } |
| 1171 } | 1170 } |
| 1172 | 1171 |
| 1173 std::unique_ptr<MediaTracks> media_tracks(new MediaTracks()); | 1172 std::unique_ptr<MediaTracks> media_tracks(new MediaTracks()); |
| 1174 AVStream* audio_stream = NULL; | 1173 AVStream* audio_stream = NULL; |
| 1175 AudioDecoderConfig audio_config; | 1174 AudioDecoderConfig audio_config; |
| 1176 AVStream* video_stream = NULL; | 1175 AVStream* video_stream = NULL; |
| 1177 VideoDecoderConfig video_config; | 1176 VideoDecoderConfig video_config; |
| 1178 | 1177 |
| 1179 DCHECK(track_id_to_demux_stream_map_.empty()); | 1178 DCHECK(track_id_to_demux_stream_map_.empty()); |
| 1180 | 1179 |
| 1181 // If available, |start_time_| will be set to the lowest stream start time. | 1180 // If available, |start_time_| will be set to the lowest stream start time. |
| 1182 start_time_ = kInfiniteDuration(); | 1181 start_time_ = kInfiniteDuration; |
| 1183 | 1182 |
| 1184 base::TimeDelta max_duration; | 1183 base::TimeDelta max_duration; |
| 1185 int detected_audio_track_count = 0; | 1184 int detected_audio_track_count = 0; |
| 1186 int detected_video_track_count = 0; | 1185 int detected_video_track_count = 0; |
| 1187 int detected_text_track_count = 0; | 1186 int detected_text_track_count = 0; |
| 1188 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 1187 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 1189 AVStream* stream = format_context->streams[i]; | 1188 AVStream* stream = format_context->streams[i]; |
| 1190 const AVCodecContext* codec_context = stream->codec; | 1189 const AVCodecContext* codec_context = stream->codec; |
| 1191 const AVMediaType codec_type = codec_context->codec_type; | 1190 const AVMediaType codec_type = codec_context->codec_type; |
| 1192 | 1191 |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1309 media_track->set_id(base::UintToString(track_id)); | 1308 media_track->set_id(base::UintToString(track_id)); |
| 1310 DCHECK(track_id_to_demux_stream_map_.find(media_track->id()) == | 1309 DCHECK(track_id_to_demux_stream_map_.find(media_track->id()) == |
| 1311 track_id_to_demux_stream_map_.end()); | 1310 track_id_to_demux_stream_map_.end()); |
| 1312 track_id_to_demux_stream_map_[media_track->id()] = streams_[i]; | 1311 track_id_to_demux_stream_map_[media_track->id()] = streams_[i]; |
| 1313 } | 1312 } |
| 1314 | 1313 |
| 1315 max_duration = std::max(max_duration, streams_[i]->duration()); | 1314 max_duration = std::max(max_duration, streams_[i]->duration()); |
| 1316 | 1315 |
| 1317 const base::TimeDelta start_time = | 1316 const base::TimeDelta start_time = |
| 1318 ExtractStartTime(stream, start_time_estimates[i]); | 1317 ExtractStartTime(stream, start_time_estimates[i]); |
| 1319 const bool has_start_time = start_time != kNoTimestamp(); | 1318 const bool has_start_time = start_time != kNoTimestamp; |
| 1320 | 1319 |
| 1321 // Always prefer the video stream for seeking. If none exists, we'll swap | 1320 // Always prefer the video stream for seeking. If none exists, we'll swap |
| 1322 // the fallback stream with the preferred stream below. | 1321 // the fallback stream with the preferred stream below. |
| 1323 if (codec_type == AVMEDIA_TYPE_VIDEO) { | 1322 if (codec_type == AVMEDIA_TYPE_VIDEO) { |
| 1324 preferred_stream_for_seeking_ = | 1323 preferred_stream_for_seeking_ = |
| 1325 StreamSeekInfo(i, has_start_time ? start_time : base::TimeDelta()); | 1324 StreamSeekInfo(i, has_start_time ? start_time : base::TimeDelta()); |
| 1326 } | 1325 } |
| 1327 | 1326 |
| 1328 if (!has_start_time) | 1327 if (!has_start_time) |
| 1329 continue; | 1328 continue; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1353 | 1352 |
| 1354 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 1353 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 1355 // If there is a duration value in the container use that to find the | 1354 // If there is a duration value in the container use that to find the |
| 1356 // maximum between it and the duration from A/V streams. | 1355 // maximum between it and the duration from A/V streams. |
| 1357 const AVRational av_time_base = {1, AV_TIME_BASE}; | 1356 const AVRational av_time_base = {1, AV_TIME_BASE}; |
| 1358 max_duration = | 1357 max_duration = |
| 1359 std::max(max_duration, | 1358 std::max(max_duration, |
| 1360 ConvertFromTimeBase(av_time_base, format_context->duration)); | 1359 ConvertFromTimeBase(av_time_base, format_context->duration)); |
| 1361 } else { | 1360 } else { |
| 1362 // The duration is unknown, in which case this is likely a live stream. | 1361 // The duration is unknown, in which case this is likely a live stream. |
| 1363 max_duration = kInfiniteDuration(); | 1362 max_duration = kInfiniteDuration; |
| 1364 } | 1363 } |
| 1365 | 1364 |
| 1366 // FFmpeg represents audio data marked as before the beginning of stream as | 1365 // FFmpeg represents audio data marked as before the beginning of stream as |
| 1367 // having negative timestamps. This data must be discarded after it has been | 1366 // having negative timestamps. This data must be discarded after it has been |
| 1368 // decoded, not before since it is used to warmup the decoder. There are | 1367 // decoded, not before since it is used to warmup the decoder. There are |
| 1369 // currently two known cases for this: vorbis in ogg and opus in ogg and webm. | 1368 // currently two known cases for this: vorbis in ogg and opus in ogg and webm. |
| 1370 // | 1369 // |
| 1371 // For API clarity, it was decided that the rest of the media pipeline should | 1370 // For API clarity, it was decided that the rest of the media pipeline should |
| 1372 // not be exposed to negative timestamps. Which means we need to rebase these | 1371 // not be exposed to negative timestamps. Which means we need to rebase these |
| 1373 // negative timestamps and mark them for discard post decoding. | 1372 // negative timestamps and mark them for discard post decoding. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1391 // because it has a lower starting time. | 1390 // because it has a lower starting time. |
| 1392 if (fallback_stream_for_seeking_.first == audio_stream->index && | 1391 if (fallback_stream_for_seeking_.first == audio_stream->index && |
| 1393 fallback_stream_for_seeking_.second < base::TimeDelta()) { | 1392 fallback_stream_for_seeking_.second < base::TimeDelta()) { |
| 1394 fallback_stream_for_seeking_.second = base::TimeDelta(); | 1393 fallback_stream_for_seeking_.second = base::TimeDelta(); |
| 1395 } | 1394 } |
| 1396 } | 1395 } |
| 1397 | 1396 |
| 1398 // If no start time could be determined, default to zero and prefer the video | 1397 // If no start time could be determined, default to zero and prefer the video |
| 1399 // stream over the audio stream for seeking. E.g., The WAV demuxer does not | 1398 // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
| 1400 // put timestamps on its frames. | 1399 // put timestamps on its frames. |
| 1401 if (start_time_ == kInfiniteDuration()) { | 1400 if (start_time_ == kInfiniteDuration) { |
| 1402 start_time_ = base::TimeDelta(); | 1401 start_time_ = base::TimeDelta(); |
| 1403 preferred_stream_for_seeking_ = StreamSeekInfo( | 1402 preferred_stream_for_seeking_ = StreamSeekInfo( |
| 1404 video_stream ? video_stream->index : audio_stream->index, start_time_); | 1403 video_stream ? video_stream->index : audio_stream->index, start_time_); |
| 1405 } else if (!video_stream) { | 1404 } else if (!video_stream) { |
| 1406 // If no video stream exists, use the audio or text stream found above. | 1405 // If no video stream exists, use the audio or text stream found above. |
| 1407 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; | 1406 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; |
| 1408 } | 1407 } |
| 1409 | 1408 |
| 1410 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS | 1409 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS |
| 1411 // generation so we always get timestamps, see http://crbug.com/169570 | 1410 // generation so we always get timestamps, see http://crbug.com/169570 |
| 1412 if (strcmp(format_context->iformat->name, "avi") == 0) | 1411 if (strcmp(format_context->iformat->name, "avi") == 0) |
| 1413 format_context->flags |= AVFMT_FLAG_GENPTS; | 1412 format_context->flags |= AVFMT_FLAG_GENPTS; |
| 1414 | 1413 |
| 1415 // For testing purposes, don't overwrite the timeline offset if set already. | 1414 // For testing purposes, don't overwrite the timeline offset if set already. |
| 1416 if (timeline_offset_.is_null()) | 1415 if (timeline_offset_.is_null()) |
| 1417 timeline_offset_ = ExtractTimelineOffset(format_context); | 1416 timeline_offset_ = ExtractTimelineOffset(format_context); |
| 1418 | 1417 |
| 1419 // Since we're shifting the externally visible start time to zero, we need to | 1418 // Since we're shifting the externally visible start time to zero, we need to |
| 1420 // adjust the timeline offset to compensate. | 1419 // adjust the timeline offset to compensate. |
| 1421 if (!timeline_offset_.is_null() && start_time_ < base::TimeDelta()) | 1420 if (!timeline_offset_.is_null() && start_time_ < base::TimeDelta()) |
| 1422 timeline_offset_ += start_time_; | 1421 timeline_offset_ += start_time_; |
| 1423 | 1422 |
| 1424 if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) { | 1423 if (max_duration == kInfiniteDuration && !timeline_offset_.is_null()) { |
| 1425 SetLiveness(DemuxerStream::LIVENESS_LIVE); | 1424 SetLiveness(DemuxerStream::LIVENESS_LIVE); |
| 1426 } else if (max_duration != kInfiniteDuration()) { | 1425 } else if (max_duration != kInfiniteDuration) { |
| 1427 SetLiveness(DemuxerStream::LIVENESS_RECORDED); | 1426 SetLiveness(DemuxerStream::LIVENESS_RECORDED); |
| 1428 } else { | 1427 } else { |
| 1429 SetLiveness(DemuxerStream::LIVENESS_UNKNOWN); | 1428 SetLiveness(DemuxerStream::LIVENESS_UNKNOWN); |
| 1430 } | 1429 } |
| 1431 | 1430 |
| 1432 // Good to go: set the duration and bitrate and notify we're done | 1431 // Good to go: set the duration and bitrate and notify we're done |
| 1433 // initializing. | 1432 // initializing. |
| 1434 host_->SetDuration(max_duration); | 1433 host_->SetDuration(max_duration); |
| 1435 duration_known_ = (max_duration != kInfiniteDuration()); | 1434 duration_known_ = (max_duration != kInfiniteDuration); |
| 1436 | 1435 |
| 1437 int64_t filesize_in_bytes = 0; | 1436 int64_t filesize_in_bytes = 0; |
| 1438 url_protocol_->GetSize(&filesize_in_bytes); | 1437 url_protocol_->GetSize(&filesize_in_bytes); |
| 1439 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); | 1438 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); |
| 1440 if (bitrate_ > 0) | 1439 if (bitrate_ > 0) |
| 1441 data_source_->SetBitrate(bitrate_); | 1440 data_source_->SetBitrate(bitrate_); |
| 1442 | 1441 |
| 1443 // Use a single MediaLogEvent to batch all parameter updates at once; this | 1442 // Use a single MediaLogEvent to batch all parameter updates at once; this |
| 1444 // prevents throttling of events due to the large number of updates here. | 1443 // prevents throttling of events due to the large number of updates here. |
| 1445 std::unique_ptr<MediaLogEvent> metadata_event = | 1444 std::unique_ptr<MediaLogEvent> metadata_event = |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1599 if (!duration_known_) { | 1598 if (!duration_known_) { |
| 1600 base::TimeDelta max_duration; | 1599 base::TimeDelta max_duration; |
| 1601 | 1600 |
| 1602 for (StreamVector::iterator iter = streams_.begin(); | 1601 for (StreamVector::iterator iter = streams_.begin(); |
| 1603 iter != streams_.end(); | 1602 iter != streams_.end(); |
| 1604 ++iter) { | 1603 ++iter) { |
| 1605 if (!*iter) | 1604 if (!*iter) |
| 1606 continue; | 1605 continue; |
| 1607 | 1606 |
| 1608 base::TimeDelta duration = (*iter)->GetElapsedTime(); | 1607 base::TimeDelta duration = (*iter)->GetElapsedTime(); |
| 1609 if (duration != kNoTimestamp() && duration > max_duration) | 1608 if (duration != kNoTimestamp && duration > max_duration) |
| 1610 max_duration = duration; | 1609 max_duration = duration; |
| 1611 } | 1610 } |
| 1612 | 1611 |
| 1613 if (max_duration > base::TimeDelta()) { | 1612 if (max_duration > base::TimeDelta()) { |
| 1614 host_->SetDuration(max_duration); | 1613 host_->SetDuration(max_duration); |
| 1615 duration_known_ = true; | 1614 duration_known_ = true; |
| 1616 } | 1615 } |
| 1617 } | 1616 } |
| 1618 // If we have reached the end of stream, tell the downstream filters about | 1617 // If we have reached the end of stream, tell the downstream filters about |
| 1619 // the event. | 1618 // the event. |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1697 | 1696 |
| 1698 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { | 1697 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { |
| 1699 DCHECK(task_runner_->BelongsToCurrentThread()); | 1698 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1700 for (auto* stream : streams_) { | 1699 for (auto* stream : streams_) { |
| 1701 if (stream) | 1700 if (stream) |
| 1702 stream->SetLiveness(liveness); | 1701 stream->SetLiveness(liveness); |
| 1703 } | 1702 } |
| 1704 } | 1703 } |
| 1705 | 1704 |
| 1706 } // namespace media | 1705 } // namespace media |
| OLD | NEW |