tests: migrate gtest suite to the ffmpeg_bridge API and add bridge coverage
- Replace direct FFmpeg usage in the test suite (channel layout masks, pixel/sample format constants, AVFrame field access, sws_scale) with the bridge equivalents and the olive::AVFrame adapter - Drop CoreRational.ToAVRational (API removed with core's FFmpeg dependency) and the GetSwsColorspaceFromAVColorSpace tests (helper moved inside the bridge) - New ffmpeg_bridge_test.cpp exercises the C API directly: constants vs core, error strings, pixel format utilities, frames/packets, scaler, resampler, audio graph tempo processing, probe/decoder round-trip on tests/demo.mp4 (including hw-frame transfer), SRT subtitle reading, and encoder end-to-end tests (PNG video and PCM audio probed back)
This commit is contained in:
@@ -7,11 +7,6 @@
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "render/texture.h"
|
||||
@@ -20,19 +15,19 @@ using namespace olive;
|
||||
using namespace olive::core;
|
||||
|
||||
// Test helper to create AVFrame with specific format
|
||||
static AVFramePtr CreateTestFrame(int width, int height, AVPixelFormat fmt,
|
||||
static AVFramePtr CreateTestFrame(int width, int height, int fmt,
|
||||
uint32_t fill_color = 0xFF804020)
|
||||
{
|
||||
AVFramePtr frame = CreateAVFramePtr();
|
||||
frame->width = width;
|
||||
frame->height = height;
|
||||
frame->format = fmt;
|
||||
frame->set_width(width);
|
||||
frame->set_height(height);
|
||||
frame->set_format(fmt);
|
||||
|
||||
if (av_frame_get_buffer(frame.get(), 0) < 0) {
|
||||
if (frame->get_buffer(0) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (av_frame_make_writable(frame.get()) < 0) {
|
||||
if (frame->make_writable() < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -42,9 +37,9 @@ static AVFramePtr CreateTestFrame(int width, int height, AVPixelFormat fmt,
|
||||
uint8_t b = (fill_color >> 8) & 0xFF;
|
||||
uint8_t a = fill_color & 0xFF;
|
||||
|
||||
if (fmt == AV_PIX_FMT_RGBA) {
|
||||
if (fmt == FB_PIX_FMT_RGBA) {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
uint8_t *row = frame->data[0] + y * frame->linesize[0];
|
||||
uint8_t *row = frame->data(0) + y * frame->linesize(0);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
row[x * 4 + 0] = r;
|
||||
row[x * 4 + 1] = g;
|
||||
@@ -52,14 +47,14 @@ static AVFramePtr CreateTestFrame(int width, int height, AVPixelFormat fmt,
|
||||
row[x * 4 + 3] = a;
|
||||
}
|
||||
}
|
||||
} else if (fmt == AV_PIX_FMT_RGBA64) {
|
||||
} else if (fmt == FB_PIX_FMT_RGBA64LE) {
|
||||
uint16_t r16 = (r << 8) | r;
|
||||
uint16_t g16 = (g << 8) | g;
|
||||
uint16_t b16 = (b << 8) | b;
|
||||
uint16_t a16 = (a << 8) | a;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
uint16_t *row = reinterpret_cast<uint16_t *>(
|
||||
frame->data[0] + y * frame->linesize[0]);
|
||||
uint16_t *row = reinterpret_cast<uint16_t *>(frame->data(0) +
|
||||
y * frame->linesize(0));
|
||||
for (int x = 0; x < width; ++x) {
|
||||
row[x * 4 + 0] = r16;
|
||||
row[x * 4 + 1] = g16;
|
||||
@@ -81,11 +76,11 @@ TEST(FormatConversion, U8ToU16)
|
||||
|
||||
// Create U8 frame
|
||||
AVFramePtr u8_frame =
|
||||
CreateTestFrame(width, height, AV_PIX_FMT_RGBA, test_color);
|
||||
CreateTestFrame(width, height, FB_PIX_FMT_RGBA, test_color);
|
||||
ASSERT_NE(u8_frame, nullptr);
|
||||
|
||||
// Verify U8 values
|
||||
uint8_t *first_pixel_u8 = u8_frame->data[0];
|
||||
uint8_t *first_pixel_u8 = u8_frame->data(0);
|
||||
EXPECT_EQ(first_pixel_u8[0], 0xFF); // R
|
||||
EXPECT_EQ(first_pixel_u8[1], 0x80); // G
|
||||
EXPECT_EQ(first_pixel_u8[2], 0x40); // B
|
||||
@@ -93,12 +88,12 @@ TEST(FormatConversion, U8ToU16)
|
||||
|
||||
// Create U16 frame
|
||||
AVFramePtr u16_frame =
|
||||
CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color);
|
||||
CreateTestFrame(width, height, FB_PIX_FMT_RGBA64LE, test_color);
|
||||
ASSERT_NE(u16_frame, nullptr);
|
||||
|
||||
// Verify U16 values (should be U8 value repeated: 0xFF -> 0xFFFF, 0x80 -> 0x8080)
|
||||
uint16_t *first_pixel_u16 =
|
||||
reinterpret_cast<uint16_t *>(u16_frame->data[0]);
|
||||
reinterpret_cast<uint16_t *>(u16_frame->data(0));
|
||||
EXPECT_EQ(first_pixel_u16[0], 0xFFFF); // R
|
||||
EXPECT_EQ(first_pixel_u16[1], 0x8080); // G
|
||||
EXPECT_EQ(first_pixel_u16[2], 0x4040); // B
|
||||
@@ -114,58 +109,69 @@ TEST(FormatConversion, FFmpegU16ToU8)
|
||||
|
||||
// Create U16 frame
|
||||
AVFramePtr u16_frame =
|
||||
CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color);
|
||||
CreateTestFrame(width, height, FB_PIX_FMT_RGBA64LE, test_color);
|
||||
ASSERT_NE(u16_frame, nullptr);
|
||||
|
||||
// Create destination U8 frame
|
||||
AVFramePtr u8_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA, 0);
|
||||
AVFramePtr u8_frame = CreateTestFrame(width, height, FB_PIX_FMT_RGBA, 0);
|
||||
ASSERT_NE(u8_frame, nullptr);
|
||||
|
||||
// Use sws_scale to convert
|
||||
SwsContext *sws_ctx = sws_getContext(width, height, AV_PIX_FMT_RGBA64,
|
||||
width, height, AV_PIX_FMT_RGBA,
|
||||
SWS_POINT, nullptr, nullptr, nullptr);
|
||||
// Use the bridge scaler to convert
|
||||
FBScaler *sws_ctx = fb_scaler_create(width, height, FB_PIX_FMT_RGBA64LE,
|
||||
width, height, FB_PIX_FMT_RGBA,
|
||||
FB_SCALER_POINT);
|
||||
ASSERT_NE(sws_ctx, nullptr);
|
||||
|
||||
sws_scale(sws_ctx, u16_frame->data, u16_frame->linesize, 0, height,
|
||||
u8_frame->data, u8_frame->linesize);
|
||||
sws_freeContext(sws_ctx);
|
||||
uint8_t *src_data[4];
|
||||
int src_linesize[4];
|
||||
uint8_t *dst_data[4];
|
||||
int dst_linesize[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
src_data[i] = u16_frame->data(i);
|
||||
src_linesize[i] = u16_frame->linesize(i);
|
||||
dst_data[i] = u8_frame->data(i);
|
||||
dst_linesize[i] = u8_frame->linesize(i);
|
||||
}
|
||||
|
||||
fb_scaler_scale_slices(sws_ctx, src_data, src_linesize, height, dst_data,
|
||||
dst_linesize);
|
||||
fb_scaler_free(&sws_ctx);
|
||||
|
||||
// Verify conversion (U16 0xFFFF -> U8 0xFF, 0x8080 -> ~0x80, etc.)
|
||||
// Note: FFmpeg sws_scale has rounding offset, so values may be off by 1
|
||||
uint8_t *first_pixel = u8_frame->data[0];
|
||||
uint8_t *first_pixel = u8_frame->data(0);
|
||||
EXPECT_NEAR(first_pixel[0], 0xFF, 1); // R (255 vs 255)
|
||||
EXPECT_NEAR(first_pixel[1], 0x80, 1); // G (128 vs 129)
|
||||
EXPECT_NEAR(first_pixel[2], 0x40, 1); // B (64 vs 64)
|
||||
EXPECT_NEAR(first_pixel[3], 0x20, 1); // A (32 vs 32)
|
||||
}
|
||||
|
||||
// Test VideoParams to AVPixelFormat mapping
|
||||
// Test VideoParams to bridge pixel format mapping
|
||||
TEST(FormatConversion, VideoParamsToAVFormat)
|
||||
{
|
||||
// U8 RGBA
|
||||
VideoParams u8_rgba(320, 240, PixelFormat::U8, 4);
|
||||
AVPixelFormat fmt_u8_rgba = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
int fmt_u8_rgba = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
u8_rgba.format(), u8_rgba.channel_count());
|
||||
EXPECT_EQ(fmt_u8_rgba, AV_PIX_FMT_RGBA);
|
||||
EXPECT_EQ(fmt_u8_rgba, FB_PIX_FMT_RGBA);
|
||||
|
||||
// U16 RGBA
|
||||
VideoParams u16_rgba(320, 240, PixelFormat::U16, 4);
|
||||
AVPixelFormat fmt_u16_rgba = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
int fmt_u16_rgba = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
u16_rgba.format(), u16_rgba.channel_count());
|
||||
EXPECT_EQ(fmt_u16_rgba, AV_PIX_FMT_RGBA64);
|
||||
EXPECT_EQ(fmt_u16_rgba, FB_PIX_FMT_RGBA64LE);
|
||||
|
||||
// U8 RGB
|
||||
VideoParams u8_rgb(320, 240, PixelFormat::U8, 3);
|
||||
AVPixelFormat fmt_u8_rgb = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
int fmt_u8_rgb = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
u8_rgb.format(), u8_rgb.channel_count());
|
||||
EXPECT_EQ(fmt_u8_rgb, AV_PIX_FMT_RGB24);
|
||||
EXPECT_EQ(fmt_u8_rgb, FB_PIX_FMT_RGB24);
|
||||
|
||||
// U16 RGB
|
||||
VideoParams u16_rgb(320, 240, PixelFormat::U16, 3);
|
||||
AVPixelFormat fmt_u16_rgb = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
int fmt_u16_rgb = FFmpegUtils::GetFFmpegPixelFormat(
|
||||
u16_rgb.format(), u16_rgb.channel_count());
|
||||
EXPECT_EQ(fmt_u16_rgb, AV_PIX_FMT_RGB48);
|
||||
EXPECT_EQ(fmt_u16_rgb, FB_PIX_FMT_RGB48LE);
|
||||
}
|
||||
|
||||
// Test row bytes calculation
|
||||
@@ -193,18 +199,18 @@ TEST(FormatConversion, LinesizeAlignment)
|
||||
const int height = 10;
|
||||
|
||||
AVFramePtr frame = CreateAVFramePtr();
|
||||
frame->width = width;
|
||||
frame->height = height;
|
||||
frame->format = AV_PIX_FMT_RGBA;
|
||||
frame->set_width(width);
|
||||
frame->set_height(height);
|
||||
frame->set_format(FB_PIX_FMT_RGBA);
|
||||
|
||||
ASSERT_EQ(av_frame_get_buffer(frame.get(), 0), 0);
|
||||
ASSERT_EQ(frame->get_buffer(0), 0);
|
||||
|
||||
// linesize[0] should be at least width * 4
|
||||
EXPECT_GE(frame->linesize[0], width * 4);
|
||||
EXPECT_GE(frame->linesize(0), width * 4);
|
||||
|
||||
// linesize may be larger due to alignment (typically 32-byte aligned)
|
||||
qDebug() << "Width:" << width << "Expected bytes:" << width * 4
|
||||
<< "Actual linesize:" << frame->linesize[0];
|
||||
<< "Actual linesize:" << frame->linesize(0);
|
||||
}
|
||||
|
||||
// Test loading actual image file
|
||||
@@ -216,34 +222,34 @@ TEST(FormatConversion, LoadImageFile)
|
||||
|
||||
AVFramePtr frame = CreateAVFramePtr();
|
||||
// Just create a simple test frame instead of loading an image
|
||||
frame->width = 1920;
|
||||
frame->height = 1080;
|
||||
frame->format = AV_PIX_FMT_RGBA;
|
||||
if (av_frame_get_buffer(frame.get(), 0) < 0) {
|
||||
frame->set_width(1920);
|
||||
frame->set_height(1080);
|
||||
frame->set_format(FB_PIX_FMT_RGBA);
|
||||
if (frame->get_buffer(0) < 0) {
|
||||
return;
|
||||
}
|
||||
// Fill with orange color (sunrise sky)
|
||||
for (int y = 0; y < frame->height; ++y) {
|
||||
uint8_t *row = frame->data[0] + y * frame->linesize[0];
|
||||
for (int y = 0; y < frame->height(); ++y) {
|
||||
uint8_t *row = frame->data(0) + y * frame->linesize(0);
|
||||
uint8_t r = 255;
|
||||
uint8_t g = 128 + (y * 127) / frame->height; // Gradient from 128 to 255
|
||||
uint8_t g = 128 + (y * 127) / frame->height(); // Gradient from 128 to 255
|
||||
uint8_t b = 64;
|
||||
uint8_t a = 255;
|
||||
for (int x = 0; x < frame->width; ++x) {
|
||||
for (int x = 0; x < frame->width(); ++x) {
|
||||
row[x * 4 + 0] = r;
|
||||
row[x * 4 + 1] = g;
|
||||
row[x * 4 + 2] = b;
|
||||
row[x * 4 + 3] = a;
|
||||
}
|
||||
}
|
||||
ASSERT_NE(frame->data[0], nullptr) << "Failed to create test frame";
|
||||
ASSERT_NE(frame->data(0), nullptr) << "Failed to create test frame";
|
||||
|
||||
EXPECT_EQ(frame->width, 1920);
|
||||
EXPECT_EQ(frame->height, 1080);
|
||||
EXPECT_EQ(frame->width(), 1920);
|
||||
EXPECT_EQ(frame->height(), 1080);
|
||||
|
||||
// Check first pixel (top-left corner of the sunrise image)
|
||||
// Based on the image, it should have some orange/pink color in the sky area
|
||||
uint8_t *first_pixel = frame->data[0];
|
||||
uint8_t *first_pixel = frame->data(0);
|
||||
qDebug() << "First pixel RGBA:" << first_pixel[0] << first_pixel[1]
|
||||
<< first_pixel[2] << first_pixel[3];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user