mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 03:25:36 +00:00
MediaCapture/FFmpeg: Tidy up format selection
And remove use of deprecated fields.
This commit is contained in:
parent
e439ebc1c6
commit
22109fc8e0
@ -65,7 +65,7 @@ if(NOT WIN32 AND NOT APPLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT ANDROID AND NOT WIN32)
|
if(NOT ANDROID AND NOT WIN32)
|
||||||
find_package(FFMPEG COMPONENTS avcodec avformat avutil swresample swscale)
|
find_package(FFMPEG 7.1.0 COMPONENTS avcodec avformat avutil swresample swscale)
|
||||||
if(NOT FFMPEG_FOUND)
|
if(NOT FFMPEG_FOUND)
|
||||||
message(WARNING "FFmpeg not found, using bundled headers.")
|
message(WARNING "FFmpeg not found, using bundled headers.")
|
||||||
endif()
|
endif()
|
||||||
|
@ -56,6 +56,7 @@ extern "C" {
|
|||||||
#include "libavformat/version.h"
|
#include "libavformat/version.h"
|
||||||
#include "libavutil/dict.h"
|
#include "libavutil/dict.h"
|
||||||
#include "libavutil/opt.h"
|
#include "libavutil/opt.h"
|
||||||
|
#include "libavutil/pixdesc.h"
|
||||||
#include "libavutil/version.h"
|
#include "libavutil/version.h"
|
||||||
#include "libswresample/swresample.h"
|
#include "libswresample/swresample.h"
|
||||||
#include "libswresample/version.h"
|
#include "libswresample/version.h"
|
||||||
@ -1796,22 +1797,6 @@ bool MediaCaptureMF::ProcessAudioPackets(s64 video_pts, Error* error)
|
|||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
|
|
||||||
// We're using deprecated fields because we're targeting multiple ffmpeg versions.
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma warning(disable : 4996) // warning C4996: 'AVCodecContext::channels': was declared deprecated
|
|
||||||
#elif defined(__clang__)
|
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Compatibility with both ffmpeg 4.x and 5.x.
|
|
||||||
#if (LIBAVFORMAT_VERSION_MAJOR < 59)
|
|
||||||
#define ff_const59
|
|
||||||
#else
|
|
||||||
#define ff_const59 const
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VISIT_AVCODEC_IMPORTS(X) \
|
#define VISIT_AVCODEC_IMPORTS(X) \
|
||||||
X(avcodec_find_encoder_by_name) \
|
X(avcodec_find_encoder_by_name) \
|
||||||
X(avcodec_find_encoder) \
|
X(avcodec_find_encoder) \
|
||||||
@ -1822,6 +1807,7 @@ bool MediaCaptureMF::ProcessAudioPackets(s64 video_pts, Error* error)
|
|||||||
X(avcodec_receive_packet) \
|
X(avcodec_receive_packet) \
|
||||||
X(avcodec_parameters_from_context) \
|
X(avcodec_parameters_from_context) \
|
||||||
X(avcodec_get_hw_config) \
|
X(avcodec_get_hw_config) \
|
||||||
|
X(avcodec_get_supported_config) \
|
||||||
X(av_codec_iterate) \
|
X(av_codec_iterate) \
|
||||||
X(av_packet_alloc) \
|
X(av_packet_alloc) \
|
||||||
X(av_packet_free) \
|
X(av_packet_free) \
|
||||||
@ -1840,17 +1826,10 @@ bool MediaCaptureMF::ProcessAudioPackets(s64 video_pts, Error* error)
|
|||||||
X(avio_open) \
|
X(avio_open) \
|
||||||
X(avio_closep)
|
X(avio_closep)
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_MAJOR < 57
|
#define VISIT_AVUTIL_IMPORTS(X) \
|
||||||
#define AVUTIL_57_IMPORTS(X)
|
|
||||||
#else
|
|
||||||
#define AVUTIL_57_IMPORTS(X) \
|
|
||||||
X(av_channel_layout_default) \
|
X(av_channel_layout_default) \
|
||||||
X(av_channel_layout_copy) \
|
X(av_channel_layout_copy) \
|
||||||
X(av_opt_set_chlayout)
|
X(av_opt_set_chlayout) \
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VISIT_AVUTIL_IMPORTS(X) \
|
|
||||||
AVUTIL_57_IMPORTS(X) \
|
|
||||||
X(av_frame_alloc) \
|
X(av_frame_alloc) \
|
||||||
X(av_frame_get_buffer) \
|
X(av_frame_get_buffer) \
|
||||||
X(av_frame_free) \
|
X(av_frame_free) \
|
||||||
@ -1863,6 +1842,7 @@ bool MediaCaptureMF::ProcessAudioPackets(s64 video_pts, Error* error)
|
|||||||
X(av_opt_set_int) \
|
X(av_opt_set_int) \
|
||||||
X(av_opt_set_sample_fmt) \
|
X(av_opt_set_sample_fmt) \
|
||||||
X(av_compare_ts) \
|
X(av_compare_ts) \
|
||||||
|
X(av_get_pix_fmt) \
|
||||||
X(av_get_bytes_per_sample) \
|
X(av_get_bytes_per_sample) \
|
||||||
X(av_sample_fmt_is_planar) \
|
X(av_sample_fmt_is_planar) \
|
||||||
X(av_d2q) \
|
X(av_d2q) \
|
||||||
@ -2096,7 +2076,7 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
std::string_view audio_codec, u32 audio_bitrate,
|
std::string_view audio_codec, u32 audio_bitrate,
|
||||||
std::string_view audio_codec_args, Error* error)
|
std::string_view audio_codec_args, Error* error)
|
||||||
{
|
{
|
||||||
ff_const59 AVOutputFormat* output_format = wrap_av_guess_format(nullptr, m_path.c_str(), nullptr);
|
const AVOutputFormat* output_format = wrap_av_guess_format(nullptr, m_path.c_str(), nullptr);
|
||||||
if (!output_format)
|
if (!output_format)
|
||||||
{
|
{
|
||||||
Error::SetStringFmt(error, "Failed to get output format for '{}'", Path::GetFileName(m_path));
|
Error::SetStringFmt(error, "Failed to get output format for '{}'", Path::GetFileName(m_path));
|
||||||
@ -2151,6 +2131,8 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
m_video_codec_context->sample_aspect_ratio = wrap_av_d2q(aspect, 100000);
|
m_video_codec_context->sample_aspect_ratio = wrap_av_d2q(aspect, 100000);
|
||||||
wrap_av_reduce(&m_video_codec_context->time_base.num, &m_video_codec_context->time_base.den, 10000,
|
wrap_av_reduce(&m_video_codec_context->time_base.num, &m_video_codec_context->time_base.den, 10000,
|
||||||
static_cast<s64>(static_cast<double>(fps) * 10000.0), std::numeric_limits<s32>::max());
|
static_cast<s64>(static_cast<double>(fps) * 10000.0), std::numeric_limits<s32>::max());
|
||||||
|
wrap_av_reduce(&m_video_codec_context->framerate.num, &m_video_codec_context->framerate.den,
|
||||||
|
static_cast<s64>(static_cast<double>(fps) * 10000.0), 10000, std::numeric_limits<s32>::max());
|
||||||
|
|
||||||
// Map input pixel format.
|
// Map input pixel format.
|
||||||
static constexpr const std::pair<GPUTexture::Format, AVPixelFormat> texture_pf_mapping[] = {
|
static constexpr const std::pair<GPUTexture::Format, AVPixelFormat> texture_pf_mapping[] = {
|
||||||
@ -2171,43 +2153,35 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to YUV 4:2:0 if the codec doesn't specify a pixel format.
|
if (!video_codec_args.empty())
|
||||||
AVPixelFormat sw_pix_fmt = AV_PIX_FMT_YUV420P;
|
|
||||||
if (vcodec->pix_fmts)
|
|
||||||
{
|
{
|
||||||
// Prefer YUV420 given the choice, but otherwise fall back to whatever it supports.
|
res = wrap_av_dict_parse_string(&m_video_codec_arguments, SmallString(video_codec_args).c_str(), "=", ":", 0);
|
||||||
sw_pix_fmt = vcodec->pix_fmts[0];
|
if (res < 0)
|
||||||
for (u32 i = 0; vcodec->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
|
|
||||||
{
|
{
|
||||||
if (vcodec->pix_fmts[i] == AV_PIX_FMT_YUV420P)
|
SetAVError(error, "av_dict_parse_string() for video failed: ", res);
|
||||||
{
|
return false;
|
||||||
sw_pix_fmt = vcodec->pix_fmts[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_video_codec_context->pix_fmt = sw_pix_fmt;
|
|
||||||
|
// Select output pixel format.
|
||||||
|
AVPixelFormat request_pix_fmt = AV_PIX_FMT_YUV420P;
|
||||||
|
if (const AVDictionaryEntry* de = wrap_av_dict_get(m_video_codec_arguments, "pixel_format", nullptr, 0))
|
||||||
|
{
|
||||||
|
const AVPixelFormat de_fmt = wrap_av_get_pix_fmt(de->value);
|
||||||
|
request_pix_fmt = (de_fmt != AV_PIX_FMT_NONE) ? de_fmt : request_pix_fmt;
|
||||||
|
if (de_fmt == AV_PIX_FMT_NONE)
|
||||||
|
WARNING_LOG("Invalid pixel format override: {}", de->value);
|
||||||
|
}
|
||||||
|
|
||||||
// Can we use hardware encoding?
|
// Can we use hardware encoding?
|
||||||
const AVCodecHWConfig* hwconfig = wrap_avcodec_get_hw_config(vcodec, 0);
|
const AVCodecHWConfig* hwconfig = wrap_avcodec_get_hw_config(vcodec, 0);
|
||||||
if (hwconfig && hwconfig->pix_fmt != AV_PIX_FMT_NONE && hwconfig->pix_fmt != sw_pix_fmt)
|
AVPixelFormat sw_pix_fmt;
|
||||||
{
|
|
||||||
// First index isn't our preferred pixel format, try the others, but fall back if one doesn't exist.
|
|
||||||
int index = 1;
|
|
||||||
while (const AVCodecHWConfig* next_hwconfig = wrap_avcodec_get_hw_config(vcodec, index++))
|
|
||||||
{
|
|
||||||
if (next_hwconfig->pix_fmt == sw_pix_fmt)
|
|
||||||
{
|
|
||||||
hwconfig = next_hwconfig;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwconfig)
|
if (hwconfig)
|
||||||
{
|
{
|
||||||
Error hw_error;
|
// Can't do this test for hardware codecs, because they don't list the software formats as inputs.
|
||||||
|
sw_pix_fmt = request_pix_fmt;
|
||||||
|
|
||||||
|
Error hw_error;
|
||||||
INFO_LOG("Trying to use {} hardware device for video encoding.",
|
INFO_LOG("Trying to use {} hardware device for video encoding.",
|
||||||
wrap_av_hwdevice_get_type_name(hwconfig->device_type));
|
wrap_av_hwdevice_get_type_name(hwconfig->device_type));
|
||||||
res = wrap_av_hwdevice_ctx_create(&m_video_hw_context, hwconfig->device_type, nullptr, nullptr, 0);
|
res = wrap_av_hwdevice_ctx_create(&m_video_hw_context, hwconfig->device_type, nullptr, nullptr, 0);
|
||||||
@ -2255,21 +2229,37 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video_codec_args.empty())
|
if (!hwconfig)
|
||||||
{
|
{
|
||||||
res = wrap_av_dict_parse_string(&m_video_codec_arguments, SmallString(video_codec_args).c_str(), "=", ":", 0);
|
// Default to YUV 4:2:0 if the codec doesn't specify a pixel format.
|
||||||
|
const AVPixelFormat* supported_pixel_formats = nullptr;
|
||||||
|
int num_supported_pixel_formats = 0;
|
||||||
|
res = wrap_avcodec_get_supported_config(m_video_codec_context, vcodec, AV_CODEC_CONFIG_PIX_FORMAT, 0,
|
||||||
|
reinterpret_cast<const void**>(&supported_pixel_formats),
|
||||||
|
&num_supported_pixel_formats);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
SetAVError(error, "av_dict_parse_string() for video failed: ", res);
|
SetAVError(error, "avcodec_get_supported_config() failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prefer YUV420 given the choice, but otherwise fall back to whatever it supports.
|
||||||
|
sw_pix_fmt = supported_pixel_formats[0];
|
||||||
|
for (int i = 0; i < num_supported_pixel_formats; i++)
|
||||||
|
{
|
||||||
|
if (supported_pixel_formats[i] == request_pix_fmt)
|
||||||
|
{
|
||||||
|
sw_pix_fmt = supported_pixel_formats[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_video_codec_context->pix_fmt = sw_pix_fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output_format->flags & AVFMT_GLOBALHEADER)
|
if (output_format->flags & AVFMT_GLOBALHEADER)
|
||||||
m_video_codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
m_video_codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
bool has_pixel_format_override = wrap_av_dict_get(m_video_codec_arguments, "pixel_format", nullptr, 0);
|
|
||||||
|
|
||||||
res = wrap_avcodec_open2(m_video_codec_context, vcodec, &m_video_codec_arguments);
|
res = wrap_avcodec_open2(m_video_codec_context, vcodec, &m_video_codec_arguments);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
@ -2277,10 +2267,6 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user overrode the pixel format, get that now
|
|
||||||
if (has_pixel_format_override)
|
|
||||||
sw_pix_fmt = m_video_codec_context->pix_fmt;
|
|
||||||
|
|
||||||
m_converted_video_frame = wrap_av_frame_alloc();
|
m_converted_video_frame = wrap_av_frame_alloc();
|
||||||
m_hw_video_frame = IsUsingHardwareVideoEncoding() ? wrap_av_frame_alloc() : nullptr;
|
m_hw_video_frame = IsUsingHardwareVideoEncoding() ? wrap_av_frame_alloc() : nullptr;
|
||||||
if (!m_converted_video_frame || (IsUsingHardwareVideoEncoding() && !m_hw_video_frame))
|
if (!m_converted_video_frame || (IsUsingHardwareVideoEncoding() && !m_hw_video_frame))
|
||||||
@ -2369,17 +2355,23 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
m_audio_codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
|
m_audio_codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||||
m_audio_codec_context->sample_rate = sample_rate;
|
m_audio_codec_context->sample_rate = sample_rate;
|
||||||
m_audio_codec_context->time_base = {1, static_cast<int>(sample_rate)};
|
m_audio_codec_context->time_base = {1, static_cast<int>(sample_rate)};
|
||||||
#if LIBAVUTIL_VERSION_MAJOR < 57
|
|
||||||
m_audio_codec_context->channels = AUDIO_CHANNELS;
|
|
||||||
m_audio_codec_context->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
#else
|
|
||||||
wrap_av_channel_layout_default(&m_audio_codec_context->ch_layout, AUDIO_CHANNELS);
|
wrap_av_channel_layout_default(&m_audio_codec_context->ch_layout, AUDIO_CHANNELS);
|
||||||
#endif
|
|
||||||
|
|
||||||
bool supports_format = false;
|
bool supports_format = false;
|
||||||
for (const AVSampleFormat* p = acodec->sample_fmts; *p != AV_SAMPLE_FMT_NONE; p++)
|
const AVSampleFormat* supported_sample_formats = nullptr;
|
||||||
|
int num_supported_sample_formats = 0;
|
||||||
|
res = wrap_avcodec_get_supported_config(m_video_codec_context, acodec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0,
|
||||||
|
reinterpret_cast<const void**>(&supported_sample_formats),
|
||||||
|
&num_supported_sample_formats);
|
||||||
|
if (res < 0)
|
||||||
{
|
{
|
||||||
if (*p == m_audio_codec_context->sample_fmt)
|
SetAVError(error, "avcodec_get_supported_config() for audio failed: ", res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_supported_sample_formats; i++)
|
||||||
|
{
|
||||||
|
if (supported_sample_formats[i] == m_audio_codec_context->sample_fmt)
|
||||||
{
|
{
|
||||||
supports_format = true;
|
supports_format = true;
|
||||||
break;
|
break;
|
||||||
@ -2388,7 +2380,7 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
if (!supports_format)
|
if (!supports_format)
|
||||||
{
|
{
|
||||||
WARNING_LOG("Audio codec '{}' does not support S16 samples, using default.", acodec->name);
|
WARNING_LOG("Audio codec '{}' does not support S16 samples, using default.", acodec->name);
|
||||||
m_audio_codec_context->sample_fmt = acodec->sample_fmts[0];
|
m_audio_codec_context->sample_fmt = supported_sample_formats[0];
|
||||||
m_swr_context = wrap_swr_alloc();
|
m_swr_context = wrap_swr_alloc();
|
||||||
if (!m_swr_context)
|
if (!m_swr_context)
|
||||||
{
|
{
|
||||||
@ -2402,11 +2394,8 @@ bool MediaCaptureFFmpeg::InternalBeginCapture(float fps, float aspect, u32 sampl
|
|||||||
wrap_av_opt_set_int(m_swr_context, "out_channel_count", AUDIO_CHANNELS, 0);
|
wrap_av_opt_set_int(m_swr_context, "out_channel_count", AUDIO_CHANNELS, 0);
|
||||||
wrap_av_opt_set_int(m_swr_context, "out_sample_rate", sample_rate, 0);
|
wrap_av_opt_set_int(m_swr_context, "out_sample_rate", sample_rate, 0);
|
||||||
wrap_av_opt_set_sample_fmt(m_swr_context, "out_sample_fmt", m_audio_codec_context->sample_fmt, 0);
|
wrap_av_opt_set_sample_fmt(m_swr_context, "out_sample_fmt", m_audio_codec_context->sample_fmt, 0);
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_MAJOR >= 59
|
|
||||||
wrap_av_opt_set_chlayout(m_swr_context, "in_chlayout", &m_audio_codec_context->ch_layout, 0);
|
wrap_av_opt_set_chlayout(m_swr_context, "in_chlayout", &m_audio_codec_context->ch_layout, 0);
|
||||||
wrap_av_opt_set_chlayout(m_swr_context, "out_chlayout", &m_audio_codec_context->ch_layout, 0);
|
wrap_av_opt_set_chlayout(m_swr_context, "out_chlayout", &m_audio_codec_context->ch_layout, 0);
|
||||||
#endif
|
|
||||||
|
|
||||||
res = wrap_swr_init(m_swr_context);
|
res = wrap_swr_init(m_swr_context);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user