| Index: media/filters/android/media_codec_audio_decoder.h
|
| diff --git a/media/filters/android/media_codec_audio_decoder.h b/media/filters/android/media_codec_audio_decoder.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a15fd800e491df08fa04142a56cb48448eb5890b
|
| --- /dev/null
|
| +++ b/media/filters/android/media_codec_audio_decoder.h
|
| @@ -0,0 +1,217 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#ifndef MEDIA_FILTERS_ANDROID_MEDIA_CODEC_AUDIO_DECODER_H_
|
| +#define MEDIA_FILTERS_ANDROID_MEDIA_CODEC_AUDIO_DECODER_H_
|
| +
|
| +#include <deque>
|
| +#include <utility>
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/memory/weak_ptr.h"
|
| +#include "base/time/time.h"
|
| +#include "base/timer/timer.h"
|
| +#include "media/base/android/media_codec_bridge.h"
|
| +#include "media/base/audio_decoder.h"
|
| +#include "media/base/audio_decoder_config.h"
|
| +#include "media/base/media_export.h"
|
| +
|
| +namespace base {
|
| +class SingleThreadTaskRunner;
|
| +}
|
| +
|
| +namespace media {
|
| +
|
| +// MediaCodecAudioDecoder is based on Android's MediaCodec API.
|
| +// The MediaCodec API is required to play encrypted (as in EME) content on
|
| +// Android. It is also a way to employ hardware-accelerated decoding.
|
| +
|
| +// TODO(timav): This class has the logic to manipulate the MediaCodec that is
|
| +// the common for audio and video stream and can and should be refactored out
|
| +// (http://crbug.com/583082).
|
| +
|
| +// Implementation notes.
|
| +//
|
| +// The MediaCodec
|
| +// (http://developer.android.com/reference/android/media/MediaCodec.html) works
|
| +// by exchanging buffers between the client and the codec itself. On the input
|
| +// side an "empty" buffer has to be dequeued from the codec, filled with data
|
| +// and queued back. On the output side a "full" buffer with data should be
|
| +// dequeued, the data is to be used somehow (copied out, or rendered to a pre-
|
| +// defined texture for video) and the buffer has to be returned back (released).
|
| +// Neither input nor output dequeue operations are guaranteed to succeed: the
|
| +// codec might not have available input buffers yet, or not every encoded buffer
|
| +// has arrived to complete an output frame. In such case the client should try
|
| +// to dequeue a buffer again at a later time.
|
| +//
|
| +// There is also a special situation related to an encrypted stream, where the
|
| +// enqueuing of a filled input buffer might fail due to lack of the relevant key
|
| +// in the CDM module.
|
| +//
|
| +// Because both dequeuing and enqueuing of an input buffer can fail, the
|
| +// implementation puts the input |DecoderBuffer|s and the corresponding decode
|
| +// callbacks into an input queue. The decoder has a timer that periodically
|
| +// tries to send the front buffer from the input queue to the MediaCodec. In
|
| +// the case of success the element is removed from the queue, the decode
|
| +// callback is fired and the decoding process advances. The same timer tries to
|
| +// dequeue an output buffer.
|
| +//
|
| +// Although one can specify a delay in the MediaCodec's dequeue operations,
|
| +// this implementation follows the simple logic which is similar to
|
| +// AndroidVideoDecodeAccelerator: no delay for either input or output buffers,
|
| +// the processing is initated by the timer with short period (10 ms). Using no
|
| +// delay for enqueue operations has an extra benefit of not blocking the current
|
| +// thread.
|
| +//
|
| +// This implementation detects the MediaCodec idle run (no input or output
|
| +// buffer processing) and after being idle for a predefined time the timer
|
| +// stops. Every Decode() wakes the timer up.
|
| +//
|
| +// The current implementation is single threaded. Every method is supposed to
|
| +// run on the same thread.
|
| +//
|
| +// State diagram.
|
| +//
|
| +// [Uninitialized] <-> (init failed) [Ready]
|
| +// | |
|
| +// (init succeeded) (MEDIA_CODEC_NO_KEY)
|
| +// | |
|
| +// [Ready] [WaitingForKey]
|
| +// | |
|
| +// (EOS enqueued) (OnKeyAdded)
|
| +// | |
|
| +// [Draining] [Ready]
|
| +// |
|
| +// (EOS dequeued on output)
|
| +// |
|
| +// [Drained]
|
| +//
|
| +// [Ready, WaitingForKey, Draining] -[Any state]-
|
| +// | | |
|
| +// (MediaCodec error) (Reset ok) (Reset fails)
|
| +// | | |
|
| +// [Error] [Ready] [Error]
|
| +
|
| +class MEDIA_EXPORT MediaCodecAudioDecoder : public AudioDecoder {
|
| + public:
|
| + explicit MediaCodecAudioDecoder(
|
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
|
| + ~MediaCodecAudioDecoder() override;
|
| +
|
| + // AudioDecoder implementation.
|
| + std::string GetDisplayName() const override;
|
| + void Initialize(const AudioDecoderConfig& config,
|
| + const SetCdmReadyCB& set_cdm_ready_cb,
|
| + const InitCB& init_cb,
|
| + const OutputCB& output_cb) override;
|
| + void Decode(const scoped_refptr<DecoderBuffer>& buffer,
|
| + const DecodeCB& decode_cb) override;
|
| + void Reset(const base::Closure& closure) override;
|
| +
|
| + private:
|
| + // Possible states.
|
| + enum State {
|
| + STATE_UNINITIALIZED,
|
| + STATE_READY,
|
| + STATE_WAITING_FOR_KEY,
|
| + STATE_DRAINING,
|
| + STATE_DRAINED,
|
| + STATE_ERROR,
|
| + };
|
| +
|
| + // Information about the MediaCodec's output buffer.
|
| + struct OutputBufferInfo {
|
| + int buf_index; // The codec output buffers are referred to by this index.
|
| + size_t offset; // Position in the buffer where data starts.
|
| + size_t size; // The size of the buffer (includes offset).
|
| + base::TimeDelta pts; // Presentation timestamp.
|
| + bool is_eos; // true if this buffer is the end of stream.
|
| + bool is_key_frame;
|
| + };
|
| +
|
| + // Callback called when a new key is available after the codec received
|
| + // the status MEDIA_CODEC_NO_KEY.
|
| + void OnKeyAdded();
|
| +
|
| + // Does the MediaCodec processing cycle: enqueues an input buffer, then
|
| + // dequeues output buffers.
|
| + void DoIOTask();
|
| +
|
| + // Enqueues one pending input buffer into MediaCodec if MediaCodec has room.
|
| + // Returns true if any input was processed.
|
| + bool QueueInput();
|
| +
|
| + // Dequeues all output buffers from MediaCodec that are immediately available.
|
| + // Returns true if any output buffer was received from MediaCodec.
|
| + bool DequeueOutput();
|
| +
|
| + // Start the timer immediately if |start| is true or stop it based on elapsed
|
| + // idle time if |start| is false.
|
| + void ManageTimer(bool start);
|
| +
|
| + // Helper method to change the state.
|
| + void SetState(State new_state);
|
| +
|
| + // The following helper methods ConfigureMediaCodec(), OnDecodedFrame(),
|
| + // OnOutputFormatChanged() are specific to the stream (audio/video), but
|
| + // others seem to apply to any MediaCodec decoder.
|
| + // TODO(timav): refactor the common part out and use it here and in AVDA
|
| + // (http://crbug.com/583082).
|
| +
|
| + // Configures MediaCodec with |config|. Returns valid MediaCodec pointer if
|
| + // succeeded or null if configuration failed.
|
| + scoped_ptr<MediaCodecBridge> ConfigureMediaCodec(
|
| + const AudioDecoderConfig& config);
|
| +
|
| + // Processes the output buffer after it comes from MediaCodec.
|
| + void OnDecodedFrame(const OutputBufferInfo& out);
|
| +
|
| + // Processes the output format change.
|
| + void OnOutputFormatChanged();
|
| +
|
| + // A helper function for logging.
|
| + static const char* AsString(State state);
|
| +
|
| + // Used to post tasks. This class is single threaded and every method should
|
| + // run on this task runner.
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
| +
|
| + State state_;
|
| +
|
| + // The queue of encoded (and maybe encrypted) buffers. The MediaCodec might
|
| + // not be able to accept the input at the time of Decode(), thus all
|
| + // DecoderBuffers first go to |input_queue_|.
|
| + using BufferCBPair = std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>;
|
| + using InputQueue = std::deque<BufferCBPair>;
|
| + InputQueue input_queue_;
|
| +
|
| + // Cached decoder config.
|
| + AudioDecoderConfig config_;
|
| +
|
| + // Callback that delivers output frames.
|
| + OutputCB output_cb_;
|
| +
|
| + scoped_ptr<MediaCodecBridge> media_codec_;
|
| +
|
| + // Repeating timer that kicks MediaCodec operation.
|
| + base::RepeatingTimer io_timer_;
|
| +
|
| + // Time at which we last did useful work on |io_timer_|.
|
| + base::TimeTicks idle_time_begin_;
|
| +
|
| + // Index of the dequeued and filled buffer that we keep trying to enqueue.
|
| + // Such buffer appears in MEDIA_CODEC_NO_KEY processing. The -1 value means
|
| + // there is no such buffer.
|
| + int pending_input_buf_index_;
|
| +
|
| + // Weak pointer factory must be the last member variable.
|
| + base::WeakPtrFactory<MediaCodecAudioDecoder> weak_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MediaCodecAudioDecoder);
|
| +};
|
| +
|
| +} // namespace media
|
| +
|
| +#endif // MEDIA_FILTERS_ANDROID_MEDIA_CODEC_AUDIO_DECODER_H_
|
|
|