[FFmpeg-devel] [PATCH 1/2] Add support d3d11va Intel Hevc Rext decoder.
Mark Thompson
sw at jkqxz.net
Sun Feb 25 12:36:40 EET 2024
On 25/02/2024 02:22, Водянников А.В. via ffmpeg-devel wrote:
> From ed8fda62bbdbc62f7565891c935966c931d001ca Mon Sep 17 00:00:00 2001
> From: Aleksoid <Aleksoid1978 at mail.ru>
> Date: Thu, 22 Feb 2024 19:15:48 +1000
> Subject: [PATCH 1/2] Add support d3d11va Intel Hevc Rext decoder.
>
> Signed-off-by: Aleksoid <Aleksoid1978 at mail.ru>
> ---
> libavcodec/d3d12va_hevc.c | 2 +-
> libavcodec/dxva2.c | 68 +++++++++++++++++++++++++++++++++--
> libavcodec/dxva2_hevc.c | 41 ++++++++++++++++++---
> libavcodec/dxva2_internal.h | 38 +++++++++++++++++++-
> libavcodec/hevcdec.c | 16 +++++++++
> libavutil/hwcontext_d3d11va.c | 26 +++++++++++---
> 6 files changed, 178 insertions(+), 13 deletions(-)
What elements are Intel-specific about this?
Presumably there will be an official rext mode for D3D in future; are there any possible problems with having this vendor extension in that case?
> diff --git a/libavcodec/d3d12va_hevc.c b/libavcodec/d3d12va_hevc.c
> index a4964a05c6..0912e01b7d 100644
> --- a/libavcodec/d3d12va_hevc.c
> +++ b/libavcodec/d3d12va_hevc.c
> @@ -62,7 +62,7 @@ static int d3d12va_hevc_start_frame(AVCodecContext *avctx, av_unused const uint8
>
> ctx->used_mask = 0;
>
> - ff_dxva2_hevc_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp);
> + ff_dxva2_hevc_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, (DXVA_PicParams_HEVC_Rext*)&ctx_pic->pp);
Get the type right to avoid the cast.
>
> ff_dxva2_hevc_fill_scaling_lists(avctx, (AVDXVAContext *)ctx, &ctx_pic->qm);
>
> diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
> index 59025633f7..a611989911 100644
> --- a/libavcodec/dxva2.c
> +++ b/libavcodec/dxva2.c
> @@ -50,6 +50,13 @@ DEFINE_GUID(ff_DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x0
> DEFINE_GUID(ff_GUID_NULL, 0x00000000, 0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
> DEFINE_GUID(ff_IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
>
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main12_Intel, 0x8FF8A3AA, 0xC456, 0x4132, 0xB6, 0xEF, 0x69, 0xD9, 0xDD, 0x72, 0x57, 0x1D);
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main422_10_Intel, 0xE484DCB8, 0xCAC9, 0x4859, 0x99, 0xF5, 0x5C, 0x0D, 0x45, 0x06, 0x90, 0x89);
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main422_12_Intel, 0xC23DD857, 0x874B, 0x423C, 0xB6, 0xE0, 0x82, 0xCE, 0xAA, 0x9B, 0x11, 0x8A);
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main444_Intel, 0x41A5AF96, 0xE415, 0x4B0C, 0x9D, 0x03, 0x90, 0x78, 0x58, 0xE2, 0x3E, 0x78);
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main444_10_Intel, 0x6A6A81BA, 0x912A, 0x485D, 0xB5, 0x7F, 0xCC, 0xD2, 0xD3, 0x7B, 0x8D, 0x94);
> +DEFINE_GUID(ff_DXVA2_HEVC_VLD_Main444_12_Intel, 0x5B08E35D, 0x0C66, 0x4C51, 0xA6, 0xF1, 0x89, 0xD0, 0x0C, 0xB2, 0xC1, 0x97);
Where are these GUIDs documented?
> +
> typedef struct dxva_mode {
> const GUID *guid;
> enum AVCodecID codec;
> @@ -75,6 +82,8 @@ static const int prof_vp9_profile2[] = {AV_PROFILE_VP9_2,
> AV_PROFILE_UNKNOWN};
> static const int prof_av1_profile0[] = {AV_PROFILE_AV1_MAIN,
> AV_PROFILE_UNKNOWN};
> +static const int prof_hevc_rext[] = {AV_PROFILE_HEVC_REXT,
> + AV_PROFILE_UNKNOWN};
>
> static const dxva_mode dxva_modes[] = {
> /* MPEG-2 */
> @@ -104,6 +113,14 @@ static const dxva_mode dxva_modes[] = {
> /* AV1 */
> { &ff_DXVA2_ModeAV1_VLD_Profile0, AV_CODEC_ID_AV1, prof_av1_profile0 },
>
> + /* HEVC/H.265 Rext */
> + { &ff_DXVA2_HEVC_VLD_Main12_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
> + { &ff_DXVA2_HEVC_VLD_Main422_10_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
> + { &ff_DXVA2_HEVC_VLD_Main422_12_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
> + { &ff_DXVA2_HEVC_VLD_Main444_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
> + { &ff_DXVA2_HEVC_VLD_Main444_10_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
> + { &ff_DXVA2_HEVC_VLD_Main444_12_Intel, AV_CODEC_ID_HEVC, prof_hevc_rext },
Doesn't this need more detail about what the modes actually support? Declaring that any one of these supports all rext streams does not seem right.
Related: what is the desired behaviour if you have a YUV 4:2:0 8-bit stream but the profile is rext?
> +
> { NULL, 0 },
> };
>
> @@ -301,6 +318,14 @@ static int dxva_get_decoder_guid(AVCodecContext *avctx, void *service, void *sur
> if (IsEqualGUID(decoder_guid, &ff_DXVADDI_Intel_ModeH264_E))
> sctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
>
> + av_log(avctx, AV_LOG_VERBOSE,
> + "Used guid : {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}\n",
> + (unsigned)decoder_guid->Data1, decoder_guid->Data2, decoder_guid->Data3,
> + decoder_guid->Data4[0], decoder_guid->Data4[1],
> + decoder_guid->Data4[2], decoder_guid->Data4[3],
> + decoder_guid->Data4[4], decoder_guid->Data4[5],
> + decoder_guid->Data4[6], decoder_guid->Data4[7]);
> +
> return 0;
> }
>
> @@ -458,6 +483,13 @@ static DXGI_FORMAT d3d11va_map_sw_to_hw_format(enum AVPixelFormat pix_fmt)
> case AV_PIX_FMT_NV12: return DXGI_FORMAT_NV12;
> case AV_PIX_FMT_P010: return DXGI_FORMAT_P010;
> case AV_PIX_FMT_YUV420P: return DXGI_FORMAT_420_OPAQUE;
> + case AV_PIX_FMT_P016: return DXGI_FORMAT_P016;
The profiles above suggest that this does not support 16-bit, so you shouldn't have AV_PIX_FMT_P016 here. Probably you want P012?
(The ffmpeg formats contain the depth information where the DXGI ones are just containers. You need to set the format correctly or later components will believe there are significant low bits.)
> + case AV_PIX_FMT_YUYV422: return DXGI_FORMAT_YUY2;
> + case AV_PIX_FMT_Y210: return DXGI_FORMAT_Y210;
> + case AV_PIX_FMT_Y212: return DXGI_FORMAT_Y216;
> + case AV_PIX_FMT_VUYX: return DXGI_FORMAT_AYUV;
> + case AV_PIX_FMT_XV30: return DXGI_FORMAT_Y410;
> + case AV_PIX_FMT_XV36: return DXGI_FORMAT_Y416;
> default: return DXGI_FORMAT_UNKNOWN;
> }
> }
> @@ -589,6 +621,39 @@ static void ff_dxva2_unlock(AVCodecContext *avctx)
> #endif
> }
>
> +static enum AVPixelFormat map_sw_pix_format(enum AVPixelFormat pix_fmt, enum AVPixelFormat hw_pix_fmt)
> +{
> +#if CONFIG_D3D11VA
> + if (hw_pix_fmt == AV_PIX_FMT_D3D11) {
Is this actually locked to be D3D11 only?
> + switch (pix_fmt)
> + {
> + case AV_PIX_FMT_YUV420P10:
> + case AV_PIX_FMT_P010:
> + return AV_PIX_FMT_P010;
> + case AV_PIX_FMT_YUV420P12:
> + return AV_PIX_FMT_P016;
Same problem here with it creating extra bit depth out of nowhere.
> + case AV_PIX_FMT_YUV422P:
> + return AV_PIX_FMT_YUYV422;
> + case AV_PIX_FMT_YUV422P10:
> + return AV_PIX_FMT_Y210;
> + case AV_PIX_FMT_YUV444P:
> + return AV_PIX_FMT_VUYX;
> + case AV_PIX_FMT_YUV444P10:
> + return AV_PIX_FMT_XV30;
> + case AV_PIX_FMT_YUV422P12:
> + return AV_PIX_FMT_Y212;
> + case AV_PIX_FMT_YUV444P12:
> + return AV_PIX_FMT_XV36;
> + case AV_PIX_FMT_NV12:
> + default:
> + return AV_PIX_FMT_NV12;
> + }
> + }
> +#endif
> + return pix_fmt == AV_PIX_FMT_YUV420P10 ?
> + AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
> +}
> +
> int ff_dxva2_common_frame_params(AVCodecContext *avctx,
> AVBufferRef *hw_frames_ctx)
> {
> @@ -626,8 +691,7 @@ int ff_dxva2_common_frame_params(AVCodecContext *avctx,
> else
> num_surfaces += 2;
>
> - frames_ctx->sw_format = avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ?
> - AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
> + frames_ctx->sw_format = map_sw_pix_format(avctx->sw_pix_fmt, frames_ctx->format);
> frames_ctx->width = FFALIGN(avctx->coded_width, surface_alignment);
> frames_ctx->height = FFALIGN(avctx->coded_height, surface_alignment);
> frames_ctx->initial_pool_size = num_surfaces;
> diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c
> index 31d74a7164..7153b2e604 100644
> --- a/libavcodec/dxva2_hevc.c
> +++ b/libavcodec/dxva2_hevc.c
> @@ -32,7 +32,7 @@
> #define MAX_SLICES 256
>
> struct hevc_dxva2_picture_context {
> - DXVA_PicParams_HEVC pp;
> + DXVA_PicParams_HEVC_Rext pp;
Use a union here to avoid non-rext code noticing anything about the rext structure?
> DXVA_Qmatrix_HEVC qm;
> unsigned slice_count;
> DXVA_Slice_HEVC_Short slice_short[MAX_SLICES];
> @@ -58,19 +58,49 @@ static int get_refpic_index(const DXVA_PicParams_HEVC *pp, int surface_index)
> }
>
> void ff_dxva2_hevc_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx,
> - DXVA_PicParams_HEVC *pp)
> + DXVA_PicParams_HEVC_Rext *ppext)
> {
> const HEVCContext *h = avctx->priv_data;
> const HEVCFrame *current_picture = h->ref;
> const HEVCSPS *sps = h->ps.sps;
> const HEVCPPS *pps = h->ps.pps;
> int i, j;
> + DXVA_PicParams_HEVC *pp = &ppext->main;
>
> - memset(pp, 0, sizeof(*pp));
> + memset(ppext, 0, sizeof(*ppext));
>
> pp->PicWidthInMinCbsY = sps->min_cb_width;
> pp->PicHeightInMinCbsY = sps->min_cb_height;
>
> + if (sps->sps_range_extension_flag) {
> + ppext->dwRangeExtensionFlags |= (sps->transform_skip_rotation_enabled_flag << 0) |
> + (sps->transform_skip_context_enabled_flag << 1) |
> + (sps->implicit_rdpcm_enabled_flag << 2) |
> + (sps->explicit_rdpcm_enabled_flag << 3) |
> + (sps->extended_precision_processing_flag << 4) |
> + (sps->intra_smoothing_disabled_flag << 5) |
> + (sps->high_precision_offsets_enabled_flag << 5) |
Two << 5 here. Do you have streams to give test coverage of all of these flags?
Also, given that you have a bitfield representation why not just use it to avoid the manual shifts?
> + (sps->persistent_rice_adaptation_enabled_flag << 7) |
> + (sps->cabac_bypass_alignment_enabled_flag << 8);
> + }
> + if (pps->pps_range_extensions_flag) {
> + ppext->dwRangeExtensionFlags |= (pps->cross_component_prediction_enabled_flag << 9) |
> + (pps->chroma_qp_offset_list_enabled_flag << 10);
> + if (pps->chroma_qp_offset_list_enabled_flag) {
> + ppext->diff_cu_chroma_qp_offset_depth = pps->diff_cu_chroma_qp_offset_depth;
> + ppext->chroma_qp_offset_list_len_minus1 = pps->chroma_qp_offset_list_len_minus1;
> + for (i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) {
> + ppext->cb_qp_offset_list[i] = pps->cb_qp_offset_list[i];
> + ppext->cr_qp_offset_list[i] = pps->cr_qp_offset_list[i];
> + }
> + }
> + ppext->log2_sao_offset_scale_luma = pps->log2_sao_offset_scale_luma;
> + ppext->log2_sao_offset_scale_chroma = pps->log2_sao_offset_scale_chroma;
> + if (pps->transform_skip_enabled_flag) {
> + ppext->log2_max_transform_skip_block_size_minus2 = pps->log2_max_transform_skip_block_size - 2;
> + }
> + }
Suggest putting the rext stuff at the end of the function, after all of the common parts.
> +
> pp->wFormatAndSequenceInfoFlags = (sps->chroma_format_idc << 0) |
> (sps->separate_colour_plane_flag << 2) |
> ((sps->bit_depth - 8) << 3) |
> @@ -409,14 +439,15 @@ static int dxva2_hevc_end_frame(AVCodecContext *avctx)
> {
> HEVCContext *h = avctx->priv_data;
> struct hevc_dxva2_picture_context *ctx_pic = h->ref->hwaccel_picture_private;
> - int scale = ctx_pic->pp.dwCodingParamToolFlags & 1;
> + int scale = ctx_pic->pp.main.dwCodingParamToolFlags & 1;
> + int rext = avctx->profile == AV_PROFILE_HEVC_REXT && ff_dxva2_is_d3d11(avctx);
> int ret;
>
> if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0)
> return -1;
>
> ret = ff_dxva2_common_end_frame(avctx, h->ref->frame,
> - &ctx_pic->pp, sizeof(ctx_pic->pp),
> + &ctx_pic->pp, rext ? sizeof(ctx_pic->pp) : sizeof(ctx_pic->pp.main),
> scale ? &ctx_pic->qm : NULL, scale ? sizeof(ctx_pic->qm) : 0,
> commit_bitstream_and_slice_buffer);
> return ret;
> diff --git a/libavcodec/dxva2_internal.h b/libavcodec/dxva2_internal.h
> index 224a867ebc..38e34ce64f 100644
> --- a/libavcodec/dxva2_internal.h
> +++ b/libavcodec/dxva2_internal.h
> @@ -64,6 +64,42 @@
> #include <dxva.h>
> #endif
>
> +#pragma pack(push, 1)
> +typedef struct
> +{
> + DXVA_PicParams_HEVC main;
> +
> + // HEVC Range Extension
> + __C89_NAMELESS union {
> + __C89_NAMELESS struct {
> + UINT32 transform_skip_rotation_enabled_flag : 1;
> + UINT32 transform_skip_context_enabled_flag : 1;
> + UINT32 implicit_rdpcm_enabled_flag : 1;
> + UINT32 explicit_rdpcm_enabled_flag : 1;
> + UINT32 extended_precision_processing_flag : 1;
> + UINT32 intra_smoothing_disabled_flag : 1;
> + UINT32 high_precision_offsets_enabled_flag : 1;
> + UINT32 persistent_rice_adaptation_enabled_flag : 1;
> + UINT32 cabac_bypass_alignment_enabled_flag : 1;
> + UINT32 cross_component_prediction_enabled_flag : 1;
> + UINT32 chroma_qp_offset_list_enabled_flag : 1;
> + UINT32 BitDepthLuma16 : 1; // TODO merge in ReservedBits5 if not needed
> + UINT32 BitDepthChroma16 : 1; // TODO merge in ReservedBits5 if not needed
> + UINT32 ReservedBits8 : 19;
> + };
> + UINT32 dwRangeExtensionFlags;
> + };
> +
> + UCHAR diff_cu_chroma_qp_offset_depth;
> + UCHAR chroma_qp_offset_list_len_minus1;
> + UCHAR log2_sao_offset_scale_luma;
> + UCHAR log2_sao_offset_scale_chroma;
> + UCHAR log2_max_transform_skip_block_size_minus2;
> + CHAR cb_qp_offset_list[6];
> + CHAR cr_qp_offset_list[6];
> +} DXVA_PicParams_HEVC_Rext;
> +#pragma pack(pop)
Where is this structure copied from? (Is it under a licence with any relevant conditions?)
> +
> #include "libavutil/hwcontext.h"
>
> #include "avcodec.h"
> @@ -171,7 +207,7 @@ void ff_dxva2_h264_fill_picture_parameters(const AVCodecContext *avctx, AVDXVACo
>
> void ff_dxva2_h264_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_H264 *qm);
>
> -void ff_dxva2_hevc_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_HEVC *pp);
> +void ff_dxva2_hevc_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_HEVC_Rext *ppext);
>
> void ff_dxva2_hevc_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_HEVC *qm);
>
> diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
> index b5311ae510..508f279933 100644
> --- a/libavcodec/hevcdec.c
> +++ b/libavcodec/hevcdec.c
> @@ -463,6 +463,10 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps)
> #endif
> break;
> case AV_PIX_FMT_YUV444P:
> +#if CONFIG_HEVC_D3D11VA_HWACCEL
> + *fmt++ = AV_PIX_FMT_D3D11VA_VLD;
Have you tested this with the legacy API?
> + *fmt++ = AV_PIX_FMT_D3D11;
> +#endif
> #if CONFIG_HEVC_VAAPI_HWACCEL
> *fmt++ = AV_PIX_FMT_VAAPI;
> #endif
> @@ -481,6 +485,10 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps)
> break;
> case AV_PIX_FMT_YUV422P:
> case AV_PIX_FMT_YUV422P10LE:
> +#if CONFIG_HEVC_D3D11VA_HWACCEL
> + *fmt++ = AV_PIX_FMT_D3D11VA_VLD;
> + *fmt++ = AV_PIX_FMT_D3D11;
> +#endif
> #if CONFIG_HEVC_VAAPI_HWACCEL
> *fmt++ = AV_PIX_FMT_VAAPI;
> #endif
> @@ -498,6 +506,10 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps)
> /* NOTE: fallthrough */
> case AV_PIX_FMT_YUV420P12:
> case AV_PIX_FMT_YUV444P12:
> +#if CONFIG_HEVC_D3D11VA_HWACCEL
> + *fmt++ = AV_PIX_FMT_D3D11VA_VLD;
> + *fmt++ = AV_PIX_FMT_D3D11;
> +#endif
> #if CONFIG_HEVC_VAAPI_HWACCEL
> *fmt++ = AV_PIX_FMT_VAAPI;
> #endif
> @@ -512,6 +524,10 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps)
> #endif
> break;
> case AV_PIX_FMT_YUV422P12:
> +#if CONFIG_HEVC_D3D11VA_HWACCEL
> + *fmt++ = AV_PIX_FMT_D3D11VA_VLD;
> + *fmt++ = AV_PIX_FMT_D3D11;
> +#endif
> #if CONFIG_HEVC_VAAPI_HWACCEL
> *fmt++ = AV_PIX_FMT_VAAPI;
> #endif
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> index 2fd3561c88..79b1f34b3b 100644
> --- a/libavutil/hwcontext_d3d11va.c
> +++ b/libavutil/hwcontext_d3d11va.c
Move the changes to the hwcontext into a separate patch.
> @@ -94,6 +94,7 @@ static const struct {
> { DXGI_FORMAT_Y210, AV_PIX_FMT_Y210 },
> { DXGI_FORMAT_Y410, AV_PIX_FMT_XV30 },
> { DXGI_FORMAT_P016, AV_PIX_FMT_P012 },
> + { DXGI_FORMAT_P016, AV_PIX_FMT_P016 },
Ok here because it only maps AV -> DXGI.
> { DXGI_FORMAT_Y216, AV_PIX_FMT_Y212 },
> { DXGI_FORMAT_Y416, AV_PIX_FMT_XV36 },
> // Special opaque formats. The pix_fmt is merely a place holder, as the
> @@ -394,11 +395,28 @@ static void fill_texture_ptrs(uint8_t *data[4], int linesize[4],
> D3D11_TEXTURE2D_DESC *desc,
> D3D11_MAPPED_SUBRESOURCE *map)
> {
> - int i;
> -
> - for (i = 0; i < 4; i++)
> - linesize[i] = map->RowPitch;
> + int width;
> + int codedbytes = 1;
> +
> + switch (ctx->sw_format) {
> + case AV_PIX_FMT_P010:
> + case AV_PIX_FMT_P016:
> + case AV_PIX_FMT_YUYV422:
> + codedbytes = 2;
> + break;
> + case AV_PIX_FMT_Y210:
> + case AV_PIX_FMT_Y212:
> + case AV_PIX_FMT_VUYX:
> + case AV_PIX_FMT_XV30:
> + codedbytes = 4;
> + break;
> + case AV_PIX_FMT_XV36:
> + codedbytes = 8;
> + break;
> + }
>
> + width = map->RowPitch / codedbytes;
> + av_image_fill_linesizes(linesize, ctx->sw_format, width);
> av_image_fill_pointers(data, ctx->sw_format, desc->Height,
> (uint8_t*)map->pData, linesize);
Can you explain why this change is being made?
(av_image_fill_pointers() has been given the format and finds the element sizes from pixdesc, so I think it should be working. If it isn't then maybe it should be fixed.)
> }
> --
> 2.43.0.windows.1
Thanks,
- Mark
More information about the ffmpeg-devel
mailing list