| 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_audio_decoder.h" | 5 #include "media/filters/ffmpeg_audio_decoder.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/single_thread_task_runner.h" | 10 #include "base/single_thread_task_runner.h" |
| 11 #include "media/base/audio_buffer.h" | 11 #include "media/base/audio_buffer.h" |
| 12 #include "media/base/audio_bus.h" | 12 #include "media/base/audio_bus.h" |
| 13 #include "media/base/audio_decoder_config.h" | 13 #include "media/base/audio_decoder_config.h" |
| 14 #include "media/base/audio_timestamp_helper.h" | 14 #include "media/base/audio_timestamp_helper.h" |
| 15 #include "media/base/bind_to_current_loop.h" | 15 #include "media/base/bind_to_current_loop.h" |
| 16 #include "media/base/decoder_buffer.h" | 16 #include "media/base/decoder_buffer.h" |
| 17 #include "media/base/demuxer.h" | 17 #include "media/base/demuxer.h" |
| 18 #include "media/base/limits.h" | 18 #include "media/base/limits.h" |
| 19 #include "media/base/pipeline.h" | 19 #include "media/base/pipeline.h" |
| 20 #include "media/base/sample_format.h" | 20 #include "media/base/sample_format.h" |
| 21 #include "media/ffmpeg/ffmpeg_common.h" | 21 #include "media/ffmpeg/ffmpeg_common.h" |
| 22 #include "media/filters/ffmpeg_glue.h" | 22 #include "media/filters/ffmpeg_glue.h" |
| 23 | 23 |
| 24 namespace media { | 24 namespace media { |
| 25 | 25 |
| 26 // Helper structure for managing multiple decoded audio frames per packet. | |
| 27 struct QueuedAudioBuffer { | |
| 28 AudioDecoder::Status status; | |
| 29 scoped_refptr<AudioBuffer> buffer; | |
| 30 }; | |
| 31 | |
| 32 // Returns true if the decode result was end of stream. | 26 // Returns true if the decode result was end of stream. |
| 33 static inline bool IsEndOfStream(int result, | 27 static inline bool IsEndOfStream(int result, |
| 34 int decoded_size, | 28 int decoded_size, |
| 35 const scoped_refptr<DecoderBuffer>& input) { | 29 const scoped_refptr<DecoderBuffer>& input) { |
| 36 // Three conditions to meet to declare end of stream for this decoder: | 30 // Three conditions to meet to declare end of stream for this decoder: |
| 37 // 1. FFmpeg didn't read anything. | 31 // 1. FFmpeg didn't read anything. |
| 38 // 2. FFmpeg didn't output anything. | 32 // 2. FFmpeg didn't output anything. |
| 39 // 3. An end of stream buffer is received. | 33 // 3. An end of stream buffer is received. |
| 40 return result == 0 && decoded_size == 0 && input->end_of_stream(); | 34 return result == 0 && decoded_size == 0 && input->end_of_stream(); |
| 41 } | 35 } |
| 42 | 36 |
| 43 // Return the number of channels from the data in |frame|. | 37 // Return the number of channels from the data in |frame|. |
| 44 static inline int DetermineChannels(AVFrame* frame) { | 38 static inline int DetermineChannels(AVFrame* frame) { |
| 45 #if defined(CHROMIUM_NO_AVFRAME_CHANNELS) | 39 #if defined(CHROMIUM_NO_AVFRAME_CHANNELS) |
| 46 // When use_system_ffmpeg==1, libav's AVFrame doesn't have channels field. | 40 // When use_system_ffmpeg==1, libav's AVFrame doesn't have channels field. |
| 47 return av_get_channel_layout_nb_channels(frame->channel_layout); | 41 return av_get_channel_layout_nb_channels(frame->channel_layout); |
| 48 #else | 42 #else |
| 49 return frame->channels; | 43 return frame->channels; |
| 50 #endif | 44 #endif |
| 51 } | 45 } |
| 52 | 46 |
| 53 // Called by FFmpeg's allocation routine to allocate a buffer. Uses | |
| 54 // AVCodecContext.opaque to get the object reference in order to call | |
| 55 // GetAudioBuffer() to do the actual allocation. | |
| 56 static int GetAudioBufferImpl(struct AVCodecContext* s, | |
| 57 AVFrame* frame, | |
| 58 int flags) { | |
| 59 DCHECK(s->codec->capabilities & CODEC_CAP_DR1); | |
| 60 DCHECK_EQ(s->codec_type, AVMEDIA_TYPE_AUDIO); | |
| 61 FFmpegAudioDecoder* decoder = static_cast<FFmpegAudioDecoder*>(s->opaque); | |
| 62 return decoder->GetAudioBuffer(s, frame, flags); | |
| 63 } | |
| 64 | |
| 65 // Called by FFmpeg's allocation routine to free a buffer. |opaque| is the | 47 // Called by FFmpeg's allocation routine to free a buffer. |opaque| is the |
| 66 // AudioBuffer allocated, so unref it. | 48 // AudioBuffer allocated, so unref it. |
| 67 static void ReleaseAudioBufferImpl(void* opaque, uint8* data) { | 49 static void ReleaseAudioBufferImpl(void* opaque, uint8* data) { |
| 68 scoped_refptr<AudioBuffer> buffer; | 50 scoped_refptr<AudioBuffer> buffer; |
| 69 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque)); | 51 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque)); |
| 70 } | 52 } |
| 71 | 53 |
| 72 FFmpegAudioDecoder::FFmpegAudioDecoder( | 54 // Called by FFmpeg's allocation routine to allocate a buffer. Uses |
| 73 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | 55 // AVCodecContext.opaque to get the object reference in order to call |
| 74 : task_runner_(task_runner), | 56 // GetAudioBuffer() to do the actual allocation. |
| 75 weak_factory_(this), | 57 static int GetAudioBuffer(struct AVCodecContext* s, AVFrame* frame, int flags) { |
| 76 demuxer_stream_(NULL), | 58 DCHECK(s->codec->capabilities & CODEC_CAP_DR1); |
| 77 bytes_per_channel_(0), | 59 DCHECK_EQ(s->codec_type, AVMEDIA_TYPE_AUDIO); |
| 78 channel_layout_(CHANNEL_LAYOUT_NONE), | |
| 79 channels_(0), | |
| 80 samples_per_second_(0), | |
| 81 av_sample_format_(0), | |
| 82 last_input_timestamp_(kNoTimestamp()), | |
| 83 output_frames_to_drop_(0) { | |
| 84 } | |
| 85 | 60 |
| 86 void FFmpegAudioDecoder::Initialize( | |
| 87 DemuxerStream* stream, | |
| 88 const PipelineStatusCB& status_cb, | |
| 89 const StatisticsCB& statistics_cb) { | |
| 90 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 91 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | |
| 92 | |
| 93 FFmpegGlue::InitializeFFmpeg(); | |
| 94 | |
| 95 if (demuxer_stream_) { | |
| 96 // TODO(scherkus): initialization currently happens more than once in | |
| 97 // PipelineIntegrationTest.BasicPlayback. | |
| 98 LOG(ERROR) << "Initialize has already been called."; | |
| 99 CHECK(false); | |
| 100 } | |
| 101 | |
| 102 weak_this_ = weak_factory_.GetWeakPtr(); | |
| 103 demuxer_stream_ = stream; | |
| 104 | |
| 105 if (!ConfigureDecoder()) { | |
| 106 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 107 return; | |
| 108 } | |
| 109 | |
| 110 statistics_cb_ = statistics_cb; | |
| 111 initialize_cb.Run(PIPELINE_OK); | |
| 112 } | |
| 113 | |
| 114 void FFmpegAudioDecoder::Read(const ReadCB& read_cb) { | |
| 115 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 116 DCHECK(!read_cb.is_null()); | |
| 117 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 118 DCHECK(reset_cb_.is_null()); | |
| 119 DCHECK(stop_cb_.is_null()); | |
| 120 | |
| 121 read_cb_ = BindToCurrentLoop(read_cb); | |
| 122 | |
| 123 // If we don't have any queued audio from the last packet we decoded, ask for | |
| 124 // more data from the demuxer to satisfy this read. | |
| 125 if (queued_audio_.empty()) { | |
| 126 ReadFromDemuxerStream(); | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 base::ResetAndReturn(&read_cb_).Run( | |
| 131 queued_audio_.front().status, queued_audio_.front().buffer); | |
| 132 queued_audio_.pop_front(); | |
| 133 } | |
| 134 | |
| 135 int FFmpegAudioDecoder::bits_per_channel() { | |
| 136 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 137 return bytes_per_channel_ * 8; | |
| 138 } | |
| 139 | |
| 140 ChannelLayout FFmpegAudioDecoder::channel_layout() { | |
| 141 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 142 return channel_layout_; | |
| 143 } | |
| 144 | |
| 145 int FFmpegAudioDecoder::samples_per_second() { | |
| 146 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 147 return samples_per_second_; | |
| 148 } | |
| 149 | |
| 150 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { | |
| 151 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 152 reset_cb_ = BindToCurrentLoop(closure); | |
| 153 | |
| 154 // A demuxer read is pending, we'll wait until it finishes. | |
| 155 if (!read_cb_.is_null()) | |
| 156 return; | |
| 157 | |
| 158 DoReset(); | |
| 159 } | |
| 160 | |
| 161 void FFmpegAudioDecoder::Stop(const base::Closure& closure) { | |
| 162 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 163 stop_cb_ = BindToCurrentLoop(closure); | |
| 164 | |
| 165 // A demuxer read is pending, we'll wait until it finishes. | |
| 166 if (!read_cb_.is_null()) | |
| 167 return; | |
| 168 | |
| 169 if (!reset_cb_.is_null()) { | |
| 170 DoReset(); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 DoStop(); | |
| 175 } | |
| 176 | |
| 177 FFmpegAudioDecoder::~FFmpegAudioDecoder() {} | |
| 178 | |
| 179 int FFmpegAudioDecoder::GetAudioBuffer(AVCodecContext* codec, | |
| 180 AVFrame* frame, | |
| 181 int flags) { | |
| 182 // Since this routine is called by FFmpeg when a buffer is required for audio | 61 // Since this routine is called by FFmpeg when a buffer is required for audio |
| 183 // data, use the values supplied by FFmpeg (ignoring the current settings). | 62 // data, use the values supplied by FFmpeg (ignoring the current settings). |
| 184 // RunDecodeLoop() gets to determine if the buffer is useable or not. | 63 // FFmpegDecode() gets to determine if the buffer is useable or not. |
| 185 AVSampleFormat format = static_cast<AVSampleFormat>(frame->format); | 64 AVSampleFormat format = static_cast<AVSampleFormat>(frame->format); |
| 186 SampleFormat sample_format = AVSampleFormatToSampleFormat(format); | 65 SampleFormat sample_format = AVSampleFormatToSampleFormat(format); |
| 187 int channels = DetermineChannels(frame); | 66 int channels = DetermineChannels(frame); |
| 188 if ((channels <= 0) || (channels >= limits::kMaxChannels)) { | 67 if ((channels <= 0) || (channels >= limits::kMaxChannels)) { |
| 189 DLOG(ERROR) << "Requested number of channels (" << channels | 68 DLOG(ERROR) << "Requested number of channels (" << channels |
| 190 << ") exceeds limit."; | 69 << ") exceeds limit."; |
| 191 return AVERROR(EINVAL); | 70 return AVERROR(EINVAL); |
| 192 } | 71 } |
| 193 | 72 |
| 194 int bytes_per_channel = SampleFormatToBytesPerChannel(sample_format); | 73 int bytes_per_channel = SampleFormatToBytesPerChannel(sample_format); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 113 |
| 235 // Now create an AVBufferRef for the data just allocated. It will own the | 114 // Now create an AVBufferRef for the data just allocated. It will own the |
| 236 // reference to the AudioBuffer object. | 115 // reference to the AudioBuffer object. |
| 237 void* opaque = NULL; | 116 void* opaque = NULL; |
| 238 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque)); | 117 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque)); |
| 239 frame->buf[0] = av_buffer_create( | 118 frame->buf[0] = av_buffer_create( |
| 240 frame->data[0], buffer_size_in_bytes, ReleaseAudioBufferImpl, opaque, 0); | 119 frame->data[0], buffer_size_in_bytes, ReleaseAudioBufferImpl, opaque, 0); |
| 241 return 0; | 120 return 0; |
| 242 } | 121 } |
| 243 | 122 |
| 244 void FFmpegAudioDecoder::DoStop() { | 123 FFmpegAudioDecoder::FFmpegAudioDecoder( |
| 245 DCHECK(task_runner_->BelongsToCurrentThread()); | 124 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| 246 DCHECK(!stop_cb_.is_null()); | 125 : task_runner_(task_runner), |
| 247 DCHECK(read_cb_.is_null()); | 126 weak_factory_(this), |
| 248 DCHECK(reset_cb_.is_null()); | 127 state_(kUninitialized), |
| 249 | 128 bytes_per_channel_(0), |
| 129 channel_layout_(CHANNEL_LAYOUT_NONE), |
| 130 channels_(0), |
| 131 samples_per_second_(0), |
| 132 av_sample_format_(0), |
| 133 last_input_timestamp_(kNoTimestamp()), |
| 134 output_frames_to_drop_(0) {} |
| 135 |
| 136 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
| 137 DCHECK_EQ(state_, kUninitialized); |
| 138 DCHECK(!codec_context_); |
| 139 DCHECK(!av_frame_); |
| 140 } |
| 141 |
| 142 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, |
| 143 const PipelineStatusCB& status_cb) { |
| 144 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 145 DCHECK(!config.is_encrypted()); |
| 146 |
| 147 FFmpegGlue::InitializeFFmpeg(); |
| 148 weak_this_ = weak_factory_.GetWeakPtr(); |
| 149 |
| 150 config_ = config; |
| 151 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
| 152 |
| 153 if (!config.IsValidConfig() || !ConfigureDecoder()) { |
| 154 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 155 return; |
| 156 } |
| 157 |
| 158 // Success! |
| 159 state_ = kNormal; |
| 160 initialize_cb.Run(PIPELINE_OK); |
| 161 } |
| 162 |
| 163 void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 164 const DecodeCB& decode_cb) { |
| 165 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 166 DCHECK(!decode_cb.is_null()); |
| 167 CHECK_NE(state_, kUninitialized); |
| 168 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
| 169 |
| 170 if (state_ == kError) { |
| 171 decode_cb_bound.Run(kDecodeError, NULL); |
| 172 return; |
| 173 } |
| 174 |
| 175 // Return empty frames if decoding has finished. |
| 176 if (state_ == kDecodeFinished) { |
| 177 decode_cb_bound.Run(kOk, AudioBuffer::CreateEOSBuffer()); |
| 178 return; |
| 179 } |
| 180 |
| 181 if (!buffer) { |
| 182 decode_cb_bound.Run(kAborted, NULL); |
| 183 return; |
| 184 } |
| 185 |
| 186 DecodeBuffer(buffer, decode_cb_bound); |
| 187 } |
| 188 |
| 189 scoped_refptr<AudioBuffer> FFmpegAudioDecoder::GetDecodeOutput() { |
| 190 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 191 if (queued_audio_.empty()) |
| 192 return NULL; |
| 193 scoped_refptr<AudioBuffer> out = queued_audio_.front(); |
| 194 queued_audio_.pop_front(); |
| 195 return out; |
| 196 } |
| 197 |
| 198 int FFmpegAudioDecoder::bits_per_channel() { |
| 199 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 200 return bytes_per_channel_ * 8; |
| 201 } |
| 202 |
| 203 ChannelLayout FFmpegAudioDecoder::channel_layout() { |
| 204 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 205 return channel_layout_; |
| 206 } |
| 207 |
| 208 int FFmpegAudioDecoder::samples_per_second() { |
| 209 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 210 return samples_per_second_; |
| 211 } |
| 212 |
| 213 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { |
| 214 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 215 |
| 216 avcodec_flush_buffers(codec_context_.get()); |
| 217 state_ = kNormal; |
| 250 ResetTimestampState(); | 218 ResetTimestampState(); |
| 251 queued_audio_.clear(); | 219 task_runner_->PostTask(FROM_HERE, closure); |
| 220 } |
| 221 |
| 222 void FFmpegAudioDecoder::Stop(const base::Closure& closure) { |
| 223 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 224 base::ScopedClosureRunner runner(BindToCurrentLoop(closure)); |
| 225 |
| 226 if (state_ == kUninitialized) |
| 227 return; |
| 228 |
| 252 ReleaseFFmpegResources(); | 229 ReleaseFFmpegResources(); |
| 253 base::ResetAndReturn(&stop_cb_).Run(); | |
| 254 } | |
| 255 | |
| 256 void FFmpegAudioDecoder::DoReset() { | |
| 257 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 258 DCHECK(!reset_cb_.is_null()); | |
| 259 DCHECK(read_cb_.is_null()); | |
| 260 | |
| 261 avcodec_flush_buffers(codec_context_.get()); | |
| 262 ResetTimestampState(); | 230 ResetTimestampState(); |
| 263 queued_audio_.clear(); | 231 state_ = kUninitialized; |
| 264 base::ResetAndReturn(&reset_cb_).Run(); | 232 } |
| 265 | 233 |
| 266 if (!stop_cb_.is_null()) | 234 void FFmpegAudioDecoder::DecodeBuffer( |
| 267 DoStop(); | 235 const scoped_refptr<DecoderBuffer>& buffer, |
| 268 } | 236 const DecodeCB& decode_cb) { |
| 269 | 237 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 270 void FFmpegAudioDecoder::ReadFromDemuxerStream() { | 238 DCHECK_NE(state_, kUninitialized); |
| 271 DCHECK(!read_cb_.is_null()); | 239 DCHECK_NE(state_, kDecodeFinished); |
| 272 demuxer_stream_->Read(base::Bind( | 240 DCHECK_NE(state_, kError); |
| 273 &FFmpegAudioDecoder::BufferReady, weak_this_)); | 241 |
| 274 } | 242 DCHECK(buffer); |
| 275 | 243 |
| 276 void FFmpegAudioDecoder::BufferReady( | 244 // During decode, because reads are issued asynchronously, it is possible to |
| 277 DemuxerStream::Status status, | 245 // receive multiple end of stream buffers since each decode is acked. When the |
| 278 const scoped_refptr<DecoderBuffer>& input) { | 246 // first end of stream buffer is read, FFmpeg may still have frames queued |
| 279 DCHECK(task_runner_->BelongsToCurrentThread()); | 247 // up in the decoder so we need to go through the decode loop until it stops |
| 280 DCHECK(!read_cb_.is_null()); | 248 // giving sensible data. After that, the decoder should output empty |
| 281 DCHECK(queued_audio_.empty()); | 249 // frames. There are three states the decoder can be in: |
| 282 DCHECK_EQ(status != DemuxerStream::kOk, !input.get()) << status; | 250 // |
| 283 | 251 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
| 284 // Pending Reset: ignore the buffer we just got, send kAborted to |read_cb_| | 252 // are discarded. |
| 285 // and carry out the Reset(). | 253 // kFlushCodec: There isn't any more input data. Call avcodec_decode_audio4 |
| 286 // If there happens to also be a pending Stop(), that will be handled at | 254 // until no more data is returned to flush out remaining |
| 287 // the end of DoReset(). | 255 // frames. The input buffer is ignored at this point. |
| 288 if (!reset_cb_.is_null()) { | 256 // kDecodeFinished: All calls return empty frames. |
| 289 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | 257 // kError: Unexpected error happened. |
| 290 DoReset(); | 258 // |
| 291 return; | 259 // These are the possible state transitions. |
| 292 } | 260 // |
| 293 | 261 // kNormal -> kFlushCodec: |
| 294 // Pending Stop: ignore the buffer we just got, send kAborted to |read_cb_| | 262 // When buffer->end_of_stream() is first true. |
| 295 // and carry out the Stop(). | 263 // kNormal -> kError: |
| 296 if (!stop_cb_.is_null()) { | 264 // A decoding error occurs and decoding needs to stop. |
| 297 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | 265 // kFlushCodec -> kDecodeFinished: |
| 298 DoStop(); | 266 // When avcodec_decode_audio4() returns 0 data. |
| 299 return; | 267 // kFlushCodec -> kError: |
| 300 } | 268 // When avcodec_decode_audio4() errors out. |
| 301 | 269 // (any state) -> kNormal: |
| 302 if (status == DemuxerStream::kAborted) { | 270 // Any time Reset() is called. |
| 303 DCHECK(!input.get()); | |
| 304 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 if (status == DemuxerStream::kConfigChanged) { | |
| 309 DCHECK(!input.get()); | |
| 310 | |
| 311 // Send a "end of stream" buffer to the decode loop | |
| 312 // to output any remaining data still in the decoder. | |
| 313 RunDecodeLoop(DecoderBuffer::CreateEOSBuffer(), true); | |
| 314 | |
| 315 DVLOG(1) << "Config changed."; | |
| 316 | |
| 317 if (!ConfigureDecoder()) { | |
| 318 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
| 319 return; | |
| 320 } | |
| 321 | |
| 322 ResetTimestampState(); | |
| 323 | |
| 324 if (queued_audio_.empty()) { | |
| 325 ReadFromDemuxerStream(); | |
| 326 return; | |
| 327 } | |
| 328 | |
| 329 base::ResetAndReturn(&read_cb_).Run( | |
| 330 queued_audio_.front().status, queued_audio_.front().buffer); | |
| 331 queued_audio_.pop_front(); | |
| 332 return; | |
| 333 } | |
| 334 | |
| 335 DCHECK_EQ(status, DemuxerStream::kOk); | |
| 336 DCHECK(input.get()); | |
| 337 | 271 |
| 338 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 272 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
| 339 // occurs with some damaged files. | 273 // occurs with some damaged files. |
| 340 if (!input->end_of_stream() && input->timestamp() == kNoTimestamp() && | 274 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp() && |
| 341 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { | 275 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { |
| 342 DVLOG(1) << "Received a buffer without timestamps!"; | 276 DVLOG(1) << "Received a buffer without timestamps!"; |
| 343 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 277 decode_cb.Run(kDecodeError, NULL); |
| 344 return; | 278 return; |
| 345 } | 279 } |
| 346 | 280 |
| 347 if (!input->end_of_stream()) { | 281 if (!buffer->end_of_stream()) { |
| 348 if (last_input_timestamp_ == kNoTimestamp() && | 282 if (last_input_timestamp_ == kNoTimestamp() && |
| 349 codec_context_->codec_id == AV_CODEC_ID_VORBIS && | 283 codec_context_->codec_id == AV_CODEC_ID_VORBIS && |
| 350 input->timestamp() < base::TimeDelta()) { | 284 buffer->timestamp() < base::TimeDelta()) { |
| 351 // Dropping frames for negative timestamps as outlined in section A.2 | 285 // Dropping frames for negative timestamps as outlined in section A.2 |
| 352 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 286 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
| 353 output_frames_to_drop_ = floor( | 287 output_frames_to_drop_ = floor( |
| 354 0.5 + -input->timestamp().InSecondsF() * samples_per_second_); | 288 0.5 + -buffer->timestamp().InSecondsF() * samples_per_second_); |
| 355 } else { | 289 } else { |
| 356 if (last_input_timestamp_ != kNoTimestamp() && | 290 if (last_input_timestamp_ != kNoTimestamp() && |
| 357 input->timestamp() < last_input_timestamp_) { | 291 buffer->timestamp() < last_input_timestamp_) { |
| 358 const base::TimeDelta diff = input->timestamp() - last_input_timestamp_; | 292 const base::TimeDelta diff = |
| 293 buffer->timestamp() - last_input_timestamp_; |
| 359 DLOG(WARNING) | 294 DLOG(WARNING) |
| 360 << "Input timestamps are not monotonically increasing! " | 295 << "Input timestamps are not monotonically increasing! " |
| 361 << " ts " << input->timestamp().InMicroseconds() << " us" | 296 << " ts " << buffer->timestamp().InMicroseconds() << " us" |
| 362 << " diff " << diff.InMicroseconds() << " us"; | 297 << " diff " << diff.InMicroseconds() << " us"; |
| 363 } | 298 } |
| 364 | 299 |
| 365 last_input_timestamp_ = input->timestamp(); | 300 last_input_timestamp_ = buffer->timestamp(); |
| 366 } | 301 } |
| 367 } | 302 } |
| 368 | 303 |
| 369 RunDecodeLoop(input, false); | 304 // Transition to kFlushCodec on the first end of stream buffer. |
| 370 | 305 if (state_ == kNormal && buffer->end_of_stream()) { |
| 371 // We exhausted the provided packet, but it wasn't enough for a frame. Ask | 306 state_ = kFlushCodec; |
| 372 // for more data in order to fulfill this read. | 307 } |
| 308 |
| 309 if (!FFmpegDecode(buffer)) { |
| 310 state_ = kError; |
| 311 decode_cb.Run(kDecodeError, NULL); |
| 312 return; |
| 313 } |
| 314 |
| 373 if (queued_audio_.empty()) { | 315 if (queued_audio_.empty()) { |
| 374 ReadFromDemuxerStream(); | 316 if (state_ == kFlushCodec) { |
| 375 return; | 317 DCHECK(buffer->end_of_stream()); |
| 376 } | 318 state_ = kDecodeFinished; |
| 377 | 319 decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer()); |
| 378 // Execute callback to return the first frame we decoded. | 320 return; |
| 379 base::ResetAndReturn(&read_cb_).Run( | 321 } |
| 380 queued_audio_.front().status, queued_audio_.front().buffer); | 322 |
| 323 decode_cb.Run(kNotEnoughData, NULL); |
| 324 return; |
| 325 } |
| 326 |
| 327 decode_cb.Run(kOk, queued_audio_.front()); |
| 381 queued_audio_.pop_front(); | 328 queued_audio_.pop_front(); |
| 382 } | 329 } |
| 383 | 330 |
| 384 bool FFmpegAudioDecoder::ConfigureDecoder() { | 331 bool FFmpegAudioDecoder::FFmpegDecode( |
| 385 const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config(); | 332 const scoped_refptr<DecoderBuffer>& buffer) { |
| 386 | 333 |
| 387 if (!config.IsValidConfig()) { | 334 DCHECK(queued_audio_.empty()); |
| 388 DLOG(ERROR) << "Invalid audio stream -" | 335 |
| 389 << " codec: " << config.codec() | |
| 390 << " channel layout: " << config.channel_layout() | |
| 391 << " bits per channel: " << config.bits_per_channel() | |
| 392 << " samples per second: " << config.samples_per_second(); | |
| 393 return false; | |
| 394 } | |
| 395 | |
| 396 if (config.is_encrypted()) { | |
| 397 DLOG(ERROR) << "Encrypted audio stream not supported"; | |
| 398 return false; | |
| 399 } | |
| 400 | |
| 401 if (codec_context_.get() && | |
| 402 (bytes_per_channel_ != config.bytes_per_channel() || | |
| 403 channel_layout_ != config.channel_layout() || | |
| 404 samples_per_second_ != config.samples_per_second())) { | |
| 405 DVLOG(1) << "Unsupported config change :"; | |
| 406 DVLOG(1) << "\tbytes_per_channel : " << bytes_per_channel_ | |
| 407 << " -> " << config.bytes_per_channel(); | |
| 408 DVLOG(1) << "\tchannel_layout : " << channel_layout_ | |
| 409 << " -> " << config.channel_layout(); | |
| 410 DVLOG(1) << "\tsample_rate : " << samples_per_second_ | |
| 411 << " -> " << config.samples_per_second(); | |
| 412 return false; | |
| 413 } | |
| 414 | |
| 415 // Release existing decoder resources if necessary. | |
| 416 ReleaseFFmpegResources(); | |
| 417 | |
| 418 // Initialize AVCodecContext structure. | |
| 419 codec_context_.reset(avcodec_alloc_context3(NULL)); | |
| 420 AudioDecoderConfigToAVCodecContext(config, codec_context_.get()); | |
| 421 | |
| 422 codec_context_->opaque = this; | |
| 423 codec_context_->get_buffer2 = GetAudioBufferImpl; | |
| 424 codec_context_->refcounted_frames = 1; | |
| 425 | |
| 426 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
| 427 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | |
| 428 DLOG(ERROR) << "Could not initialize audio decoder: " | |
| 429 << codec_context_->codec_id; | |
| 430 return false; | |
| 431 } | |
| 432 | |
| 433 // Success! | |
| 434 av_frame_.reset(av_frame_alloc()); | |
| 435 channel_layout_ = config.channel_layout(); | |
| 436 samples_per_second_ = config.samples_per_second(); | |
| 437 output_timestamp_helper_.reset( | |
| 438 new AudioTimestampHelper(config.samples_per_second())); | |
| 439 | |
| 440 // Store initial values to guard against midstream configuration changes. | |
| 441 channels_ = codec_context_->channels; | |
| 442 if (channels_ != ChannelLayoutToChannelCount(channel_layout_)) { | |
| 443 DLOG(ERROR) << "Audio configuration specified " | |
| 444 << ChannelLayoutToChannelCount(channel_layout_) | |
| 445 << " channels, but FFmpeg thinks the file contains " | |
| 446 << channels_ << " channels"; | |
| 447 return false; | |
| 448 } | |
| 449 av_sample_format_ = codec_context_->sample_fmt; | |
| 450 sample_format_ = AVSampleFormatToSampleFormat( | |
| 451 static_cast<AVSampleFormat>(av_sample_format_)); | |
| 452 bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format_); | |
| 453 | |
| 454 return true; | |
| 455 } | |
| 456 | |
| 457 void FFmpegAudioDecoder::ReleaseFFmpegResources() { | |
| 458 codec_context_.reset(); | |
| 459 av_frame_.reset(); | |
| 460 } | |
| 461 | |
| 462 void FFmpegAudioDecoder::ResetTimestampState() { | |
| 463 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); | |
| 464 last_input_timestamp_ = kNoTimestamp(); | |
| 465 output_frames_to_drop_ = 0; | |
| 466 } | |
| 467 | |
| 468 void FFmpegAudioDecoder::RunDecodeLoop( | |
| 469 const scoped_refptr<DecoderBuffer>& input, | |
| 470 bool skip_eos_append) { | |
| 471 AVPacket packet; | 336 AVPacket packet; |
| 472 av_init_packet(&packet); | 337 av_init_packet(&packet); |
| 473 if (input->end_of_stream()) { | 338 if (buffer->end_of_stream()) { |
| 474 packet.data = NULL; | 339 packet.data = NULL; |
| 475 packet.size = 0; | 340 packet.size = 0; |
| 476 } else { | 341 } else { |
| 477 packet.data = const_cast<uint8*>(input->data()); | 342 packet.data = const_cast<uint8*>(buffer->data()); |
| 478 packet.size = input->data_size(); | 343 packet.size = buffer->data_size(); |
| 479 } | 344 } |
| 480 | 345 |
| 481 // Each audio packet may contain several frames, so we must call the decoder | 346 // Each audio packet may contain several frames, so we must call the decoder |
| 482 // until we've exhausted the packet. Regardless of the packet size we always | 347 // until we've exhausted the packet. Regardless of the packet size we always |
| 483 // want to hand it to the decoder at least once, otherwise we would end up | 348 // want to hand it to the decoder at least once, otherwise we would end up |
| 484 // skipping end of stream packets since they have a size of zero. | 349 // skipping end of stream packets since they have a size of zero. |
| 485 do { | 350 do { |
| 486 int frame_decoded = 0; | 351 int frame_decoded = 0; |
| 487 int result = avcodec_decode_audio4( | 352 int result = avcodec_decode_audio4( |
| 488 codec_context_.get(), av_frame_.get(), &frame_decoded, &packet); | 353 codec_context_.get(), av_frame_.get(), &frame_decoded, &packet); |
| 489 | 354 |
| 490 if (result < 0) { | 355 if (result < 0) { |
| 491 DCHECK(!input->end_of_stream()) | 356 DCHECK(!buffer->end_of_stream()) |
| 492 << "End of stream buffer produced an error! " | 357 << "End of stream buffer produced an error! " |
| 493 << "This is quite possibly a bug in the audio decoder not handling " | 358 << "This is quite possibly a bug in the audio decoder not handling " |
| 494 << "end of stream AVPackets correctly."; | 359 << "end of stream AVPackets correctly."; |
| 495 | 360 |
| 496 DLOG(WARNING) | 361 DLOG(WARNING) |
| 497 << "Failed to decode an audio frame with timestamp: " | 362 << "Failed to decode an audio frame with timestamp: " |
| 498 << input->timestamp().InMicroseconds() << " us, duration: " | 363 << buffer->timestamp().InMicroseconds() << " us, duration: " |
| 499 << input->duration().InMicroseconds() << " us, packet size: " | 364 << buffer->duration().InMicroseconds() << " us, packet size: " |
| 500 << input->data_size() << " bytes"; | 365 << buffer->data_size() << " bytes"; |
| 501 | 366 |
| 502 break; | 367 break; |
| 503 } | 368 } |
| 504 | 369 |
| 505 // Update packet size and data pointer in case we need to call the decoder | 370 // Update packet size and data pointer in case we need to call the decoder |
| 506 // with the remaining bytes from this packet. | 371 // with the remaining bytes from this packet. |
| 507 packet.size -= result; | 372 packet.size -= result; |
| 508 packet.data += result; | 373 packet.data += result; |
| 509 | 374 |
| 510 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && | 375 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && |
| 511 !input->end_of_stream()) { | 376 !buffer->end_of_stream()) { |
| 512 DCHECK(input->timestamp() != kNoTimestamp()); | 377 DCHECK(buffer->timestamp() != kNoTimestamp()); |
| 513 if (output_frames_to_drop_ > 0) { | 378 if (output_frames_to_drop_ > 0) { |
| 514 // Currently Vorbis is the only codec that causes us to drop samples. | 379 // Currently Vorbis is the only codec that causes us to drop samples. |
| 515 // If we have to drop samples it always means the timeline starts at 0. | 380 // If we have to drop samples it always means the timeline starts at 0. |
| 516 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); | 381 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); |
| 517 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); | 382 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); |
| 518 } else { | 383 } else { |
| 519 output_timestamp_helper_->SetBaseTimestamp(input->timestamp()); | 384 output_timestamp_helper_->SetBaseTimestamp(buffer->timestamp()); |
| 520 } | 385 } |
| 521 } | 386 } |
| 522 | 387 |
| 523 scoped_refptr<AudioBuffer> output; | 388 scoped_refptr<AudioBuffer> output; |
| 524 int decoded_frames = 0; | 389 int decoded_frames = 0; |
| 525 int original_frames = 0; | 390 int original_frames = 0; |
| 526 int channels = DetermineChannels(av_frame_.get()); | 391 int channels = DetermineChannels(av_frame_.get()); |
| 527 if (frame_decoded) { | 392 if (frame_decoded) { |
| 528 if (av_frame_->sample_rate != samples_per_second_ || | 393 |
| 394 // TODO(rileya) Remove this check once we properly support midstream audio |
| 395 // config changes. |
| 396 if (av_frame_->sample_rate != config_.samples_per_second() || |
| 529 channels != channels_ || | 397 channels != channels_ || |
| 530 av_frame_->format != av_sample_format_) { | 398 av_frame_->format != av_sample_format_) { |
| 531 DLOG(ERROR) << "Unsupported midstream configuration change!" | 399 DLOG(ERROR) << "Unsupported midstream configuration change!" |
| 532 << " Sample Rate: " << av_frame_->sample_rate << " vs " | 400 << " Sample Rate: " << av_frame_->sample_rate << " vs " |
| 533 << samples_per_second_ | 401 << samples_per_second_ |
| 534 << ", Channels: " << channels << " vs " | 402 << ", Channels: " << channels << " vs " |
| 535 << channels_ | 403 << channels_ |
| 536 << ", Sample Format: " << av_frame_->format << " vs " | 404 << ", Sample Format: " << av_frame_->format << " vs " |
| 537 << av_sample_format_; | 405 << av_sample_format_; |
| 538 | 406 |
| 539 // This is an unrecoverable error, so bail out. | 407 // This is an unrecoverable error, so bail out. |
| 540 QueuedAudioBuffer queue_entry = { kDecodeError, NULL }; | 408 queued_audio_.clear(); |
| 541 queued_audio_.push_back(queue_entry); | |
| 542 av_frame_unref(av_frame_.get()); | 409 av_frame_unref(av_frame_.get()); |
| 543 break; | 410 return false; |
| 544 } | 411 } |
| 545 | 412 |
| 546 // Get the AudioBuffer that the data was decoded into. Adjust the number | 413 // Get the AudioBuffer that the data was decoded into. Adjust the number |
| 547 // of frames, in case fewer than requested were actually decoded. | 414 // of frames, in case fewer than requested were actually decoded. |
| 548 output = reinterpret_cast<AudioBuffer*>( | 415 output = reinterpret_cast<AudioBuffer*>( |
| 549 av_buffer_get_opaque(av_frame_->buf[0])); | 416 av_buffer_get_opaque(av_frame_->buf[0])); |
| 417 |
| 550 DCHECK_EQ(channels_, output->channel_count()); | 418 DCHECK_EQ(channels_, output->channel_count()); |
| 551 original_frames = av_frame_->nb_samples; | 419 original_frames = av_frame_->nb_samples; |
| 552 int unread_frames = output->frame_count() - original_frames; | 420 int unread_frames = output->frame_count() - original_frames; |
| 553 DCHECK_GE(unread_frames, 0); | 421 DCHECK_GE(unread_frames, 0); |
| 554 if (unread_frames > 0) | 422 if (unread_frames > 0) |
| 555 output->TrimEnd(unread_frames); | 423 output->TrimEnd(unread_frames); |
| 556 | 424 |
| 557 // If there are frames to drop, get rid of as many as we can. | 425 // If there are frames to drop, get rid of as many as we can. |
| 558 if (output_frames_to_drop_ > 0) { | 426 if (output_frames_to_drop_ > 0) { |
| 559 int drop = std::min(output->frame_count(), output_frames_to_drop_); | 427 int drop = std::min(output->frame_count(), output_frames_to_drop_); |
| 560 output->TrimStart(drop); | 428 output->TrimStart(drop); |
| 561 output_frames_to_drop_ -= drop; | 429 output_frames_to_drop_ -= drop; |
| 562 } | 430 } |
| 563 | 431 |
| 564 decoded_frames = output->frame_count(); | 432 decoded_frames = output->frame_count(); |
| 565 av_frame_unref(av_frame_.get()); | 433 av_frame_unref(av_frame_.get()); |
| 566 } | 434 } |
| 567 | 435 |
| 568 // WARNING: |av_frame_| no longer has valid data at this point. | 436 // WARNING: |av_frame_| no longer has valid data at this point. |
| 569 | 437 |
| 570 if (decoded_frames > 0) { | 438 if (decoded_frames > 0) { |
| 571 // Set the timestamp/duration once all the extra frames have been | 439 // Set the timestamp/duration once all the extra frames have been |
| 572 // discarded. | 440 // discarded. |
| 573 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); | 441 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); |
| 574 output->set_duration( | 442 output->set_duration( |
| 575 output_timestamp_helper_->GetFrameDuration(decoded_frames)); | 443 output_timestamp_helper_->GetFrameDuration(decoded_frames)); |
| 576 output_timestamp_helper_->AddFrames(decoded_frames); | 444 output_timestamp_helper_->AddFrames(decoded_frames); |
| 577 } else if (IsEndOfStream(result, original_frames, input) && | 445 } else if (IsEndOfStream(result, original_frames, buffer)) { |
| 578 !skip_eos_append) { | |
| 579 DCHECK_EQ(packet.size, 0); | 446 DCHECK_EQ(packet.size, 0); |
| 580 output = AudioBuffer::CreateEOSBuffer(); | 447 output = AudioBuffer::CreateEOSBuffer(); |
| 581 } else { | 448 } else { |
| 582 // In case all the frames in the buffer were dropped. | 449 // In case all the frames in the buffer were dropped. |
| 583 output = NULL; | 450 output = NULL; |
| 584 } | 451 } |
| 585 | 452 |
| 586 if (output.get()) { | 453 if (output.get()) |
| 587 QueuedAudioBuffer queue_entry = { kOk, output }; | 454 queued_audio_.push_back(output); |
| 588 queued_audio_.push_back(queue_entry); | |
| 589 } | |
| 590 | 455 |
| 591 // Decoding finished successfully, update statistics. | |
| 592 if (result > 0) { | |
| 593 PipelineStatistics statistics; | |
| 594 statistics.audio_bytes_decoded = result; | |
| 595 statistics_cb_.Run(statistics); | |
| 596 } | |
| 597 } while (packet.size > 0); | 456 } while (packet.size > 0); |
| 457 |
| 458 return true; |
| 459 } |
| 460 |
| 461 void FFmpegAudioDecoder::ReleaseFFmpegResources() { |
| 462 codec_context_.reset(); |
| 463 av_frame_.reset(); |
| 464 } |
| 465 |
| 466 bool FFmpegAudioDecoder::ConfigureDecoder() { |
| 467 if (!config_.IsValidConfig()) { |
| 468 DLOG(ERROR) << "Invalid audio stream -" |
| 469 << " codec: " << config_.codec() |
| 470 << " channel layout: " << config_.channel_layout() |
| 471 << " bits per channel: " << config_.bits_per_channel() |
| 472 << " samples per second: " << config_.samples_per_second(); |
| 473 return false; |
| 474 } |
| 475 |
| 476 if (config_.is_encrypted()) { |
| 477 DLOG(ERROR) << "Encrypted audio stream not supported"; |
| 478 return false; |
| 479 } |
| 480 |
| 481 // TODO(rileya) Remove this check once we properly support midstream audio |
| 482 // config changes. |
| 483 if (codec_context_.get() && |
| 484 (bytes_per_channel_ != config_.bytes_per_channel() || |
| 485 channel_layout_ != config_.channel_layout() || |
| 486 samples_per_second_ != config_.samples_per_second())) { |
| 487 DVLOG(1) << "Unsupported config change :"; |
| 488 DVLOG(1) << "\tbytes_per_channel : " << bytes_per_channel_ |
| 489 << " -> " << config_.bytes_per_channel(); |
| 490 DVLOG(1) << "\tchannel_layout : " << channel_layout_ |
| 491 << " -> " << config_.channel_layout(); |
| 492 DVLOG(1) << "\tsample_rate : " << samples_per_second_ |
| 493 << " -> " << config_.samples_per_second(); |
| 494 return false; |
| 495 } |
| 496 |
| 497 // Release existing decoder resources if necessary. |
| 498 ReleaseFFmpegResources(); |
| 499 |
| 500 // Initialize AVCodecContext structure. |
| 501 codec_context_.reset(avcodec_alloc_context3(NULL)); |
| 502 AudioDecoderConfigToAVCodecContext(config_, codec_context_.get()); |
| 503 |
| 504 codec_context_->opaque = this; |
| 505 codec_context_->get_buffer2 = GetAudioBuffer; |
| 506 codec_context_->refcounted_frames = 1; |
| 507 |
| 508 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
| 509 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
| 510 DLOG(ERROR) << "Could not initialize audio decoder: " |
| 511 << codec_context_->codec_id; |
| 512 ReleaseFFmpegResources(); |
| 513 state_ = kUninitialized; |
| 514 return false; |
| 515 } |
| 516 |
| 517 // Success! |
| 518 av_frame_.reset(av_frame_alloc()); |
| 519 channel_layout_ = config_.channel_layout(); |
| 520 samples_per_second_ = config_.samples_per_second(); |
| 521 output_timestamp_helper_.reset( |
| 522 new AudioTimestampHelper(config_.samples_per_second())); |
| 523 |
| 524 // Store initial values to guard against midstream configuration changes. |
| 525 channels_ = codec_context_->channels; |
| 526 if (channels_ != ChannelLayoutToChannelCount(channel_layout_)) { |
| 527 DLOG(ERROR) << "Audio configuration specified " |
| 528 << ChannelLayoutToChannelCount(channel_layout_) |
| 529 << " channels, but FFmpeg thinks the file contains " |
| 530 << channels_ << " channels"; |
| 531 return false; |
| 532 } |
| 533 av_sample_format_ = codec_context_->sample_fmt; |
| 534 sample_format_ = AVSampleFormatToSampleFormat( |
| 535 static_cast<AVSampleFormat>(av_sample_format_)); |
| 536 bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format_); |
| 537 |
| 538 return true; |
| 539 } |
| 540 |
| 541 void FFmpegAudioDecoder::ResetTimestampState() { |
| 542 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); |
| 543 last_input_timestamp_ = kNoTimestamp(); |
| 544 output_frames_to_drop_ = 0; |
| 598 } | 545 } |
| 599 | 546 |
| 600 } // namespace media | 547 } // namespace media |
| OLD | NEW |