Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: media/filters/ffmpeg_demuxer.cc

Issue 2281843002: Refactor stream selection for seeking in FFmpegDemuxer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 // FFmpegDemuxerStream 257 // FFmpegDemuxerStream
258 // 258 //
259 FFmpegDemuxerStream::FFmpegDemuxerStream( 259 FFmpegDemuxerStream::FFmpegDemuxerStream(
260 FFmpegDemuxer* demuxer, 260 FFmpegDemuxer* demuxer,
261 AVStream* stream, 261 AVStream* stream,
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 start_time_(kNoTimestamp),
267 audio_config_(audio_config.release()), 268 audio_config_(audio_config.release()),
268 video_config_(video_config.release()), 269 video_config_(video_config.release()),
269 type_(UNKNOWN), 270 type_(UNKNOWN),
270 liveness_(LIVENESS_UNKNOWN), 271 liveness_(LIVENESS_UNKNOWN),
271 end_of_stream_(false), 272 end_of_stream_(false),
272 last_packet_timestamp_(kNoTimestamp), 273 last_packet_timestamp_(kNoTimestamp),
273 last_packet_duration_(kNoTimestamp), 274 last_packet_duration_(kNoTimestamp),
274 video_rotation_(VIDEO_ROTATION_0), 275 video_rotation_(VIDEO_ROTATION_0),
275 is_enabled_(true), 276 is_enabled_(true),
276 waiting_for_keyframe_(false), 277 waiting_for_keyframe_(false),
(...skipping 541 matching lines...) Expand 10 before | Expand all | Expand 10 after
818 const scoped_refptr<MediaLog>& media_log) 819 const scoped_refptr<MediaLog>& media_log)
819 : host_(NULL), 820 : host_(NULL),
820 task_runner_(task_runner), 821 task_runner_(task_runner),
821 blocking_thread_("FFmpegDemuxer"), 822 blocking_thread_("FFmpegDemuxer"),
822 pending_read_(false), 823 pending_read_(false),
823 pending_seek_(false), 824 pending_seek_(false),
824 data_source_(data_source), 825 data_source_(data_source),
825 media_log_(media_log), 826 media_log_(media_log),
826 bitrate_(0), 827 bitrate_(0),
827 start_time_(kNoTimestamp), 828 start_time_(kNoTimestamp),
828 preferred_stream_for_seeking_(-1, kNoTimestamp),
829 fallback_stream_for_seeking_(-1, kNoTimestamp),
830 text_enabled_(false), 829 text_enabled_(false),
831 duration_known_(false), 830 duration_known_(false),
832 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), 831 encrypted_media_init_data_cb_(encrypted_media_init_data_cb),
833 media_tracks_updated_cb_(media_tracks_updated_cb), 832 media_tracks_updated_cb_(media_tracks_updated_cb),
834 weak_factory_(this) { 833 weak_factory_(this) {
835 DCHECK(task_runner_.get()); 834 DCHECK(task_runner_.get());
836 DCHECK(data_source_); 835 DCHECK(data_source_);
837 DCHECK(!media_tracks_updated_cb_.is_null()); 836 DCHECK(!media_tracks_updated_cb_.is_null());
838 } 837 }
839 838
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
944 943
945 // 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
946 // match can be found prefer the preferred stream. 945 // match can be found prefer the preferred stream.
947 // 946 //
948 // 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
949 // 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
950 // 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
951 // 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:
952 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html 951 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
953 // Tracked by http://crbug.com/387996. 952 // Tracked by http://crbug.com/387996.
954 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp); 953 FFmpegDemuxerStream* demux_stream = FindPreferredStreamForSeeking(seek_time);
955 const int stream_index = 954 DCHECK(demux_stream);
956 seek_time < preferred_stream_for_seeking_.second && 955 const AVStream* seeking_stream = demux_stream->av_stream();
957 seek_time >= fallback_stream_for_seeking_.second 956 DCHECK(seeking_stream);
958 ? fallback_stream_for_seeking_.first
959 : preferred_stream_for_seeking_.first;
960 DCHECK_NE(stream_index, -1);
961
962 const AVStream* seeking_stream =
963 glue_->format_context()->streams[stream_index];
964 957
965 pending_seek_ = true; 958 pending_seek_ = true;
966 base::PostTaskAndReplyWithResult( 959 base::PostTaskAndReplyWithResult(
967 blocking_thread_.task_runner().get(), 960 blocking_thread_.task_runner().get(),
968 FROM_HERE, 961 FROM_HERE,
969 base::Bind(&av_seek_frame, 962 base::Bind(&av_seek_frame,
970 glue_->format_context(), 963 glue_->format_context(),
971 seeking_stream->index, 964 seeking_stream->index,
972 ConvertToTimeBase(seeking_stream->time_base, seek_time), 965 ConvertToTimeBase(seeking_stream->time_base, seek_time),
973 // Always seek to a timestamp <= to the desired timestamp. 966 // Always seek to a timestamp <= to the desired timestamp.
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after
1311 track_id_to_demux_stream_map_.end()); 1304 track_id_to_demux_stream_map_.end());
1312 track_id_to_demux_stream_map_[media_track->id()] = streams_[i]; 1305 track_id_to_demux_stream_map_[media_track->id()] = streams_[i];
1313 } 1306 }
1314 1307
1315 max_duration = std::max(max_duration, streams_[i]->duration()); 1308 max_duration = std::max(max_duration, streams_[i]->duration());
1316 1309
1317 const base::TimeDelta start_time = 1310 const base::TimeDelta start_time =
1318 ExtractStartTime(stream, start_time_estimates[i]); 1311 ExtractStartTime(stream, start_time_estimates[i]);
1319 const bool has_start_time = start_time != kNoTimestamp; 1312 const bool has_start_time = start_time != kNoTimestamp;
1320 1313
1321 // Always prefer the video stream for seeking. If none exists, we'll swap
1322 // the fallback stream with the preferred stream below.
1323 if (codec_type == AVMEDIA_TYPE_VIDEO) {
1324 preferred_stream_for_seeking_ =
1325 StreamSeekInfo(i, has_start_time ? start_time : base::TimeDelta());
1326 }
1327
1328 if (!has_start_time) 1314 if (!has_start_time)
1329 continue; 1315 continue;
1330 1316
1317 streams_[i]->set_start_time(start_time);
1331 if (start_time < start_time_) { 1318 if (start_time < start_time_) {
1332 start_time_ = start_time; 1319 start_time_ = start_time;
1333
1334 // Choose the stream with the lowest starting time as the fallback stream
1335 // for seeking. Video should always be preferred.
1336 fallback_stream_for_seeking_ = StreamSeekInfo(i, start_time);
1337 } 1320 }
1338 } 1321 }
1339 1322
1340 RecordDetectedTrackTypeStats(detected_audio_track_count, 1323 RecordDetectedTrackTypeStats(detected_audio_track_count,
1341 detected_video_track_count, 1324 detected_video_track_count,
1342 detected_text_track_count); 1325 detected_text_track_count);
1343 1326
1344 if (!audio_stream && !video_stream) { 1327 if (!audio_stream && !video_stream) {
1345 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() 1328 MEDIA_LOG(ERROR, media_log_) << GetDisplayName()
1346 << ": no supported streams"; 1329 << ": no supported streams";
(...skipping 29 matching lines...) Expand all
1376 // in section A.2 in the Ogg Vorbis spec: 1359 // in section A.2 in the Ogg Vorbis spec:
1377 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html 1360 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
1378 // 1361 //
1379 // FFmpeg's use of negative timestamps for opus pre-skip is nonstandard, but 1362 // FFmpeg's use of negative timestamps for opus pre-skip is nonstandard, but
1380 // for more information on pre-skip see section 4.2 of the Ogg Opus spec: 1363 // for more information on pre-skip see section 4.2 of the Ogg Opus spec:
1381 // https://tools.ietf.org/html/draft-ietf-codec-oggopus-08#section-4.2 1364 // https://tools.ietf.org/html/draft-ietf-codec-oggopus-08#section-4.2
1382 if (audio_stream && (audio_stream->codec->codec_id == AV_CODEC_ID_OPUS || 1365 if (audio_stream && (audio_stream->codec->codec_id == AV_CODEC_ID_OPUS ||
1383 (strcmp(format_context->iformat->name, "ogg") == 0 && 1366 (strcmp(format_context->iformat->name, "ogg") == 0 &&
1384 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS))) { 1367 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS))) {
1385 for (size_t i = 0; i < streams_.size(); ++i) { 1368 for (size_t i = 0; i < streams_.size(); ++i) {
1386 if (streams_[i]) 1369 if (!streams_[i])
1387 streams_[i]->enable_negative_timestamp_fixups(); 1370 continue;
1388 } 1371 streams_[i]->enable_negative_timestamp_fixups();
1389 1372
1390 // Fixup the seeking information to avoid selecting the audio stream simply 1373 // Fixup the seeking information to avoid selecting the audio stream
1391 // because it has a lower starting time. 1374 // simply because it has a lower starting time.
1392 if (fallback_stream_for_seeking_.first == audio_stream->index && 1375 if (streams_[i]->av_stream() == audio_stream &&
1393 fallback_stream_for_seeking_.second < base::TimeDelta()) { 1376 streams_[i]->start_time() < base::TimeDelta()) {
1394 fallback_stream_for_seeking_.second = base::TimeDelta(); 1377 streams_[i]->set_start_time(base::TimeDelta());
1378 }
1395 } 1379 }
1396 } 1380 }
1397 1381
1398 // If no start time could be determined, default to zero and prefer the video 1382 // If no start time could be determined, default to zero.
1399 // stream over the audio stream for seeking. E.g., The WAV demuxer does not
1400 // put timestamps on its frames.
1401 if (start_time_ == kInfiniteDuration) { 1383 if (start_time_ == kInfiniteDuration) {
1402 start_time_ = base::TimeDelta(); 1384 start_time_ = base::TimeDelta();
1403 preferred_stream_for_seeking_ = StreamSeekInfo(
1404 video_stream ? video_stream->index : audio_stream->index, start_time_);
1405 } else if (!video_stream) {
1406 // If no video stream exists, use the audio or text stream found above.
1407 preferred_stream_for_seeking_ = fallback_stream_for_seeking_;
1408 } 1385 }
1409 1386
1410 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS 1387 // 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 1388 // generation so we always get timestamps, see http://crbug.com/169570
1412 if (strcmp(format_context->iformat->name, "avi") == 0) 1389 if (strcmp(format_context->iformat->name, "avi") == 0)
1413 format_context->flags |= AVFMT_FLAG_GENPTS; 1390 format_context->flags |= AVFMT_FLAG_GENPTS;
1414 1391
1415 // For testing purposes, don't overwrite the timeline offset if set already. 1392 // For testing purposes, don't overwrite the timeline offset if set already.
1416 if (timeline_offset_.is_null()) 1393 if (timeline_offset_.is_null())
1417 timeline_offset_ = ExtractTimelineOffset(format_context); 1394 timeline_offset_ = ExtractTimelineOffset(format_context);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1483 SetTimeProperty(metadata_event.get(), "max_duration", max_duration); 1460 SetTimeProperty(metadata_event.get(), "max_duration", max_duration);
1484 SetTimeProperty(metadata_event.get(), "start_time", start_time_); 1461 SetTimeProperty(metadata_event.get(), "start_time", start_time_);
1485 metadata_event->params.SetInteger("bitrate", bitrate_); 1462 metadata_event->params.SetInteger("bitrate", bitrate_);
1486 media_log_->AddEvent(std::move(metadata_event)); 1463 media_log_->AddEvent(std::move(metadata_event));
1487 1464
1488 media_tracks_updated_cb_.Run(std::move(media_tracks)); 1465 media_tracks_updated_cb_.Run(std::move(media_tracks));
1489 1466
1490 status_cb.Run(PIPELINE_OK); 1467 status_cb.Run(PIPELINE_OK);
1491 } 1468 }
1492 1469
1470 FFmpegDemuxerStream* FFmpegDemuxer::FindPreferredStreamForSeeking(
1471 base::TimeDelta seek_time) {
1472 // If we have a selected/enabled video stream and its start time is lower
1473 // than the |seek_time| or unknown, then always prefer it for seeking.
1474 FFmpegDemuxerStream* video_stream = nullptr;
1475 for (const auto& stream : streams_) {
1476 if (stream && stream->type() == DemuxerStream::VIDEO && stream->enabled()) {
1477 video_stream = stream;
1478 break;
1479 }
1480 }
1481 if (video_stream && (video_stream->start_time() == kNoTimestamp ||
DaleCurtis 2016/08/26 18:21:56 Merge into l.1477?
servolk 2016/08/26 19:08:44 Done.
1482 video_stream->start_time() <= seek_time)) {
1483 return video_stream;
1484 }
1485
1486 // If video stream is not present or |seek_time| is lower than the video start
1487 // time, then try to find an enabled stream with the lowest start time.
1488 FFmpegDemuxerStream* lowest_start_time_stream = nullptr;
1489 for (const auto& stream : streams_) {
1490 if (!stream || !stream->enabled() || stream->start_time() == kNoTimestamp)
1491 continue;
1492 if (!lowest_start_time_stream ||
1493 stream->start_time() < lowest_start_time_stream->start_time()) {
1494 lowest_start_time_stream = stream;
1495 }
1496 }
1497 // If we found a stream with start time lower than |seek_time|, then use it.
1498 if (lowest_start_time_stream &&
1499 lowest_start_time_stream->start_time() <= seek_time) {
1500 return lowest_start_time_stream;
1501 }
1502
1503 // If we couldn't find any streams with the start time lower than |seek_time|
1504 // then use either video (if one exists) or any audio stream.
1505 if (video_stream)
DaleCurtis 2016/08/26 18:21:56 Ternary?
servolk 2016/08/26 19:08:44 Done.
1506 return video_stream;
1507 return static_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::AUDIO));
1508 }
1509
1493 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { 1510 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) {
1494 DCHECK(task_runner_->BelongsToCurrentThread()); 1511 DCHECK(task_runner_->BelongsToCurrentThread());
1495 CHECK(pending_seek_); 1512 CHECK(pending_seek_);
1496 pending_seek_ = false; 1513 pending_seek_ = false;
1497 1514
1498 if (!blocking_thread_.IsRunning()) { 1515 if (!blocking_thread_.IsRunning()) {
1499 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; 1516 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state";
1500 cb.Run(PIPELINE_ERROR_ABORT); 1517 cb.Run(PIPELINE_ERROR_ABORT);
1501 return; 1518 return;
1502 } 1519 }
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
1697 1714
1698 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { 1715 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) {
1699 DCHECK(task_runner_->BelongsToCurrentThread()); 1716 DCHECK(task_runner_->BelongsToCurrentThread());
1700 for (auto* stream : streams_) { 1717 for (auto* stream : streams_) {
1701 if (stream) 1718 if (stream)
1702 stream->SetLiveness(liveness); 1719 stream->SetLiveness(liveness);
1703 } 1720 }
1704 } 1721 }
1705 1722
1706 } // namespace media 1723 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698