blob: f2cdcd6c5781b1c83ae2745790bbdb9dd8185980 [file] [log] [blame]
// Copyright 2018 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_TEST_VIDEO_TEST_HELPERS_H_
#define MEDIA_GPU_TEST_VIDEO_TEST_HELPERS_H_
#include <memory>
#include <string>
#include <vector>
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_codecs.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
#include "media/base/video_types.h"
#include "media/filters/ivf_parser.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace gpu {
class GpuMemoryBufferFactory;
} // namespace gpu
namespace media {
namespace test {
class Video;
// Helper class allowing one thread to wait on a notification from another.
// If notifications come in faster than they are Wait()'d for, they are
// accumulated (so exactly as many Wait() calls will unblock as Notify() calls
// were made, regardless of order).
template <typename StateEnum>
class ClientStateNotification {
public:
ClientStateNotification();
~ClientStateNotification();
// Used to notify a single waiter of a ClientState.
void Notify(StateEnum state);
// Used by waiters to wait for the next ClientState Notification.
StateEnum Wait();
private:
base::Lock lock_;
base::ConditionVariable cv_;
base::queue<StateEnum> pending_states_for_notification_;
};
template <typename StateEnum>
ClientStateNotification<StateEnum>::ClientStateNotification() : cv_(&lock_) {}
template <typename StateEnum>
ClientStateNotification<StateEnum>::~ClientStateNotification() {}
template <typename StateEnum>
void ClientStateNotification<StateEnum>::Notify(StateEnum state) {
base::AutoLock auto_lock(lock_);
pending_states_for_notification_.push(state);
cv_.Signal();
}
template <typename StateEnum>
StateEnum ClientStateNotification<StateEnum>::Wait() {
base::AutoLock auto_lock(lock_);
while (pending_states_for_notification_.empty())
cv_.Wait();
StateEnum ret = pending_states_for_notification_.front();
pending_states_for_notification_.pop();
return ret;
}
struct IvfFrame {
IvfFrameHeader header;
uint8_t* data = nullptr;
};
// Read functions to fill IVF file header and IVF frame header from |data|.
// |data| must have sufficient length.
IvfFileHeader GetIvfFileHeader(const base::span<const uint8_t>& data);
IvfFrameHeader GetIvfFrameHeader(const base::span<const uint8_t>& data);
// The helper class to save data as ivf format.
class IvfWriter {
public:
IvfWriter(base::FilePath output_filepath);
bool WriteFileHeader(VideoCodec codec,
const gfx::Size& resolution,
uint32_t frame_rate,
uint32_t num_frames);
bool WriteFrame(uint32_t data_size, uint64_t timestamp, const uint8_t* data);
private:
base::File output_file_;
};
// Helper to extract fragments from encoded video stream.
class EncodedDataHelper {
public:
EncodedDataHelper(const std::vector<uint8_t>& stream,
VideoCodecProfile profile);
~EncodedDataHelper();
// Compute and return the next fragment to be sent to the decoder, starting
// from the current position in the stream, and advance the current position
// to after the returned fragment.
scoped_refptr<DecoderBuffer> GetNextBuffer();
static bool HasConfigInfo(const uint8_t* data,
size_t size,
VideoCodecProfile profile);
void Rewind() { next_pos_to_decode_ = 0; }
bool AtHeadOfStream() const { return next_pos_to_decode_ == 0; }
bool ReachEndOfStream() const { return next_pos_to_decode_ == data_.size(); }
size_t num_skipped_fragments() { return num_skipped_fragments_; }
private:
// For h.264/HEVC.
scoped_refptr<DecoderBuffer> GetNextFragment();
// For VP8/9.
scoped_refptr<DecoderBuffer> GetNextFrame();
absl::optional<IvfFrameHeader> GetNextIvfFrameHeader() const;
absl::optional<IvfFrame> ReadNextIvfFrame();
// Helpers for GetBytesForNextFragment above.
size_t GetBytesForNextNALU(size_t pos);
bool IsNALHeader(const std::string& data, size_t pos);
bool LookForSPS(size_t* skipped_fragments_count);
std::string data_;
VideoCodecProfile profile_;
size_t next_pos_to_decode_ = 0;
size_t num_skipped_fragments_ = 0;
};
#if defined(ARCH_CPU_ARM_FAMILY)
// ARM performs CPU cache management with CPU cache line granularity. We thus
// need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
// Otherwise newer kernels will refuse to accept them, and on older kernels
// we'll be treating ourselves to random corruption.
// Moreover, some hardware codecs require 128-byte alignment for physical
// buffers.
constexpr size_t kPlatformBufferAlignment = 128;
#else
constexpr size_t kPlatformBufferAlignment = 8;
#endif
// Helper to align data and extract frames from raw video streams.
// GetNextFrame() returns VideoFrames with a specified |storage_type|. The
// VideoFrames are aligned by the specified |alignment| in the case of
// MojoSharedBuffer VideoFrame. On the other hand, GpuMemoryBuffer based
// VideoFrame is determined by the GpuMemoryBuffer allocation backend.
// GetNextFrame() returns valid frame if AtEndOfStream() returns false, i.e.,
// until GetNextFrame() is called |num_read_frames| times.
// |num_frames| is the number of frames contained in |stream|. |num_read_frames|
// can be larger than |num_frames|.
// If |reverse| is true , GetNextFrame() for a frame returns frames in a
// round-trip playback fashion (0, 1,.., |num_frames| - 2, |num_frames| - 1,
// |num_frames| - 1, |num_frames_| - 2,.., 1, 0, 0, 1,..) so that the content of
// returned frames is consecutive.
// If |reverse| is false, GetNextFrame() just loops the stream (0, 1,..,
// |num_frames| - 2, |num_frames| - 1, 0, 1,..), so the content of returned
// frames is not consecutive.
class AlignedDataHelper {
public:
AlignedDataHelper(
const std::vector<uint8_t>& stream,
uint32_t num_frames,
uint32_t num_read_frames,
bool reverse,
VideoPixelFormat pixel_format,
const gfx::Size& src_coded_size,
const gfx::Size& dst_coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
uint32_t frame_rate,
VideoFrame::StorageType storage_type,
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory);
~AlignedDataHelper();
// Compute and return the next frame to be sent to the encoder.
scoped_refptr<VideoFrame> GetNextFrame();
// Rewind to the position of the video stream.
void Rewind();
// Check whether we are at the start of the video stream.
bool AtHeadOfStream() const;
// Check whether we are at the end of the video stream.
bool AtEndOfStream() const;
// Change the timing between frames.
void UpdateFrameRate(uint32_t frame_rate);
private:
struct VideoFrameData;
static VideoFrameLayout GetAlignedVideoFrameLayout(
VideoPixelFormat pixel_format,
const gfx::Size& dimension,
const uint32_t alignment,
std::vector<size_t>* plane_rows,
size_t* video_frame_size);
// Create MojoSharedMemory VideoFrames whose memory are aligned by
// kPlatformBufferAlignment.
void InitializeAlignedMemoryFrames(const std::vector<uint8_t>& stream,
const VideoPixelFormat pixel_format,
const gfx::Size& src_coded_size,
const gfx::Size& dst_coded_size);
// Create GpuMemoryBuffer VideoFrame whose alignments is determined by
// a GpuMemoryBuffer allocation backend (e.g. minigbm).
void InitializeGpuMemoryBufferFrames(const std::vector<uint8_t>& stream,
const VideoPixelFormat pixel_format,
const gfx::Size& src_coded_size,
const gfx::Size& dst_coded_size);
// The number of frames in the given |stream|.
const uint32_t num_frames_;
// The number of frames to be read. It may be more than |num_frames_|.
const uint32_t num_read_frames_;
const bool reverse_;
// The index of VideoFrame to be read next.
uint32_t frame_index_ = 0;
const VideoFrame::StorageType storage_type_;
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
// The layout of VideoFrames returned by GetNextFrame().
absl::optional<VideoFrameLayout> layout_;
const gfx::Rect visible_rect_;
const gfx::Size natural_size_;
base::TimeDelta time_stamp_interval_;
base::TimeDelta elapsed_frame_time_;
// The frame data returned by GetNextFrame().
std::vector<VideoFrameData> video_frame_data_;
};
// Small helper class to extract video frames from raw data streams.
// However, the data wrapped by VideoFrame is not guaranteed to be aligned.
// This class doesn't change |video|, but cannot be mark it as constant because
// GetFrame() returns non const |data_| wrapped by the returned VideoFrame.
// If |reverse| is true , GetNextFrame() for a frame returns frames in a
// round-trip playback fashion (0, 1,.., |num_frames| - 2, |num_frames| - 1,
// |num_frames| - 1, |num_frames_| - 2,.., 1, 0, 0, 1,..).
// If |reverse| is false, GetNextFrame() just loops the stream (0, 1,..,
// |num_frames| - 2, |num_frames| - 1, 0, 1,..).
class RawDataHelper {
public:
static std::unique_ptr<RawDataHelper> Create(Video* video, bool reverse);
~RawDataHelper();
// Returns i-th VideoFrame in |video|. The returned frame doesn't own the
// underlying video data.
scoped_refptr<const VideoFrame> GetFrame(size_t index);
private:
RawDataHelper(Video* video,
bool reverse_,
size_t frame_size,
const VideoFrameLayout& layout);
// |video| and its associated data must outlive this class and VideoFrames
// returned by GetFrame().
Video* const video_;
const bool reverse_;
// The size of one video frame.
const size_t frame_size_;
// The layout of VideoFrames returned by GetFrame().
const absl::optional<VideoFrameLayout> layout_;
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_TEST_HELPERS_H_