aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Staessens <dstaessens@google.com>2021-02-18 16:25:57 +0900
committerChih-Yu Huang <akahuang@google.com>2021-03-15 14:41:33 +0900
commit505d5cba9fb41ddca8b7799207af6e69cb422a88 (patch)
treed7e17646fdfeee8a54f26a489e8c306d43739ad7
parent2a9793618bd9778598c2867acc8846f3161d765d (diff)
downloadv4l2_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.cpp94
-rw-r--r--tests/c2_e2e_test/jni/common.h35
-rw-r--r--tests/c2_e2e_test/jni/mediacodec_encoder.cpp49
-rw-r--r--tests/c2_e2e_test/jni/mediacodec_encoder.h11
-rw-r--r--tests/c2_e2e_test/jni/video_encoder_e2e_test.cpp47
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], &timestamp, 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());