blob: 8470efaa40a18870e08009a19cc27b3e10bd1ad4 [file] [log] [blame]
// Copyright (c) 2013 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_GPU_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
#define MEDIA_GPU_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
#include <stdint.h>
#include <list>
#include <map>
#include <queue>
#include <vector>
#include "base/compiler_specific.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_preferences.h"
#include "media/base/android/media_drm_bridge_cdm_context.h"
#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/media_keys.h"
#include "media/gpu/avda_picture_buffer_manager.h"
#include "media/gpu/avda_state_provider.h"
#include "media/gpu/avda_surface_tracker.h"
#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
#include "media/gpu/media_gpu_export.h"
#include "media/video/video_decode_accelerator.h"
#include "ui/gl/android/scoped_java_surface.h"
namespace gl {
class SurfaceTexture;
}
namespace media {
class SharedMemoryRegion;
// A VideoDecodeAccelerator implementation for Android. This class decodes the
// encded input stream using Android's MediaCodec. It handles the work of
// transferring data to and from MediaCodec, and delegates attaching MediaCodec
// output buffers to PictureBuffers to AVDAPictureBufferManager.
class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator
: public VideoDecodeAccelerator,
public AVDAStateProvider {
public:
AndroidVideoDecodeAccelerator(
const MakeGLContextCurrentCallback& make_context_current_cb,
const GetGLES2DecoderCallback& get_gles2_decoder_cb);
~AndroidVideoDecodeAccelerator() override;
// VideoDecodeAccelerator implementation:
bool Initialize(const Config& config, Client* client) override;
void Decode(const BitstreamBuffer& bitstream_buffer) override;
void AssignPictureBuffers(const std::vector<PictureBuffer>& buffers) override;
void ReusePictureBuffer(int32_t picture_buffer_id) override;
void Flush() override;
void Reset() override;
void Destroy() override;
bool TryToSetupDecodeOnSeparateThread(
const base::WeakPtr<Client>& decode_client,
const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner)
override;
// AVDAStateProvider implementation:
const gfx::Size& GetSize() const override;
base::WeakPtr<gpu::gles2::GLES2Decoder> GetGlDecoder() const override;
void PostError(const ::tracked_objects::Location& from_here,
VideoDecodeAccelerator::Error error) override;
static VideoDecodeAccelerator::Capabilities GetCapabilities(
const gpu::GpuPreferences& gpu_preferences);
private:
friend class AVDAManager;
// TODO(timav): evaluate the need for more states in the AVDA state machine.
enum State {
NO_ERROR,
ERROR,
// Set when we are asynchronously constructing the codec. Will transition
// to NO_ERROR or ERROR depending on success.
WAITING_FOR_CODEC,
// Set when we have a codec, but it doesn't yet have a key.
WAITING_FOR_KEY,
// The output surface was destroyed. We must not configure a new MediaCodec
// with the destroyed surface.
SURFACE_DESTROYED,
};
enum DrainType {
DRAIN_TYPE_NONE,
DRAIN_FOR_FLUSH,
DRAIN_FOR_RESET,
DRAIN_FOR_DESTROY,
};
// Configuration info for MediaCodec.
// This is used to shuttle configuration info between threads without needing
// to worry about the lifetime of the AVDA instance. All of these should not
// be modified while |state_| is WAITING_FOR_CODEC.
class CodecConfig : public base::RefCountedThreadSafe<CodecConfig> {
public:
CodecConfig();
// Codec type. Used when we configure media codec.
VideoCodec codec_ = kUnknownVideoCodec;
// Whether encryption scheme requires to use protected surface.
bool needs_protected_surface_ = false;
// The surface that MediaCodec is configured to output to.
gl::ScopedJavaSurface surface_;
// The MediaCrypto object is used in the MediaCodec.configure() in case of
// an encrypted stream.
MediaDrmBridgeCdmContext::JavaObjectPtr media_crypto_;
// Initial coded size. The actual size might change at any time, so this
// is only a hint.
gfx::Size initial_expected_coded_size_;
// Should we allow MediaCodec to autodetect the codec type (true), or
// select a software decoder manually (false). This is because fallback to
// software when autodetecting can sometimes hang mediaserver.
bool allow_autodetection_ = false;
protected:
friend class base::RefCountedThreadSafe<CodecConfig>;
virtual ~CodecConfig();
private:
DISALLOW_COPY_AND_ASSIGN(CodecConfig);
};
// Callback that is called when the SurfaceView becomes available, if it's
// not during Initialize. |success| is true if it is now available, false
// if initialization should stop.
void OnSurfaceAvailable(bool success);
// Initialize of the picture buffer manager. This is to be called when the
// SurfaceView in |surface_id_|, if any, is no longer busy. It will return
// false on failure, and true if initialization was successful. This includes
// synchronous and asynchronous init; the AVDA might not yet have a codec on
// success, but async init will at least be in progress.
bool InitializePictureBufferManager();
// A part of destruction process that is sometimes postponed after the drain.
void ActualDestroy();
// Configures |media_codec_| with the given codec parameters from the client.
// This configuration will (probably) not be complete before this call
// returns. Multiple calls before completion will be ignored. |state_|
// must be NO_ERROR or WAITING_FOR_CODEC. Note that, once you call this,
// you should be careful to avoid modifying members of |codec_config_| until
// |state_| is no longer WAITING_FOR_CODEC.
void ConfigureMediaCodecAsynchronously();
// Like ConfigureMediaCodecAsynchronously, but synchronous. Returns true if
// and only if |media_codec_| is non-null. Since all configuration is done
// synchronously, there is no concern with modifying |codec_config_| after
// this returns.
bool ConfigureMediaCodecSynchronously();
// Instantiate a media codec using |codec_config|.
// This may be called on any thread.
static std::unique_ptr<VideoCodecBridge> ConfigureMediaCodecOnAnyThread(
scoped_refptr<CodecConfig> codec_config);
// Called on the main thread to update |media_codec_| and complete codec
// configuration. |media_codec| will be null if configuration failed.
void OnCodecConfigured(std::unique_ptr<VideoCodecBridge> media_codec);
// Sends the decoded frame specified by |codec_buffer_index| to the client.
void SendDecodedFrameToClient(int32_t codec_buffer_index,
int32_t bitstream_id);
// Does pending IO tasks if any. Once this is called, it polls |media_codec_|
// until it finishes pending tasks. For the polling, |kDecodePollDelay| is
// used.
void DoIOTask(bool start_timer);
// Feeds input data to |media_codec_|. This checks
// |pending_bitstream_buffers_| and queues a buffer to |media_codec_|.
// Returns true if any input was processed.
bool QueueInput();
// Dequeues output from |media_codec_| and feeds the decoded frame to the
// client. Returns a hint about whether calling again might produce
// more output.
bool DequeueOutput();
// Requests picture buffers from the client.
void RequestPictureBuffers();
// Decode the content in the |bitstream_buffer|. Note that a
// |bitstream_buffer| of id as -1 indicates a flush command.
void DecodeBuffer(const BitstreamBuffer& bitstream_buffer);
// Called during Initialize() for encrypted streams to set up the CDM.
void InitializeCdm();
// Called after the CDM obtains a MediaCrypto object.
void OnMediaCryptoReady(MediaDrmBridgeCdmContext::JavaObjectPtr media_crypto,
bool needs_protected_surface);
// Called when a new key is added to the CDM.
void OnKeyAdded();
// Notifies the client of the result of deferred initialization.
void NotifyInitializationComplete(bool success);
// Notifies the client about the availability of a picture.
void NotifyPictureReady(const Picture& picture);
// Notifies the client that the input buffer identifed by input_buffer_id has
// been processed.
void NotifyEndOfBitstreamBuffer(int input_buffer_id);
// Notifies the client that the decoder was flushed.
void NotifyFlushDone();
// Notifies the client that the decoder was reset.
void NotifyResetDone();
// Notifies about decoding errors.
// Note: you probably don't want to call this directly. Use PostError or
// RETURN_ON_FAILURE, since we can defer error reporting to keep the pipeline
// from breaking. NotifyError will do so immediately, PostError may wait.
// |token| has to match |error_sequence_token_|, or else it's assumed to be
// from a post that's prior to a previous reset, and ignored.
void NotifyError(VideoDecodeAccelerator::Error error, int token);
// Start or stop our work-polling timer based on whether we did any work, and
// how long it has been since we've done work. Calling this with true will
// start the timer. Calling it with false may stop the timer.
void ManageTimer(bool did_work);
// Safely clear |media_codec_|. Do this instead of calling reset() / assign.
// Otherwise, the destructor can hang if mediaserver is in a bad state. This
// will release immediately if safe, else post to a separate thread. Either
// way, |media_codec_| will be null upon return.
void ReleaseMediaCodec();
// Start the MediaCodec drain process by adding end_of_stream() buffer to the
// encoded buffers queue. When we receive EOS from the output buffer the drain
// process completes and we perform the action depending on the |drain_type|.
void StartCodecDrain(DrainType drain_type);
// Returns true if we are currently draining the codec and doing that as part
// of Reset() or Destroy() VP8 workaround. (http://crbug.com/598963). We won't
// display any frames and disable normal errors handling.
bool IsDrainingForResetOrDestroy() const;
// A helper method that performs the operation required after the drain
// completion (usually when we receive EOS in the output). The operation
// itself depends on the |drain_type_|.
void OnDrainCompleted();
// Resets MediaCodec and buffers/containers used for storing output. These
// components need to be reset upon EOS to decode a later stream. Input state
// (e.g. queued BitstreamBuffers) is not reset, as input following an EOS
// is still valid and should be processed.
void ResetCodecState();
// Registered to be called when surfaces are being destroyed. If |surface_id|
// is our surface, we should release the MediaCodec before returning from
// this.
void OnDestroyingSurface(int surface_id);
// Indicates if MediaCodec should not be used for software decoding since we
// have safer versions elsewhere.
bool IsMediaCodecSoftwareDecodingForbidden() const;
// Used to DCHECK that we are called on the correct thread.
base::ThreadChecker thread_checker_;
// To expose client callbacks from VideoDecodeAccelerator.
Client* client_;
// Callback to set the correct gl context.
MakeGLContextCurrentCallback make_context_current_cb_;
// Callback to get the GLES2Decoder instance.
GetGLES2DecoderCallback get_gles2_decoder_cb_;
// The current state of this class. For now, this is used only for setting
// error state.
State state_;
// The assigned picture buffers by picture buffer id.
AVDAPictureBufferManager::PictureBufferMap output_picture_buffers_;
// This keeps the free picture buffer ids which can be used for sending
// decoded frames to the client.
std::queue<int32_t> free_picture_ids_;
// The low-level decoder which Android SDK provides.
std::unique_ptr<VideoCodecBridge> media_codec_;
// Set to true after requesting picture buffers to the client.
bool picturebuffers_requested_;
// The resolution of the stream.
gfx::Size size_;
// Handy structure to remember a BitstreamBuffer and also its shared memory,
// if any. The goal is to prevent leaving a BitstreamBuffer's shared memory
// handle open.
struct BitstreamRecord {
BitstreamRecord(const BitstreamBuffer&);
BitstreamRecord(BitstreamRecord&& other);
~BitstreamRecord();
BitstreamBuffer buffer;
// |memory| is not mapped, and may be null if buffer has no data.
std::unique_ptr<SharedMemoryRegion> memory;
};
// Encoded bitstream buffers to be passed to media codec, queued until an
// input buffer is available.
std::queue<BitstreamRecord> pending_bitstream_records_;
// A map of presentation timestamp to bitstream buffer id for the bitstream
// buffers that have been submitted to the decoder but haven't yet produced an
// output frame with the same timestamp. Note: there will only be one entry
// for multiple bitstream buffers that have the same presentation timestamp.
std::map<base::TimeDelta, int32_t> bitstream_buffers_in_decoder_;
// Keeps track of bitstream ids notified to the client with
// NotifyEndOfBitstreamBuffer() before getting output from the bitstream.
std::list<int32_t> bitstreams_notified_in_advance_;
AVDAPictureBufferManager picture_buffer_manager_;
// Time at which we last did useful work on io_timer_.
base::TimeTicks most_recent_work_;
// Type of a drain request. We need to distinguish between DRAIN_FOR_FLUSH
// and other types, see IsDrainingForResetOrDestroy().
DrainType drain_type_;
// Holds a ref-count to the CDM to avoid using the CDM after it's destroyed.
scoped_refptr<MediaKeys> cdm_for_reference_holding_only_;
MediaDrmBridgeCdmContext* media_drm_bridge_cdm_context_;
// MediaDrmBridge requires registration/unregistration of the player, this
// registration id is used for this.
int cdm_registration_id_;
// Configuration that we use for MediaCodec.
// Do not update any of its members while |state_| is WAITING_FOR_CODEC.
scoped_refptr<CodecConfig> codec_config_;
// Index of the dequeued and filled buffer that we keep trying to enqueue.
// Such buffer appears in MEDIA_CODEC_NO_KEY processing.
int pending_input_buf_index_;
// Monotonically increasing value that is used to prevent old, delayed errors
// from being sent after a reset.
int error_sequence_token_;
// PostError will defer sending an error if and only if this is true.
bool defer_errors_;
// True if and only if VDA initialization is deferred, and we have not yet
// called NotifyInitializationComplete.
bool deferred_initialization_pending_;
// Indicates if ResetCodecState() should be called upon the next call to
// Decode(). Allows us to avoid trashing the last few frames of a playback
// when the EOS buffer is received.
bool codec_needs_reset_;
// Copy of the VDA::Config we were given.
Config config_;
OnDestroyingSurfaceCallback on_destroying_surface_cb_;
// WeakPtrFactory for posting tasks back to |this|.
base::WeakPtrFactory<AndroidVideoDecodeAccelerator> weak_this_factory_;
friend class AndroidVideoDecodeAcceleratorTest;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_VIDEO_DECODE_ACCELERATOR_H_