diff options
author | David Staessens <dstaessens@google.com> | 2021-02-18 16:25:57 +0900 |
---|---|---|
committer | Chih-Yu Huang <akahuang@google.com> | 2021-03-15 14:41:33 +0900 |
commit | 505d5cba9fb41ddca8b7799207af6e69cb422a88 (patch) | |
tree | d7e17646fdfeee8a54f26a489e8c306d43739ad7 | |
parent | 2a9793618bd9778598c2867acc8846f3161d765d (diff) | |
download | v4l2_codec2-505d5cba9fb41ddca8b7799207af6e69cb422a88.tar.gz |
v4l2_codec2: Add support for VP8/VP9 to the video encoder tests.
This CL adds support for the VP8 and VP9 codecs to the e2e video
encoder tests.
Bug: 155138243
Test: tast run DUT arc.VideoEncodeAccel.vp8_1080p_i420 on hatch
Change-Id: Ic430bf0d6fd7abc9cb4f7e7cc15e96273569a7cc
-rw-r--r-- | tests/c2_e2e_test/jni/common.cpp | 94 | ||||
-rw-r--r-- | tests/c2_e2e_test/jni/common.h | 35 | ||||
-rw-r--r-- | tests/c2_e2e_test/jni/mediacodec_encoder.cpp | 49 | ||||
-rw-r--r-- | tests/c2_e2e_test/jni/mediacodec_encoder.h | 11 | ||||
-rw-r--r-- | tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp | 47 |
5 files changed, 206 insertions, 30 deletions
diff --git a/tests/c2_e2e_test/jni/common.cpp b/tests/c2_e2e_test/jni/common.cpp index 2dcf47e..50f4cdf 100644 --- a/tests/c2_e2e_test/jni/common.cpp +++ b/tests/c2_e2e_test/jni/common.cpp @@ -77,6 +77,100 @@ bool InputFileASCII::ReadLine(std::string* line) { return false; // no more lines } +IVFWriter::IVFWriter(std::ofstream* output_file, VideoCodecType codec) + : output_file_(output_file), codec_(codec) {} + +bool IVFWriter::WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames) { + constexpr uint16_t kIVFHeaderSize = 32; + char header[kIVFHeaderSize]; + + // Helper lambdas to write 16bit and 32bit data, expects the device to use little endian. + auto write16 = [&header](int i, uint16_t data) { memcpy(&header[i], &data, sizeof(data)); }; + auto write32 = [&header](int i, uint32_t data) { memcpy(&header[i], &data, sizeof(data)); }; + + const char* codec_str; + switch (codec_) { + case VideoCodecType::VP8: + codec_str = "VP80"; + break; + case VideoCodecType::VP9: + codec_str = "VP90"; + break; + default: + printf("[ERR] Unknown codec: \n"); + return false; + } + + strcpy(&header[0], "DKIF"); // Bytes 0-3 of an IVF file header always contain 'DKIF' signature. + constexpr uint16_t kVersion = 0; + write16(4, kVersion); + write16(6, kIVFHeaderSize); + strcpy(&header[8], codec_str); + write16(12, resolution.width); + write16(14, resolution.height); + write32(16, frame_rate); + write32(20, 1); + write32(24, num_frames); + write32(28, 0); // Reserved. + + output_file_->write(header, kIVFHeaderSize); + return !output_file_->bad(); +} + +bool IVFWriter::WriteFrame(const uint8_t* data, uint32_t data_size, uint64_t timestamp) { + constexpr size_t kIVFFrameHeaderSize = 12; + char frame_header[kIVFFrameHeaderSize]; + memcpy(&frame_header[0], &data_size, sizeof(data_size)); + memcpy(&frame_header[4], ×tamp, sizeof(timestamp)); + output_file_->write(frame_header, kIVFFrameHeaderSize); + output_file_->write(reinterpret_cast<const char*>(data), data_size); + return !output_file_->bad(); +} + +bool IVFWriter::SetNumFrames(uint32_t num_frames) { + output_file_->seekp(24); + output_file_->write(reinterpret_cast<const char*>(&num_frames), sizeof(num_frames)); + return !output_file_->bad(); +} + +bool OutputFile::Open(const std::string& file_path, VideoCodecType codec) { + output_file_.open(file_path, std::ofstream::binary); + if (!output_file_.is_open()) { + return false; + } + + if ((codec == VideoCodecType::VP8) || (codec == VideoCodecType::VP9)) { + ivf_writer_ = std::make_unique<IVFWriter>(&output_file_, codec); + } + return true; +} + +void OutputFile::Close() { + if (ivf_writer_) { + ivf_writer_->SetNumFrames(frame_index_); + ivf_writer_.reset(); + } + output_file_.close(); +} + +bool OutputFile::IsOpen() { + return output_file_.is_open(); +} + +// Write the file header. +bool OutputFile::WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames) { + return !ivf_writer_ || ivf_writer_->WriteHeader(resolution, frame_rate, num_frames); +} + +bool OutputFile::WriteFrame(uint32_t data_size, const uint8_t* data) { + if (ivf_writer_) { + return (ivf_writer_->WriteFrame(data, data_size, frame_index_++)); + } else { + output_file_.write(reinterpret_cast<const char*>(data), data_size); + return (output_file_.fail()); + } +} + bool FPSCalculator::RecordFrameTimeDiff() { int64_t now_us = GetNowUs(); if (last_frame_time_us_ != 0) { diff --git a/tests/c2_e2e_test/jni/common.h b/tests/c2_e2e_test/jni/common.h index 8fdde80..ea8d212 100644 --- a/tests/c2_e2e_test/jni/common.h +++ b/tests/c2_e2e_test/jni/common.h @@ -7,6 +7,7 @@ #include <fstream> #include <ios> +#include <memory> #include <string> #include <vector> @@ -101,6 +102,40 @@ public: bool ReadLine(std::string* line); }; +// IVF file writer, can be used to write an encoded VP8/9 video to disk. +class IVFWriter { +public: + IVFWriter(std::ofstream* output_file, VideoCodecType codec); + + // Write the IVF file header. + bool WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames); + // Append the specified frame data to the IVF file. + bool WriteFrame(const uint8_t* data, uint32_t data_size, uint64_t timestamp); + // Set the number of video frames in the IVF file header. + bool SetNumFrames(uint32_t num_frames); + +private: + std::ofstream* output_file_; + VideoCodecType codec_ = VideoCodecType::UNKNOWN; +}; + +class OutputFile { +public: + bool Open(const std::string& file_path, VideoCodecType codec); + void Close(); + bool IsOpen(); + + // Write the video file header. + bool WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames); + // Append the specified frame data to the video file. + bool WriteFrame(uint32_t data_size, const uint8_t* data); + +private: + std::ofstream output_file_; + std::unique_ptr<IVFWriter> ivf_writer_; + uint64_t frame_index_ = 0; +}; + // The helper class to calculate FPS. class FPSCalculator { public: diff --git a/tests/c2_e2e_test/jni/mediacodec_encoder.cpp b/tests/c2_e2e_test/jni/mediacodec_encoder.cpp index fcd9644..d15f285 100644 --- a/tests/c2_e2e_test/jni/mediacodec_encoder.cpp +++ b/tests/c2_e2e_test/jni/mediacodec_encoder.cpp @@ -17,10 +17,13 @@ namespace android { namespace { -// The values are defined at +// These values are defined at // <android_root>/frameworks/base/media/java/android/media/MediaCodecInfo.java. constexpr int32_t COLOR_FormatYUV420Planar = 19; constexpr int32_t BITRATE_MODE_CBR = 2; +constexpr int32_t AVCProfileBaseline = 0x01; +constexpr int32_t VP8ProfileMain = 0x01; +constexpr int32_t VP9Profile0 = 0x01; // The time interval between two key frames. constexpr int32_t kIFrameIntervalSec = 10; @@ -40,7 +43,11 @@ std::vector<const char*> GetHWVideoEncoderNames(VideoCodecType type) { switch (type) { case VideoCodecType::H264: return {"c2.v4l2.avc.encoder", "c2.vea.avc.encoder"}; - default: // unsupported type: VP8, VP9, or unknown + case VideoCodecType::VP8: + return {"c2.v4l2.vp8.encoder"}; // Only supported on ARCVM + case VideoCodecType::VP9: + return {"c2.v4l2.vp9.encoder"}; // Only supported on ARCVM + default: return {}; } } @@ -52,16 +59,35 @@ std::vector<const char*> GetSWVideoEncoderNames(VideoCodecType type) { switch (type) { case VideoCodecType::H264: return {"c2.android.avc.encoder", "OMX.google.h264.encoder"}; - default: // unsupported type: VP8, VP9, or unknown + case VideoCodecType::VP8: + return {"c2.android.vp8.encoder", "OMX.google.vp8.encoder"}; + case VideoCodecType::VP9: + return {"c2.android.vp9.encoder", "OMX.google.vp9.encoder"}; + default: return {}; } } +// Helper function to get the profile associated with the specified codec. +int32_t GetProfile(VideoCodecType type) { + switch (type) { + case VideoCodecType::H264: + return AVCProfileBaseline; + case VideoCodecType::VP8: + return VP8ProfileMain; + case VideoCodecType::VP9: + return VP9Profile0; + default: + return AVCProfileBaseline; + } +} + } // namespace // static -std::unique_ptr<MediaCodecEncoder> MediaCodecEncoder::Create( - std::string input_path, Size visible_size, bool use_sw_encoder) { +std::unique_ptr<MediaCodecEncoder> MediaCodecEncoder::Create(std::string input_path, + VideoCodecType type, Size visible_size, + bool use_sw_encoder) { if (visible_size.width <= 0 || visible_size.height <= 0 || visible_size.width % 2 == 1 || visible_size.height % 2 == 1) { ALOGE("Size is not valid: %dx%d", visible_size.width, visible_size.height); @@ -82,8 +108,6 @@ std::unique_ptr<MediaCodecEncoder> MediaCodecEncoder::Create( } AMediaCodec* codec = nullptr; - // Only H264 is supported now. - VideoCodecType type = VideoCodecType::H264; auto encoder_names = use_sw_encoder ? GetSWVideoEncoderNames(type) : GetHWVideoEncoderNames(type); for (const auto& encoder_name : encoder_names) { @@ -98,17 +122,19 @@ std::unique_ptr<MediaCodecEncoder> MediaCodecEncoder::Create( return nullptr; } - return std::unique_ptr<MediaCodecEncoder>(new MediaCodecEncoder( - codec, std::move(input_file), visible_size, buffer_size, file_size / buffer_size)); + return std::unique_ptr<MediaCodecEncoder>( + new MediaCodecEncoder(codec, type, std::move(input_file), visible_size, buffer_size, + file_size / buffer_size)); } -MediaCodecEncoder::MediaCodecEncoder(AMediaCodec* codec, +MediaCodecEncoder::MediaCodecEncoder(AMediaCodec* codec, VideoCodecType type, std::unique_ptr<CachedInputFileStream> input_file, Size size, size_t buffer_size, size_t num_total_frames) : kVisibleSize(size), kBufferSize(buffer_size), kNumTotalFrames(num_total_frames), codec_(codec), + type_(type), num_encoded_frames_(num_total_frames), input_file_(std::move(input_file)) {} @@ -134,7 +160,8 @@ void MediaCodecEncoder::Rewind() { bool MediaCodecEncoder::Configure(int32_t bitrate, int32_t framerate) { ALOGV("Configure encoder bitrate=%d, framerate=%d", bitrate, framerate); AMediaFormat* format = AMediaFormat_new(); - AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "video/avc"); + AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, GetMimeType(type_)); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PROFILE, GetProfile(type_)); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Planar); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BITRATE_MODE, BITRATE_MODE_CBR); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, kIFrameIntervalSec); diff --git a/tests/c2_e2e_test/jni/mediacodec_encoder.h b/tests/c2_e2e_test/jni/mediacodec_encoder.h index aa25559..53e6fdf 100644 --- a/tests/c2_e2e_test/jni/mediacodec_encoder.h +++ b/tests/c2_e2e_test/jni/mediacodec_encoder.h @@ -18,8 +18,8 @@ namespace android { class MediaCodecEncoder { public: // Checks the argument and create MediaCodecEncoder instance. - static std::unique_ptr<MediaCodecEncoder> Create( - std::string input_path, Size visible_size, bool use_sw_encoder); + static std::unique_ptr<MediaCodecEncoder> Create(std::string input_path, VideoCodecType type, + Size visible_size, bool use_sw_encoder); MediaCodecEncoder() = delete; ~MediaCodecEncoder(); @@ -54,8 +54,9 @@ public: void set_run_at_fps(bool run_at_fps); private: - MediaCodecEncoder(AMediaCodec* codec, std::unique_ptr<CachedInputFileStream> inputFile, - Size size, size_t bufferSize, size_t numTotalFrames); + MediaCodecEncoder(AMediaCodec* codec, VideoCodecType type, + std::unique_ptr<CachedInputFileStream> inputFile, Size size, + size_t bufferSize, size_t numTotalFrames); // Read the content from the |input_file_| and feed into the input buffer. // |index| is the index of the target input buffer. @@ -80,6 +81,8 @@ private: // The target mediacodec encoder. AMediaCodec* codec_; + // The output codec type. + VideoCodecType type_; // The number of frames to encode. size_t num_encoded_frames_; // The input video raw stream file. The file size must be the multiple of diff --git a/tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp b/tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp index bfda8d4..5215052 100644 --- a/tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp +++ b/tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp @@ -71,7 +71,6 @@ public: // (see http://www.fourcc.org/yuv.php#IYUV). // - |width| and |height| are in pixels. // - |profile| to encode into (values of VideoCodecProfile). - // NOTE: Only H264PROFILE_MAIN(1) is supported. Now we ignore this value. // - |output_file_path| filename to save the encoded stream to (optional). // The format for H264 is Annex-B byte stream. // - |requested_bitrate| requested bitrate in bits per second. @@ -98,7 +97,21 @@ public: if (fields.size() >= 4 && !fields[3].empty()) { int profile = stoi(fields[3]); - if (profile != 1) printf("[WARN] Only H264PROFILE_MAIN(1) is supported.\n"); + switch (profile) { + case VideoCodecProfile::H264PROFILE_MAIN: + codec_ = VideoCodecType::H264; + break; + case VideoCodecProfile::VP8PROFILE_ANY: + codec_ = VideoCodecType::VP8; + break; + case VideoCodecProfile::VP9PROFILE_PROFILE0: + codec_ = VideoCodecType::VP9; + break; + default: + printf("[WARN] Only H264PROFILE_MAIN, VP8PROFILE_ANY and VP9PROFILE_PROFILE0 are" + "supported.\n"); + codec_ = VideoCodecType::H264; + } } if (fields.size() >= 5 && !fields[4].empty()) { @@ -141,6 +154,7 @@ public: } Size visible_size() const { return visible_size_; } + VideoCodecType codec() const { return codec_; } std::string input_file_path() const { return input_file_path_; } std::string output_file_path() const { return output_file_path_; } int requested_bitrate() const { return requested_bitrate_; } @@ -159,6 +173,7 @@ private: ConfigureCallback* configure_cb_; Size visible_size_; + VideoCodecType codec_; std::string input_file_path_; std::string output_file_path_; @@ -171,12 +186,10 @@ private: class C2VideoEncoderE2ETest : public testing::Test { public: // Callback functions of getting output buffers from encoder. - void WriteOutputBufferToFile(const uint8_t* data, const AMediaCodecBufferInfo& info) { - if (output_file_.is_open()) { - output_file_.write(reinterpret_cast<const char*>(data), info.size); - if (output_file_.fail()) { - printf("[ERR] Failed to write encoded buffer into file.\n"); - } + void WriteOutputBufferToFile(VideoCodecType type, const uint8_t* data, + const AMediaCodecBufferInfo& info) { + if (output_file_.IsOpen() && !output_file_.WriteFrame(info.size, data)) { + printf("[ERR] Failed to write encoded buffer into file.\n"); } } @@ -186,8 +199,8 @@ public: protected: void SetUp() override { - encoder_ = MediaCodecEncoder::Create( - g_env->input_file_path(), g_env->visible_size(), g_env->use_sw_encoder()); + encoder_ = MediaCodecEncoder::Create(g_env->input_file_path(), g_env->codec(), + g_env->visible_size(), g_env->use_sw_encoder()); ASSERT_TRUE(encoder_); g_env->configure_cb()->OnCodecReady(encoder_.get()); @@ -201,18 +214,22 @@ protected: void TearDown() override { EXPECT_TRUE(encoder_->Stop()); - output_file_.close(); + output_file_.Close(); encoder_.reset(); } bool CreateOutputFile() { if (g_env->output_file_path().empty()) return false; - output_file_.open(g_env->output_file_path(), std::ofstream::binary); - if (!output_file_.is_open()) { + if (!output_file_.Open(g_env->output_file_path(), g_env->codec())) { printf("[ERR] Failed to open file: %s\n", g_env->output_file_path().c_str()); return false; } + if (!output_file_.WriteHeader(g_env->visible_size(), g_env->requested_framerate(), 0)) { + printf("[ERR] Failed to write file header\n"); + return false; + } + return true; } @@ -224,7 +241,7 @@ protected: std::unique_ptr<MediaCodecEncoder> encoder_; // The output file to write the encoded video bitstream. - std::ofstream output_file_; + OutputFile output_file_; // Used to accumulate the output buffer size. size_t total_output_buffer_size_; }; @@ -270,7 +287,7 @@ TEST_F(C2VideoEncoderE2ETest, TestSimpleEncode) { // Write the output buffers to file. if (CreateOutputFile()) { encoder_->SetOutputBufferReadyCb(std::bind(&C2VideoEncoderE2ETest::WriteOutputBufferToFile, - this, std::placeholders::_1, + this, g_env->codec(), std::placeholders::_1, std::placeholders::_2)); } encoder_->set_run_at_fps(g_env->run_at_fps()); |