ffmpeg_bridge: fix cross-platform CI failures (Ubuntu/macOS/Windows)
Ubuntu (FFmpeg 7.0) build failure: the public FB_PIX_FMT_* values were hardcoded to AVPixelFormat enum values, but those shift between FFmpeg releases (new formats are inserted mid-enum: X2RGB10 in 7.0, RGBF16/GRAYF16 later), so the static_asserts in internal.h failed and RGBF16LE/GRAYF16LE did not exist at all. The FB pixel format values are now fixed identifiers translated inside the library by pixel format name (stable across releases): - fb::PixFmtToAV/PixFmtFromAV resolve the static table by name; formats a decoder produces that have no static identifier (hardware downloads like nv12/p010le/p210le) receive process-local dynamic ids >= 1000 so they keep round-tripping through the API (scaler etc.) instead of collapsing to NONE and crashing sws. - All crossing points translated: scaler, frame get/set format, decoder/probe stream info, encoder config and write_video_frame, fb_pix_fmt_* utilities, fb_find_best_pix_fmt_of_list (which also reinterpret_cast the caller's list; now translated element-wise and properly NONE-terminated). - nv12 and p010le get static identifiers as the common hardware download formats. - Hardware frame detection no longer compares pixel formats (breaks across format spaces); it uses the existing fb_frame_is_hw(). macOS test failures (FFmpegBridgeEncoder.WritePngVideoAndProbeBack, WritePcmAudioAndProbeBack): config.filename held a dangling pointer to a temporary QByteArray (UB; happened to work on Linux). Keep the QByteArray alive for the encoder's lifetime. Windows (MSYS2 UCRT64) build failure: timecodefunctions.h used int64_t without including <cstdint> (previously pulled in transitively). Full gtest suite: 584 passed.
This commit is contained in:
@@ -1187,7 +1187,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational &time,
|
|||||||
AVFramePtr FFmpegDecoder::TransferHardwareFrame(AVFramePtr f)
|
AVFramePtr FFmpegDecoder::TransferHardwareFrame(AVFramePtr f)
|
||||||
{
|
{
|
||||||
if (!fb_decoder_hwaccel_enabled(instance_) ||
|
if (!fb_decoder_hwaccel_enabled(instance_) ||
|
||||||
f->format() != fb_decoder_hw_pix_fmt(instance_)) {
|
!fb_frame_is_hw(f->handle())) {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include "rational.h"
|
#include "rational.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace olive::core
|
namespace olive::core
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -69,9 +69,12 @@ extern "C" {
|
|||||||
#define FB_SCALER_POINT 0x10
|
#define FB_SCALER_POINT 0x10
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pixel formats. Values deliberately mirror AVPixelFormat so that the library
|
* Pixel formats. These are fixed identifiers; the library maps them to the
|
||||||
* can use them directly; every value is static_assert'ed against the real
|
* AVPixelFormat values of whatever FFmpeg build it was compiled against
|
||||||
* FFmpeg headers inside the library. Callers must treat them as opaque.
|
* (AVPixelFormat enum values shift between FFmpeg releases). Callers must
|
||||||
|
* treat them as opaque. A format may be unknown to an older FFmpeg build,
|
||||||
|
* in which case library calls reject it gracefully. Decoded frames whose
|
||||||
|
* format has no entry here receive an opaque process-local id >= 1000.
|
||||||
*/
|
*/
|
||||||
typedef enum FBPixelFormat {
|
typedef enum FBPixelFormat {
|
||||||
FB_PIX_FMT_NONE = -1,
|
FB_PIX_FMT_NONE = -1,
|
||||||
@@ -85,6 +88,7 @@ typedef enum FBPixelFormat {
|
|||||||
FB_PIX_FMT_YUVJ420P = 12,
|
FB_PIX_FMT_YUVJ420P = 12,
|
||||||
FB_PIX_FMT_YUVJ422P = 13,
|
FB_PIX_FMT_YUVJ422P = 13,
|
||||||
FB_PIX_FMT_YUVJ444P = 14,
|
FB_PIX_FMT_YUVJ444P = 14,
|
||||||
|
FB_PIX_FMT_NV12 = 23,
|
||||||
FB_PIX_FMT_RGBA = 26,
|
FB_PIX_FMT_RGBA = 26,
|
||||||
FB_PIX_FMT_GRAY16LE = 30,
|
FB_PIX_FMT_GRAY16LE = 30,
|
||||||
FB_PIX_FMT_YUV440P = 31,
|
FB_PIX_FMT_YUV440P = 31,
|
||||||
@@ -98,6 +102,7 @@ typedef enum FBPixelFormat {
|
|||||||
FB_PIX_FMT_YUV422P12LE = 127,
|
FB_PIX_FMT_YUV422P12LE = 127,
|
||||||
FB_PIX_FMT_YUV444P12LE = 131,
|
FB_PIX_FMT_YUV444P12LE = 131,
|
||||||
FB_PIX_FMT_YUVJ411P = 138,
|
FB_PIX_FMT_YUVJ411P = 138,
|
||||||
|
FB_PIX_FMT_P010LE = 158,
|
||||||
FB_PIX_FMT_GRAYF32LE = 183,
|
FB_PIX_FMT_GRAYF32LE = 183,
|
||||||
FB_PIX_FMT_RGBAF16LE = 207,
|
FB_PIX_FMT_RGBAF16LE = 207,
|
||||||
FB_PIX_FMT_RGBF32LE = 218,
|
FB_PIX_FMT_RGBF32LE = 218,
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ int fb_decoder_get_stream_info(const FBDecoder *decoder, FBStreamInfo *out)
|
|||||||
out->has_decoder = 1; // stream is open, so a decoder was found
|
out->has_decoder = 1; // stream is open, so a decoder was found
|
||||||
out->width = par->width;
|
out->width = par->width;
|
||||||
out->height = par->height;
|
out->height = par->height;
|
||||||
out->pixel_format = par->format;
|
out->pixel_format = fb::PixFmtFromAV(AVPixelFormat(par->format));
|
||||||
out->field_order = decoder->codec_ctx ? decoder->codec_ctx->field_order :
|
out->field_order = decoder->codec_ctx ? decoder->codec_ctx->field_order :
|
||||||
AV_FIELD_UNKNOWN;
|
AV_FIELD_UNKNOWN;
|
||||||
out->color_range = par->color_range;
|
out->color_range = par->color_range;
|
||||||
@@ -499,5 +499,8 @@ int fb_decoder_hwaccel_enabled(const FBDecoder *decoder)
|
|||||||
|
|
||||||
int fb_decoder_hw_pix_fmt(const FBDecoder *decoder)
|
int fb_decoder_hw_pix_fmt(const FBDecoder *decoder)
|
||||||
{
|
{
|
||||||
return decoder ? int(decoder->hw_pix_fmt) : FB_PIX_FMT_NONE;
|
// Hardware pixel formats have no static FB_PIX_FMT_* identifier, so this
|
||||||
|
// returns a process-local dynamic id for them. Use fb_frame_is_hw() to
|
||||||
|
// detect hardware frames.
|
||||||
|
return decoder ? fb::PixFmtFromAV(decoder->hw_pix_fmt) : FB_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ struct FBEncoder {
|
|||||||
int video_frame_rate_num = 0;
|
int video_frame_rate_num = 0;
|
||||||
int video_frame_rate_den = 1;
|
int video_frame_rate_den = 1;
|
||||||
std::string video_pix_fmt;
|
std::string video_pix_fmt;
|
||||||
|
// In AVPixelFormat space (translated from the FB config value at create)
|
||||||
int video_src_pix_fmt = FB_PIX_FMT_NONE;
|
int video_src_pix_fmt = FB_PIX_FMT_NONE;
|
||||||
int video_color_range = FB_COLOR_RANGE_UNSPEC;
|
int video_color_range = FB_COLOR_RANGE_UNSPEC;
|
||||||
int video_field_order = FB_FIELD_ORDER_PROGRESSIVE;
|
int video_field_order = FB_FIELD_ORDER_PROGRESSIVE;
|
||||||
@@ -239,7 +240,9 @@ FBEncoder *fb_encoder_create(const FBEncoderConfig *config)
|
|||||||
if (config->video_pix_fmt) {
|
if (config->video_pix_fmt) {
|
||||||
e->video_pix_fmt = config->video_pix_fmt;
|
e->video_pix_fmt = config->video_pix_fmt;
|
||||||
}
|
}
|
||||||
e->video_src_pix_fmt = config->video_src_pix_fmt;
|
// Stored in AVPixelFormat space; the public config value is an
|
||||||
|
// FB_PIX_FMT_* identifier.
|
||||||
|
e->video_src_pix_fmt = fb::PixFmtToAV(config->video_src_pix_fmt);
|
||||||
e->video_color_range = config->video_color_range;
|
e->video_color_range = config->video_color_range;
|
||||||
e->video_field_order = config->video_field_order;
|
e->video_field_order = config->video_field_order;
|
||||||
e->video_bit_rate = config->video_bit_rate;
|
e->video_bit_rate = config->video_bit_rate;
|
||||||
@@ -429,7 +432,7 @@ int fb_encoder_write_video_frame(FBEncoder *e, int width, int height,
|
|||||||
|
|
||||||
input_frame->width = width;
|
input_frame->width = width;
|
||||||
input_frame->height = height;
|
input_frame->height = height;
|
||||||
input_frame->format = pix_fmt;
|
input_frame->format = fb::PixFmtToAV(pix_fmt);
|
||||||
input_frame->data[0] = const_cast<uint8_t *>(data);
|
input_frame->data[0] = const_cast<uint8_t *>(data);
|
||||||
input_frame->linesize[0] = linesize;
|
input_frame->linesize[0] = linesize;
|
||||||
|
|
||||||
|
|||||||
@@ -114,13 +114,14 @@ void fb_frame_set_height(FBFrame *frame, int height)
|
|||||||
|
|
||||||
int fb_frame_get_format(const FBFrame *frame)
|
int fb_frame_get_format(const FBFrame *frame)
|
||||||
{
|
{
|
||||||
return frame ? frame->frame->format : FB_PIX_FMT_NONE;
|
return frame ? fb::PixFmtFromAV(AVPixelFormat(frame->frame->format)) :
|
||||||
|
FB_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fb_frame_set_format(FBFrame *frame, int format)
|
void fb_frame_set_format(FBFrame *frame, int format)
|
||||||
{
|
{
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->frame->format = format;
|
frame->frame->format = fb::PixFmtToAV(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,35 +56,6 @@ static_assert(FB_TIME_BASE == AV_TIME_BASE, "FB_TIME_BASE mismatch");
|
|||||||
static_assert(FB_SCALER_POINT == SWS_POINT, "FB_SCALER_POINT mismatch");
|
static_assert(FB_SCALER_POINT == SWS_POINT, "FB_SCALER_POINT mismatch");
|
||||||
|
|
||||||
static_assert(FB_PIX_FMT_NONE == AV_PIX_FMT_NONE, "FB_PIX_FMT_NONE mismatch");
|
static_assert(FB_PIX_FMT_NONE == AV_PIX_FMT_NONE, "FB_PIX_FMT_NONE mismatch");
|
||||||
static_assert(FB_PIX_FMT_YUV420P == AV_PIX_FMT_YUV420P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGB24 == AV_PIX_FMT_RGB24, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV422P == AV_PIX_FMT_YUV422P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV444P == AV_PIX_FMT_YUV444P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV410P == AV_PIX_FMT_YUV410P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV411P == AV_PIX_FMT_YUV411P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_GRAY8 == AV_PIX_FMT_GRAY8, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUVJ420P == AV_PIX_FMT_YUVJ420P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUVJ422P == AV_PIX_FMT_YUVJ422P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUVJ444P == AV_PIX_FMT_YUVJ444P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBA == AV_PIX_FMT_RGBA, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_GRAY16LE == AV_PIX_FMT_GRAY16LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV440P == AV_PIX_FMT_YUV440P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUVJ440P == AV_PIX_FMT_YUVJ440P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGB48LE == AV_PIX_FMT_RGB48LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV420P10LE == AV_PIX_FMT_YUV420P10LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV422P10LE == AV_PIX_FMT_YUV422P10LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV444P10LE == AV_PIX_FMT_YUV444P10LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBA64LE == AV_PIX_FMT_RGBA64LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV420P12LE == AV_PIX_FMT_YUV420P12LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV422P12LE == AV_PIX_FMT_YUV422P12LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUV444P12LE == AV_PIX_FMT_YUV444P12LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_YUVJ411P == AV_PIX_FMT_YUVJ411P, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_GRAYF32LE == AV_PIX_FMT_GRAYF32LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBAF16LE == AV_PIX_FMT_RGBAF16LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBF32LE == AV_PIX_FMT_RGBF32LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBAF32LE == AV_PIX_FMT_RGBAF32LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_RGBF16LE == AV_PIX_FMT_RGBF16LE, "pixfmt mismatch");
|
|
||||||
static_assert(FB_PIX_FMT_GRAYF16LE == AV_PIX_FMT_GRAYF16LE, "pixfmt mismatch");
|
|
||||||
|
|
||||||
static_assert(FB_SAMPLE_FMT_NONE == AV_SAMPLE_FMT_NONE, "samplefmt mismatch");
|
static_assert(FB_SAMPLE_FMT_NONE == AV_SAMPLE_FMT_NONE, "samplefmt mismatch");
|
||||||
static_assert(FB_SAMPLE_FMT_U8 == AV_SAMPLE_FMT_U8, "samplefmt mismatch");
|
static_assert(FB_SAMPLE_FMT_U8 == AV_SAMPLE_FMT_U8, "samplefmt mismatch");
|
||||||
@@ -162,6 +133,24 @@ uint64_t ValidateStreamChannelLayoutMask(const AVStream *stream);
|
|||||||
/** Map an AVColorSpace to the corresponding SWS_CS_* constant. */
|
/** Map an AVColorSpace to the corresponding SWS_CS_* constant. */
|
||||||
int SwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
int SwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate an FB_PIX_FMT_* value to the AVPixelFormat of the FFmpeg build
|
||||||
|
* this library was compiled against. AVPixelFormat enum values shift between
|
||||||
|
* FFmpeg releases (new formats are inserted mid-enum), so the public FB
|
||||||
|
* values are fixed identifiers resolved by pixel format name. Returns
|
||||||
|
* AV_PIX_FMT_NONE for FB_PIX_FMT_NONE and for static FB formats unknown to
|
||||||
|
* this FFmpeg build (e.g. rgbf16le/grayf16le on FFmpeg < 7.1).
|
||||||
|
*/
|
||||||
|
AVPixelFormat PixFmtToAV(int fb_fmt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse of PixFmtToAV. Returns FB_PIX_FMT_NONE for AV_PIX_FMT_NONE.
|
||||||
|
* Formats without a static FB_PIX_FMT_* identifier (hardware-download
|
||||||
|
* formats such as p210le, etc.) receive a process-local dynamic id >= 1000
|
||||||
|
* so they can still round-trip through the API.
|
||||||
|
*/
|
||||||
|
int PixFmtFromAV(AVPixelFormat fmt);
|
||||||
|
|
||||||
void SetError(char *error_buffer, size_t error_buffer_size, const char *context,
|
void SetError(char *error_buffer, size_t error_buffer_size, const char *context,
|
||||||
int error_code);
|
int error_code);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void FillStreamInfo(const AVStream *s, int has_decoder, FBStreamInfo *out)
|
|||||||
out->has_decoder = has_decoder;
|
out->has_decoder = has_decoder;
|
||||||
out->width = par->width;
|
out->width = par->width;
|
||||||
out->height = par->height;
|
out->height = par->height;
|
||||||
out->pixel_format = par->format;
|
out->pixel_format = fb::PixFmtFromAV(AVPixelFormat(par->format));
|
||||||
out->field_order = FB_FIELD_ORDER_UNKNOWN;
|
out->field_order = FB_FIELD_ORDER_UNKNOWN;
|
||||||
out->color_range = par->color_range;
|
out->color_range = par->color_range;
|
||||||
out->sample_rate = par->sample_rate;
|
out->sample_rate = par->sample_rate;
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ FBScaler *fb_scaler_create(int src_width, int src_height, int src_format,
|
|||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
SwsContext *ctx = sws_getContext(
|
SwsContext *ctx = sws_getContext(
|
||||||
src_width, src_height, static_cast<AVPixelFormat>(src_format),
|
src_width, src_height, fb::PixFmtToAV(src_format),
|
||||||
dst_width, dst_height, static_cast<AVPixelFormat>(dst_format), flags,
|
dst_width, dst_height, fb::PixFmtToAV(dst_format), flags,
|
||||||
nullptr, nullptr, nullptr);
|
nullptr, nullptr, nullptr);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
+146
-13
@@ -25,9 +25,128 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fb
|
namespace fb
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The public FB_PIX_FMT_* values are fixed identifiers (the host app never
|
||||||
|
* sees AVPixelFormat). AVPixelFormat enum values shift between FFmpeg
|
||||||
|
* releases because new formats are inserted mid-enum, so the mapping is
|
||||||
|
* resolved through pixel format names, which are stable. Formats unknown to
|
||||||
|
* the FFmpeg build this library was compiled against resolve to
|
||||||
|
* AV_PIX_FMT_NONE.
|
||||||
|
*/
|
||||||
|
struct PixFmtName {
|
||||||
|
int fb_fmt;
|
||||||
|
const char *av_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr PixFmtName kPixFmtNames[] = {
|
||||||
|
{ FB_PIX_FMT_YUV420P, "yuv420p" },
|
||||||
|
{ FB_PIX_FMT_RGB24, "rgb24" },
|
||||||
|
{ FB_PIX_FMT_YUV422P, "yuv422p" },
|
||||||
|
{ FB_PIX_FMT_YUV444P, "yuv444p" },
|
||||||
|
{ FB_PIX_FMT_YUV410P, "yuv410p" },
|
||||||
|
{ FB_PIX_FMT_YUV411P, "yuv411p" },
|
||||||
|
{ FB_PIX_FMT_GRAY8, "gray8" },
|
||||||
|
{ FB_PIX_FMT_YUVJ420P, "yuvj420p" },
|
||||||
|
{ FB_PIX_FMT_YUVJ422P, "yuvj422p" },
|
||||||
|
{ FB_PIX_FMT_YUVJ444P, "yuvj444p" },
|
||||||
|
{ FB_PIX_FMT_NV12, "nv12" },
|
||||||
|
{ FB_PIX_FMT_RGBA, "rgba" },
|
||||||
|
{ FB_PIX_FMT_GRAY16LE, "gray16le" },
|
||||||
|
{ FB_PIX_FMT_YUV440P, "yuv440p" },
|
||||||
|
{ FB_PIX_FMT_YUVJ440P, "yuvj440p" },
|
||||||
|
{ FB_PIX_FMT_RGB48LE, "rgb48le" },
|
||||||
|
{ FB_PIX_FMT_YUV420P10LE, "yuv420p10le" },
|
||||||
|
{ FB_PIX_FMT_YUV422P10LE, "yuv422p10le" },
|
||||||
|
{ FB_PIX_FMT_YUV444P10LE, "yuv444p10le" },
|
||||||
|
{ FB_PIX_FMT_RGBA64LE, "rgba64le" },
|
||||||
|
{ FB_PIX_FMT_YUV420P12LE, "yuv420p12le" },
|
||||||
|
{ FB_PIX_FMT_YUV422P12LE, "yuv422p12le" },
|
||||||
|
{ FB_PIX_FMT_YUV444P12LE, "yuv444p12le" },
|
||||||
|
{ FB_PIX_FMT_YUVJ411P, "yuvj411p" },
|
||||||
|
{ FB_PIX_FMT_P010LE, "p010le" },
|
||||||
|
{ FB_PIX_FMT_GRAYF32LE, "grayf32le" },
|
||||||
|
{ FB_PIX_FMT_RGBAF16LE, "rgbaf16le" },
|
||||||
|
{ FB_PIX_FMT_RGBF32LE, "rgbf32le" },
|
||||||
|
{ FB_PIX_FMT_RGBAF32LE, "rgbaf32le" },
|
||||||
|
{ FB_PIX_FMT_RGBF16LE, "rgbf16le" },
|
||||||
|
{ FB_PIX_FMT_GRAYF16LE, "grayf16le" },
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats outside the static table (hardware-download formats like p210le,
|
||||||
|
* or anything else a decoder produces) receive process-local identifiers so
|
||||||
|
* they still round-trip through the API (e.g. into the scaler) instead of
|
||||||
|
* collapsing to FB_PIX_FMT_NONE. Dynamic ids start above every static
|
||||||
|
* FB_PIX_FMT_* value and are not stable across runs, which is fine because
|
||||||
|
* callers treat the values as opaque.
|
||||||
|
*/
|
||||||
|
constexpr int kDynamicPixFmtBase = 1000;
|
||||||
|
std::mutex g_dynamic_pix_fmt_mutex;
|
||||||
|
std::vector<std::pair<int, AVPixelFormat>> g_dynamic_pix_fmts;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AVPixelFormat PixFmtToAV(int fb_fmt)
|
||||||
|
{
|
||||||
|
if (fb_fmt == FB_PIX_FMT_NONE) {
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
|
}
|
||||||
|
for (const PixFmtName &entry : kPixFmtNames) {
|
||||||
|
if (entry.fb_fmt == fb_fmt) {
|
||||||
|
return av_get_pix_fmt(entry.av_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fb_fmt >= kDynamicPixFmtBase) {
|
||||||
|
std::lock_guard<std::mutex> lock(g_dynamic_pix_fmt_mutex);
|
||||||
|
for (const auto &entry : g_dynamic_pix_fmts) {
|
||||||
|
if (entry.first == fb_fmt) {
|
||||||
|
return entry.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PixFmtFromAV(AVPixelFormat fmt)
|
||||||
|
{
|
||||||
|
if (fmt == AV_PIX_FMT_NONE) {
|
||||||
|
return FB_PIX_FMT_NONE;
|
||||||
|
}
|
||||||
|
const char *name = av_get_pix_fmt_name(fmt);
|
||||||
|
if (name) {
|
||||||
|
for (const PixFmtName &entry : kPixFmtNames) {
|
||||||
|
if (strcmp(name, entry.av_name) == 0) {
|
||||||
|
return entry.fb_fmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_dynamic_pix_fmt_mutex);
|
||||||
|
for (const auto &entry : g_dynamic_pix_fmts) {
|
||||||
|
if (entry.second == fmt) {
|
||||||
|
return entry.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const int id = kDynamicPixFmtBase + int(g_dynamic_pix_fmts.size());
|
||||||
|
g_dynamic_pix_fmts.emplace_back(id, fmt);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
void ChannelLayoutFromMask(AVChannelLayout *layout, uint64_t mask,
|
void ChannelLayoutFromMask(AVChannelLayout *layout, uint64_t mask,
|
||||||
int fallback_channels)
|
int fallback_channels)
|
||||||
{
|
{
|
||||||
@@ -133,18 +252,18 @@ const char *fb_version_string(void)
|
|||||||
|
|
||||||
const char *fb_pix_fmt_name(int pix_fmt)
|
const char *fb_pix_fmt_name(int pix_fmt)
|
||||||
{
|
{
|
||||||
return av_get_pix_fmt_name(static_cast<AVPixelFormat>(pix_fmt));
|
return av_get_pix_fmt_name(fb::PixFmtToAV(pix_fmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
int fb_pix_fmt_from_name(const char *name)
|
int fb_pix_fmt_from_name(const char *name)
|
||||||
{
|
{
|
||||||
return av_get_pix_fmt(name);
|
return fb::PixFmtFromAV(av_get_pix_fmt(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
int fb_pix_fmt_bits_per_pixel(int pix_fmt)
|
int fb_pix_fmt_bits_per_pixel(int pix_fmt)
|
||||||
{
|
{
|
||||||
const AVPixFmtDescriptor *desc =
|
const AVPixFmtDescriptor *desc =
|
||||||
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(pix_fmt));
|
av_pix_fmt_desc_get(fb::PixFmtToAV(pix_fmt));
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -154,21 +273,21 @@ int fb_pix_fmt_bits_per_pixel(int pix_fmt)
|
|||||||
int fb_pix_fmt_has_alpha(int pix_fmt)
|
int fb_pix_fmt_has_alpha(int pix_fmt)
|
||||||
{
|
{
|
||||||
const AVPixFmtDescriptor *desc =
|
const AVPixFmtDescriptor *desc =
|
||||||
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(pix_fmt));
|
av_pix_fmt_desc_get(fb::PixFmtToAV(pix_fmt));
|
||||||
return desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA);
|
return desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fb_pix_fmt_is_planar(int pix_fmt)
|
int fb_pix_fmt_is_planar(int pix_fmt)
|
||||||
{
|
{
|
||||||
const AVPixFmtDescriptor *desc =
|
const AVPixFmtDescriptor *desc =
|
||||||
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(pix_fmt));
|
av_pix_fmt_desc_get(fb::PixFmtToAV(pix_fmt));
|
||||||
return desc && (desc->flags & AV_PIX_FMT_FLAG_PLANAR);
|
return desc && (desc->flags & AV_PIX_FMT_FLAG_PLANAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fb_pix_fmt_component_size(int pix_fmt)
|
int fb_pix_fmt_component_size(int pix_fmt)
|
||||||
{
|
{
|
||||||
const AVPixFmtDescriptor *desc =
|
const AVPixFmtDescriptor *desc =
|
||||||
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(pix_fmt));
|
av_pix_fmt_desc_get(fb::PixFmtToAV(pix_fmt));
|
||||||
if (!desc || desc->nb_components == 0) {
|
if (!desc || desc->nb_components == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -178,15 +297,29 @@ int fb_pix_fmt_component_size(int pix_fmt)
|
|||||||
|
|
||||||
int fb_find_best_pix_fmt_of_list(const int *list, int pix_fmt)
|
int fb_find_best_pix_fmt_of_list(const int *list, int pix_fmt)
|
||||||
{
|
{
|
||||||
// Count the list
|
// Translate the FB_PIX_FMT_NONE-terminated list to AVPixelFormat values,
|
||||||
int count = 0;
|
// skipping formats unknown to this FFmpeg build
|
||||||
while (list[count] != FB_PIX_FMT_NONE) {
|
std::vector<AVPixelFormat> av_list;
|
||||||
count++;
|
for (int i = 0; list[i] != FB_PIX_FMT_NONE; i++) {
|
||||||
|
AVPixelFormat fmt = fb::PixFmtToAV(list[i]);
|
||||||
|
if (fmt != AV_PIX_FMT_NONE) {
|
||||||
|
av_list.push_back(fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (av_list.empty()) {
|
||||||
|
return FB_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return avcodec_find_best_pix_fmt_of_list(
|
// With an unknown source format there is no loss metric to compare
|
||||||
reinterpret_cast<const AVPixelFormat *>(list),
|
// against; prefer the first (most desirable) list entry
|
||||||
static_cast<AVPixelFormat>(pix_fmt), 1, nullptr);
|
AVPixelFormat av_src = fb::PixFmtToAV(pix_fmt);
|
||||||
|
if (av_src == AV_PIX_FMT_NONE) {
|
||||||
|
return list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
av_list.push_back(AV_PIX_FMT_NONE);
|
||||||
|
return fb::PixFmtFromAV(avcodec_find_best_pix_fmt_of_list(
|
||||||
|
av_list.data(), av_src, 1, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
int fb_channel_layout_get_channels(uint64_t mask)
|
int fb_channel_layout_get_channels(uint64_t mask)
|
||||||
|
|||||||
@@ -735,8 +735,10 @@ TEST(FFmpegBridgeEncoder, WritePngVideoAndProbeBack)
|
|||||||
const int height = 64;
|
const int height = 64;
|
||||||
const int frame_count = 5;
|
const int frame_count = 5;
|
||||||
|
|
||||||
|
const QByteArray filename = path.toUtf8();
|
||||||
|
|
||||||
FBEncoderConfig config = {};
|
FBEncoderConfig config = {};
|
||||||
config.filename = path.toUtf8().constData();
|
config.filename = filename.constData();
|
||||||
config.video_enabled = 1;
|
config.video_enabled = 1;
|
||||||
config.video_codec = FB_CODEC_PNG;
|
config.video_codec = FB_CODEC_PNG;
|
||||||
config.video_width = width;
|
config.video_width = width;
|
||||||
@@ -799,8 +801,10 @@ TEST(FFmpegBridgeEncoder, WritePcmAudioAndProbeBack)
|
|||||||
const QString path = TempFilePath(QStringLiteral("fb_bridge_test.wav"));
|
const QString path = TempFilePath(QStringLiteral("fb_bridge_test.wav"));
|
||||||
QFile::remove(path);
|
QFile::remove(path);
|
||||||
|
|
||||||
|
const QByteArray filename = path.toUtf8();
|
||||||
|
|
||||||
FBEncoderConfig config = {};
|
FBEncoderConfig config = {};
|
||||||
config.filename = path.toUtf8().constData();
|
config.filename = filename.constData();
|
||||||
config.audio_enabled = 1;
|
config.audio_enabled = 1;
|
||||||
config.audio_codec = FB_CODEC_PCM;
|
config.audio_codec = FB_CODEC_PCM;
|
||||||
config.audio_sample_rate = 44100;
|
config.audio_sample_rate = 44100;
|
||||||
|
|||||||
Reference in New Issue
Block a user