diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-20 20:05:07 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-20 20:05:07 +0000 |
commit | 1be621f1d1df8231a02e12d8a0b49c0910660469 (patch) | |
tree | d28ce5c03db17487606dbf60a4a64fd14240a366 | |
parent | a5d8fac6d37dec61810f55c6f49790644053e88f (diff) | |
download | chromium_src-1be621f1d1df8231a02e12d8a0b49c0910660469.zip chromium_src-1be621f1d1df8231a02e12d8a0b49c0910660469.tar.gz chromium_src-1be621f1d1df8231a02e12d8a0b49c0910660469.tar.bz2 |
Use av_rescale_q() for converting FFmpeg timestamps to base::TimeDelta (second attempt).
Previously we were using integer math to convert to microseconds, but depending on the frame rate and packet size we could introduce enough error that could accumulate and introduce audio/video synchronization drift. av_rescale_q() is a simple function but is designed to minimize error as much as possible.
Second attempt since I forgot to manually resolve av_rescale_q() for unittests.
Review URL: http://codereview.chromium.org/113619
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16514 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/base/media_posix.cc | 11 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.cc | 30 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.h | 4 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 20 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 10 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 6 | ||||
-rw-r--r-- | third_party/ffmpeg/avutil-50.def | 1 |
7 files changed, 64 insertions, 18 deletions
diff --git a/media/base/media_posix.cc b/media/base/media_posix.cc index 4bddf6d..94c1a89 100644 --- a/media/base/media_posix.cc +++ b/media/base/media_posix.cc @@ -122,6 +122,11 @@ void av_free(void* ptr) { return av_free_ptr(ptr); } +int64_t (*av_rescale_q_ptr)(int64_t a, AVRational bq, AVRational cq) = NULL; +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) { + return av_rescale_q_ptr(a, bq, cq); +} + } // extern "C" @@ -240,6 +245,9 @@ bool InitializeMediaLibrary(const FilePath& module_dir) { av_free_ptr = reinterpret_cast<void (*)(void*)>( dlsym(libs[FILE_LIBAVUTIL], "av_free")); + av_rescale_q_ptr = + reinterpret_cast<int64_t (*)(int64_t, AVRational, AVRational)>( + dlsym(libs[FILE_LIBAVUTIL], "av_rescale_q")); // Check that all the symbols were loaded correctly before returning true. if (av_get_bits_per_sample_format_ptr && @@ -259,7 +267,8 @@ bool InitializeMediaLibrary(const FilePath& module_dir) { av_register_protocol_ptr && av_malloc_ptr && - av_free_ptr) { + av_free_ptr && + av_rescale_q_ptr) { return true; } diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc index 77b602d..e44880a 100644 --- a/media/filters/ffmpeg_audio_decoder.cc +++ b/media/filters/ffmpeg_audio_decoder.cc @@ -36,11 +36,16 @@ bool FFmpegAudioDecoder::OnInitialize(DemuxerStream* demuxer_stream) { QueryInterface<FFmpegDemuxerStream>(&ffmpeg_demuxer_stream)) return false; - // Setting the media format. + // Grab the AVStream's codec context and make sure we have sensible values. + codec_context_ = ffmpeg_demuxer_stream->av_stream()->codec; + DCHECK_GT(codec_context_->channels, 0); + DCHECK_GT(av_get_bits_per_sample_format(codec_context_->sample_fmt), 0); + DCHECK_GT(codec_context_->sample_rate, 0); + + // Set the media format. // TODO(hclam): Reuse the information provided by the demuxer for now, we may // need to wait until the first buffer is decoded to know the correct // information. - codec_context_ = ffmpeg_demuxer_stream->av_stream()->codec; media_format_.SetAsInteger(MediaFormat::kChannels, codec_context_->channels); media_format_.SetAsInteger(MediaFormat::kSampleBits, av_get_bits_per_sample_format(codec_context_->sample_fmt)); @@ -90,10 +95,29 @@ void FFmpegAudioDecoder::OnDecode(Buffer* input) { DataBuffer* result_buffer = new DataBuffer(); memcpy(result_buffer->GetWritableData(output_buffer_size), output_buffer, output_buffer_size); + + // Determine the duration if the demuxer couldn't figure it out, otherwise + // copy it over. + if (input->GetDuration().InMicroseconds() == 0) { + result_buffer->SetDuration(CalculateDuration(output_buffer_size)); + } else { + result_buffer->SetDuration(input->GetDuration()); + } + + // Copy over the timestamp. result_buffer->SetTimestamp(input->GetTimestamp()); - result_buffer->SetDuration(input->GetDuration()); + EnqueueResult(result_buffer); } } +base::TimeDelta FFmpegAudioDecoder::CalculateDuration(size_t size) { + int64 denominator = codec_context_->channels * + av_get_bits_per_sample_format(codec_context_->sample_fmt) / 8 * + codec_context_->sample_rate; + double microseconds = size / + (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); + return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); +} + } // namespace diff --git a/media/filters/ffmpeg_audio_decoder.h b/media/filters/ffmpeg_audio_decoder.h index ffb99f8..f30f330 100644 --- a/media/filters/ffmpeg_audio_decoder.h +++ b/media/filters/ffmpeg_audio_decoder.h @@ -34,6 +34,10 @@ class FFmpegAudioDecoder : public DecoderBase<AudioDecoder, Buffer> { FFmpegAudioDecoder(); virtual ~FFmpegAudioDecoder(); + // Calculates the duration of an audio buffer based on the sample rate, + // channels and bits per sample given the size in bytes. + base::TimeDelta CalculateDuration(size_t size); + // A FFmpeg defined structure that holds decoder information, this variable // is initialized in OnInitialize(). AVCodecContext* codec_context_; diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index 077314e..ab3974b 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -51,7 +51,7 @@ class AVPacketBuffer : public Buffer { FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, AVStream* stream) : demuxer_(demuxer), - av_stream_(stream), + stream_(stream), discontinuous_(false) { DCHECK(demuxer_); @@ -70,12 +70,8 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, break; } - // Calculate the time base and duration in microseconds. - int64 time_base_us = static_cast<int64>(av_q2d(stream->time_base) * - base::Time::kMicrosecondsPerSecond); - int64 duration_us = static_cast<int64>(time_base_us * stream->duration); - time_base_ = base::TimeDelta::FromMicroseconds(time_base_us); - duration_ = base::TimeDelta::FromMicroseconds(duration_us); + // Calculate the duration. + duration_ = ConvertTimestamp(stream->duration); } FFmpegDemuxerStream::~FFmpegDemuxerStream() { @@ -106,8 +102,8 @@ bool FFmpegDemuxerStream::HasPendingReads() { } base::TimeDelta FFmpegDemuxerStream::EnqueuePacket(AVPacket* packet) { - base::TimeDelta timestamp = time_base_ * packet->pts; - base::TimeDelta duration = time_base_ * packet->duration; + base::TimeDelta timestamp = ConvertTimestamp(packet->pts); + base::TimeDelta duration = ConvertTimestamp(packet->duration); Buffer* buffer = new AVPacketBuffer(packet, timestamp, duration); DCHECK(buffer); { @@ -166,6 +162,12 @@ bool FFmpegDemuxerStream::FulfillPendingReads() { return pending_reads; } +base::TimeDelta FFmpegDemuxerStream::ConvertTimestamp(int64 timestamp) { + AVRational time_base = { 1, base::Time::kMicrosecondsPerSecond }; + int64 microseconds = av_rescale_q(timestamp, stream_->time_base, time_base); + return base::TimeDelta::FromMicroseconds(microseconds); +} + // // FFmpegDemuxer diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index 9e92cd4..bd785b7 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -70,9 +70,7 @@ class FFmpegDemuxerStream : public DemuxerStream { virtual const MediaFormat& media_format(); virtual void Read(Callback1<Buffer*>::Type* read_callback); - AVStream* av_stream() { - return av_stream_; - } + AVStream* av_stream() const { return stream_; } static const char* interface_id(); @@ -83,10 +81,12 @@ class FFmpegDemuxerStream : public DemuxerStream { // Returns true if there are still pending reads. bool FulfillPendingReads(); + // Converts an FFmpeg stream timestamp into a base::TimeDelta. + base::TimeDelta ConvertTimestamp(int64 timestamp); + FFmpegDemuxer* demuxer_; - AVStream* av_stream_; + AVStream* stream_; MediaFormat media_format_; - base::TimeDelta time_base_; base::TimeDelta duration_; bool discontinuous_; diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index a882c58..f4c989a 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -134,6 +134,12 @@ int av_find_stream_info(AVFormatContext* format) { return g_av_find_stream_info; } +int64 av_rescale_q(int64 a, AVRational bq, AVRational cq) { + int64 num = bq.num * cq.den; + int64 den = cq.num * bq.den; + return a * num / den; +} + void av_free(void* ptr) { if (ptr) { EXPECT_EQ(&g_format, ptr); diff --git a/third_party/ffmpeg/avutil-50.def b/third_party/ffmpeg/avutil-50.def index 23bb5db..79e5ddc 100644 --- a/third_party/ffmpeg/avutil-50.def +++ b/third_party/ffmpeg/avutil-50.def @@ -2,3 +2,4 @@ LIBRARY avutil-50 EXPORTS av_free av_malloc + av_rescale_q |