blob: d8428de7fed5fde20a86961023467e894539be62 [file] [log] [blame]
// 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_GPU_AVDA_CODEC_ALLOCATOR_H_
#define MEDIA_GPU_AVDA_CODEC_ALLOCATOR_H_
#include <stddef.h>
#include <memory>
#include "base/android/build_info.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/synchronization/waitable_event.h"
#include "base/sys_info.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "media/base/android/media_codec_bridge_impl.h"
#include "media/base/android/media_drm_bridge_cdm_context.h"
#include "media/base/media.h"
#include "media/base/surface_manager.h"
#include "media/base/video_codecs.h"
#include "media/gpu/media_gpu_export.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/android/scoped_java_surface.h"
namespace media {
// For TaskRunnerFor. These are used as vector indices, so please update
// AVDACodecAllocator's constructor if you add / change them.
enum TaskType {
// Task for an autodetected MediaCodec instance.
AUTO_CODEC = 0,
// Task for a software-codec-required MediaCodec.
SW_CODEC = 1,
};
// Configuration info for MediaCodec.
// This is used to shuttle configuration info between threads without needing
// to worry about the lifetime of the AVDA instance.
class CodecConfig : public base::RefCountedThreadSafe<CodecConfig> {
public:
CodecConfig();
VideoCodec codec = kUnknownVideoCodec;
// The surface that MediaCodec is configured to output to.
gl::ScopedJavaSurface surface;
int surface_id = SurfaceManager::kNoSurfaceID;
// The SurfaceTexture attached to |surface|, or nullptr if |surface| is
// SurfaceView backed.
scoped_refptr<gl::SurfaceTexture> surface_texture;
// The MediaCrypto that MediaCodec is configured with for an encrypted stream.
MediaDrmBridgeCdmContext::JavaObjectPtr media_crypto;
// Whether the encryption scheme requires us to use a protected surface.
bool needs_protected_surface = false;
// The initial coded size. The actual size might change at any time, so this
// is only a hint.
gfx::Size initial_expected_coded_size;
// The type of allocation to use for. We use this to select the right thread
// for construction / destruction, and to decide if we should restrict the
// codec to be software only.
TaskType task_type;
// Codec specific data (SPS and PPS for H264).
std::vector<uint8_t> csd0;
std::vector<uint8_t> csd1;
protected:
friend class base::RefCountedThreadSafe<CodecConfig>;
virtual ~CodecConfig();
private:
DISALLOW_COPY_AND_ASSIGN(CodecConfig);
};
class AVDACodecAllocatorClient {
public:
// Called when the requested SurfaceView becomes available after a call to
// AllocateSurface()
virtual void OnSurfaceAvailable(bool success) = 0;
// Called when the allocated surface is being destroyed. This must either
// replace the surface with MediaCodec#setSurface, or release the MediaCodec
// it's attached to. The client no longer owns the surface and doesn't
// need to call DeallocateSurface();
virtual void OnSurfaceDestroyed() = 0;
// Called on the main thread when a new MediaCodec is configured.
// |media_codec| will be null if configuration failed.
virtual void OnCodecConfigured(
std::unique_ptr<MediaCodecBridge> media_codec) = 0;
protected:
~AVDACodecAllocatorClient() {}
};
// AVDACodecAllocator manages threads for allocating and releasing MediaCodec
// instances. These activities can hang, depending on android version, due
// to mediaserver bugs. AVDACodecAllocator detects these cases, and reports
// on them to allow software fallback if the HW path is hung up.
class MEDIA_GPU_EXPORT AVDACodecAllocator {
public:
static AVDACodecAllocator* Instance();
// Called synchronously when the given surface is being destroyed on the
// browser UI thread.
void OnSurfaceDestroyed(int surface_id);
// Make sure the construction threads are started for |client|. Returns true
// on success.
bool StartThread(AVDACodecAllocatorClient* client);
void StopThread(AVDACodecAllocatorClient* client);
// Returns true if the caller now owns the surface, or false if someone else
// owns the surface. |client| will be notified when the surface is available
// via OnSurfaceAvailable().
bool AllocateSurface(AVDACodecAllocatorClient* client, int surface_id);
// Relinquish ownership of the surface or stop waiting for it to be available.
// The caller must guarantee that when calling this the surface is either no
// longer attached to a MediaCodec, or the MediaCodec it was attached to is
// was released with ReleaseMediaCodec().
void DeallocateSurface(AVDACodecAllocatorClient* client, int surface_id);
// Create and configure a MediaCodec synchronously.
std::unique_ptr<MediaCodecBridge> CreateMediaCodecSync(
scoped_refptr<CodecConfig> codec_config);
// Create and configure a MediaCodec asynchronously. The result is delivered
// via OnCodecConfigured().
virtual void CreateMediaCodecAsync(
base::WeakPtr<AVDACodecAllocatorClient> client,
scoped_refptr<CodecConfig> codec_config);
// Asynchronously release |media_codec| with the attached surface.
// TODO(watk): Bundle the MediaCodec and surface together so you can't get
// this pairing wrong.
void ReleaseMediaCodec(std::unique_ptr<MediaCodecBridge> media_codec,
TaskType task_type,
int surface_id);
// Returns a hint about whether the construction thread has hung for
// |task_type|. Note that if a thread isn't started, then we'll just return
// "not hung", since it'll run on the current thread anyway. The hang
// detector will see no pending jobs in that case, so it's automatic.
bool IsThreadLikelyHung(TaskType task_type);
// Return true if and only if there is any AVDA registered.
bool IsAnyRegisteredAVDA();
// Return the task type to use for a new codec allocation, or nullopt if
// both threads are hung.
base::Optional<TaskType> TaskTypeForAllocation();
// Return the task runner for tasks of type |type|.
scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerFor(TaskType task_type);
// Return a reference to the thread for unit tests.
base::Thread& GetThreadForTesting(TaskType task_type);
protected:
// |tick_clock| and |stop_event| are for tests only.
AVDACodecAllocator(base::TickClock* tick_clock = nullptr,
base::WaitableEvent* stop_event = nullptr);
virtual ~AVDACodecAllocator();
private:
friend class AVDACodecAllocatorTest;
struct OwnerRecord {
AVDACodecAllocatorClient* owner = nullptr;
AVDACodecAllocatorClient* waiter = nullptr;
};
class HangDetector : public base::MessageLoop::TaskObserver {
public:
HangDetector(base::TickClock* tick_clock);
void WillProcessTask(const base::PendingTask& pending_task) override;
void DidProcessTask(const base::PendingTask& pending_task) override;
bool IsThreadLikelyHung();
private:
base::Lock lock_;
// Non-null when a task is currently running.
base::TimeTicks task_start_time_;
base::TickClock* tick_clock_;
DISALLOW_COPY_AND_ASSIGN(HangDetector);
};
// Handy combination of a thread and hang detector for it.
struct ThreadAndHangDetector {
ThreadAndHangDetector(const std::string& name, base::TickClock* tick_clock)
: thread(name), hang_detector(tick_clock) {}
base::Thread thread;
HangDetector hang_detector;
};
void OnMediaCodecAndSurfaceReleased(int surface_id);
// Stop the thread indicated by |index|. This signals stop_event_for_testing_
// after both threads are stopped.
void StopThreadTask(size_t index);
// All registered AVDAs.
std::set<AVDACodecAllocatorClient*> clients_;
// Indexed by surface id.
std::map<int32_t, OwnerRecord> surface_owners_;
// Waitable events for ongoing release tasks indexed by surface id so we can
// wait on the codec release if the surface attached to it is being destroyed.
std::map<int32_t, base::WaitableEvent> pending_codec_releases_;
// Threads for each of TaskType. They are started / stopped as avda instances
// show and and request them. The vector indicies must match TaskType.
std::vector<ThreadAndHangDetector*> threads_;
base::ThreadChecker thread_checker_;
base::WaitableEvent* stop_event_for_testing_;
// For canceling pending StopThreadTask()s.
base::WeakPtrFactory<AVDACodecAllocator> weak_this_factory_;
DISALLOW_COPY_AND_ASSIGN(AVDACodecAllocator);
};
} // namespace media
#endif // MEDIA_GPU_AVDA_CODEC_ALLOCATOR_H_