1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-11-26 19:01:44 +02:00
FFmpeg/libavcodec/libopenh264enc.c
Andreas Rheinhardt 790f793844 avutil/common: Don't auto-include mem.h
There are lots of files that don't need it: The number of object
files that actually need it went down from 2011 to 884 here.

Keep it for external users in order to not cause breakages.

Also improve the other headers a bit while just at it.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-03-31 00:08:43 +01:00

452 lines
18 KiB
C

/*
* OpenH264 video encoder
* Copyright (C) 2014 Martin Storsjo
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <wels/codec_api.h>
#include <wels/codec_ver.h>
#include "libavutil/attributes.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "avcodec.h"
#include "codec_internal.h"
#include "encode.h"
#include "libopenh264.h"
#if !OPENH264_VER_AT_LEAST(1, 6)
#define SM_SIZELIMITED_SLICE SM_DYN_SLICE
#endif
#define TARGET_BITRATE_DEFAULT 2*1000*1000
typedef struct SVCContext {
const AVClass *av_class;
ISVCEncoder *encoder;
int slice_mode;
int loopfilter;
int profile;
int max_nal_size;
int skip_frames;
int skipped;
int coder;
// rate control mode
int rc_mode;
} SVCContext;
#define OFFSET(x) offsetof(SVCContext, x)
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
#define DEPRECATED AV_OPT_FLAG_DEPRECATED
static const AVOption options[] = {
{ "loopfilter", "enable loop filter", OFFSET(loopfilter), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE },
{ "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xffff, VE, .unit = "profile" },
#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, .unit = "profile"
{ PROFILE("constrained_baseline", AV_PROFILE_H264_CONSTRAINED_BASELINE) },
{ PROFILE("main", AV_PROFILE_H264_MAIN) },
{ PROFILE("high", AV_PROFILE_H264_HIGH) },
#undef PROFILE
{ "max_nal_size", "set maximum NAL size in bytes", OFFSET(max_nal_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
{ "allow_skip_frames", "allow skipping frames to hit the target bitrate", OFFSET(skip_frames), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
{ "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, .unit = "coder" },
{ "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
{ "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
{ "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
{ "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
{ "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
{ "rc_mode", "Select rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = RC_QUALITY_MODE }, RC_OFF_MODE, RC_TIMESTAMP_MODE, VE, .unit = "rc_mode" },
{ "off", "bit rate control off", 0, AV_OPT_TYPE_CONST, { .i64 = RC_OFF_MODE }, 0, 0, VE, .unit = "rc_mode" },
{ "quality", "quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_QUALITY_MODE }, 0, 0, VE, .unit = "rc_mode" },
{ "bitrate", "bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BITRATE_MODE }, 0, 0, VE, .unit = "rc_mode" },
{ "buffer", "using buffer status to adjust the video quality (no bitrate control)", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BUFFERBASED_MODE }, 0, 0, VE, .unit = "rc_mode" },
#if OPENH264_VER_AT_LEAST(1, 4)
{ "timestamp", "bit rate control based on timestamp", 0, AV_OPT_TYPE_CONST, { .i64 = RC_TIMESTAMP_MODE }, 0, 0, VE, .unit = "rc_mode" },
#endif
{ NULL }
};
static const AVClass class = {
.class_name = "libopenh264enc",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
static av_cold int svc_encode_close(AVCodecContext *avctx)
{
SVCContext *s = avctx->priv_data;
if (s->encoder)
WelsDestroySVCEncoder(s->encoder);
if (s->skipped > 0)
av_log(avctx, AV_LOG_WARNING, "%d frames skipped\n", s->skipped);
return 0;
}
static av_cold int svc_encode_init(AVCodecContext *avctx)
{
SVCContext *s = avctx->priv_data;
SEncParamExt param = { 0 };
int log_level;
WelsTraceCallback callback_function;
AVCPBProperties *props;
if (WelsCreateSVCEncoder(&s->encoder)) {
av_log(avctx, AV_LOG_ERROR, "Unable to create encoder\n");
return AVERROR_UNKNOWN;
}
// Pass all libopenh264 messages to our callback, to allow ourselves to filter them.
log_level = WELS_LOG_DETAIL;
(*s->encoder)->SetOption(s->encoder, ENCODER_OPTION_TRACE_LEVEL, &log_level);
// Set the logging callback function to one that uses av_log() (see implementation above).
callback_function = (WelsTraceCallback) ff_libopenh264_trace_callback;
(*s->encoder)->SetOption(s->encoder, ENCODER_OPTION_TRACE_CALLBACK, &callback_function);
// Set the AVCodecContext as the libopenh264 callback context so that it can be passed to av_log().
(*s->encoder)->SetOption(s->encoder, ENCODER_OPTION_TRACE_CALLBACK_CONTEXT, &avctx);
(*s->encoder)->GetDefaultParams(s->encoder, &param);
if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
param.fMaxFrameRate = av_q2d(avctx->framerate);
} else {
FF_DISABLE_DEPRECATION_WARNINGS
param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base)
#if FF_API_TICKS_PER_FRAME
/ FFMAX(avctx->ticks_per_frame, 1)
#endif
;
FF_ENABLE_DEPRECATION_WARNINGS
}
param.iPicWidth = avctx->width;
param.iPicHeight = avctx->height;
param.iTargetBitrate = avctx->bit_rate > 0 ? avctx->bit_rate : TARGET_BITRATE_DEFAULT;
param.iMaxBitrate = FFMAX(avctx->rc_max_rate, avctx->bit_rate);
param.iRCMode = s->rc_mode;
if (avctx->qmax >= 0)
param.iMaxQp = av_clip(avctx->qmax, 1, 51);
if (avctx->qmin >= 0)
param.iMinQp = av_clip(avctx->qmin, 1, param.iMaxQp);
param.iTemporalLayerNum = 1;
param.iSpatialLayerNum = 1;
param.bEnableDenoise = 0;
param.bEnableBackgroundDetection = 1;
param.bEnableAdaptiveQuant = 1;
param.bEnableFrameSkip = s->skip_frames;
param.bEnableLongTermReference = 0;
param.iLtrMarkPeriod = 30;
if (avctx->gop_size >= 0)
param.uiIntraPeriod = avctx->gop_size;
#if OPENH264_VER_AT_LEAST(1, 4)
param.eSpsPpsIdStrategy = CONSTANT_ID;
#else
param.bEnableSpsPpsIdAddition = 0;
#endif
param.bPrefixNalAddingCtrl = 0;
param.iLoopFilterDisableIdc = !s->loopfilter;
param.iEntropyCodingModeFlag = s->coder >= 0 ? s->coder : 1;
param.iMultipleThreadIdc = avctx->thread_count;
/* Allow specifying the libopenh264 profile through AVCodecContext. */
if (AV_PROFILE_UNKNOWN == s->profile &&
AV_PROFILE_UNKNOWN != avctx->profile)
switch (avctx->profile) {
case AV_PROFILE_H264_HIGH:
case AV_PROFILE_H264_MAIN:
case AV_PROFILE_H264_CONSTRAINED_BASELINE:
s->profile = avctx->profile;
break;
default:
av_log(avctx, AV_LOG_WARNING,
"Unsupported avctx->profile: %d.\n", avctx->profile);
break;
}
if (s->profile == AV_PROFILE_UNKNOWN && s->coder >= 0)
s->profile = s->coder == 0 ? AV_PROFILE_H264_CONSTRAINED_BASELINE :
#if OPENH264_VER_AT_LEAST(1, 8)
AV_PROFILE_H264_HIGH;
#else
AV_PROFILE_H264_MAIN;
#endif
switch (s->profile) {
case AV_PROFILE_H264_HIGH:
av_log(avctx, AV_LOG_VERBOSE, "Using %s, "
"select EProfileIdc PRO_HIGH in libopenh264.\n",
param.iEntropyCodingModeFlag ? "CABAC" : "CAVLC");
break;
case AV_PROFILE_H264_MAIN:
av_log(avctx, AV_LOG_VERBOSE, "Using %s, "
"select EProfileIdc PRO_MAIN in libopenh264.\n",
param.iEntropyCodingModeFlag ? "CABAC" : "CAVLC");
break;
case AV_PROFILE_H264_CONSTRAINED_BASELINE:
case AV_PROFILE_UNKNOWN:
s->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE;
param.iEntropyCodingModeFlag = 0;
av_log(avctx, AV_LOG_VERBOSE, "Using CAVLC, "
"select EProfileIdc PRO_BASELINE in libopenh264.\n");
break;
default:
s->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE;
param.iEntropyCodingModeFlag = 0;
av_log(avctx, AV_LOG_WARNING, "Unsupported profile, "
"select EProfileIdc PRO_BASELINE in libopenh264.\n");
break;
}
param.sSpatialLayers[0].iVideoWidth = param.iPicWidth;
param.sSpatialLayers[0].iVideoHeight = param.iPicHeight;
param.sSpatialLayers[0].fFrameRate = param.fMaxFrameRate;
param.sSpatialLayers[0].iSpatialBitrate = param.iTargetBitrate;
param.sSpatialLayers[0].iMaxSpatialBitrate = param.iMaxBitrate;
param.sSpatialLayers[0].uiProfileIdc = s->profile;
#if OPENH264_VER_AT_LEAST(1, 7)
if (avctx->sample_aspect_ratio.num && avctx->sample_aspect_ratio.den) {
// Table E-1.
static const AVRational sar_idc[] = {
{ 0, 0 }, // Unspecified (never written here).
{ 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 },
{ 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 },
{ 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 },
{ 160, 99 }, // Last 3 are unknown to openh264: { 4, 3 }, { 3, 2 }, { 2, 1 },
};
static const ESampleAspectRatio asp_idc[] = {
ASP_UNSPECIFIED,
ASP_1x1, ASP_12x11, ASP_10x11, ASP_16x11,
ASP_40x33, ASP_24x11, ASP_20x11, ASP_32x11,
ASP_80x33, ASP_18x11, ASP_15x11, ASP_64x33,
ASP_160x99,
};
int num, den, i;
av_reduce(&num, &den, avctx->sample_aspect_ratio.num,
avctx->sample_aspect_ratio.den, 65535);
for (i = 1; i < FF_ARRAY_ELEMS(sar_idc); i++) {
if (num == sar_idc[i].num &&
den == sar_idc[i].den)
break;
}
if (i == FF_ARRAY_ELEMS(sar_idc)) {
param.sSpatialLayers[0].eAspectRatio = ASP_EXT_SAR;
param.sSpatialLayers[0].sAspectRatioExtWidth = num;
param.sSpatialLayers[0].sAspectRatioExtHeight = den;
} else {
param.sSpatialLayers[0].eAspectRatio = asp_idc[i];
}
param.sSpatialLayers[0].bAspectRatioPresent = true;
} else {
param.sSpatialLayers[0].bAspectRatioPresent = false;
}
#endif
if ((avctx->slices > 1) && (s->max_nal_size)) {
av_log(avctx, AV_LOG_ERROR,
"Invalid combination -slices %d and -max_nal_size %d.\n",
avctx->slices, s->max_nal_size);
return AVERROR(EINVAL);
}
if (avctx->slices > 1)
s->slice_mode = SM_FIXEDSLCNUM_SLICE;
if (s->max_nal_size)
s->slice_mode = SM_SIZELIMITED_SLICE;
#if OPENH264_VER_AT_LEAST(1, 6)
param.sSpatialLayers[0].sSliceArgument.uiSliceMode = s->slice_mode;
param.sSpatialLayers[0].sSliceArgument.uiSliceNum = avctx->slices;
#else
param.sSpatialLayers[0].sSliceCfg.uiSliceMode = s->slice_mode;
param.sSpatialLayers[0].sSliceCfg.sSliceArgument.uiSliceNum = avctx->slices;
#endif
if (avctx->slices == 0 && s->slice_mode == SM_FIXEDSLCNUM_SLICE)
av_log(avctx, AV_LOG_WARNING, "Slice count will be set automatically\n");
if (s->slice_mode == SM_SIZELIMITED_SLICE) {
if (s->max_nal_size) {
param.uiMaxNalSize = s->max_nal_size;
#if OPENH264_VER_AT_LEAST(1, 6)
param.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = s->max_nal_size;
#else
param.sSpatialLayers[0].sSliceCfg.sSliceArgument.uiSliceSizeConstraint = s->max_nal_size;
#endif
} else {
av_log(avctx, AV_LOG_ERROR, "Invalid -max_nal_size, "
"specify a valid max_nal_size to use -slice_mode dyn\n");
return AVERROR(EINVAL);
}
}
#if OPENH264_VER_AT_LEAST(1, 6)
param.sSpatialLayers[0].uiVideoFormat = VF_UNDEF;
if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) {
param.sSpatialLayers[0].bFullRange = (avctx->color_range == AVCOL_RANGE_JPEG);
} else if (avctx->pix_fmt == AV_PIX_FMT_YUVJ420P)
param.sSpatialLayers[0].bFullRange = 1;
if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED ||
avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||
avctx->color_trc != AVCOL_TRC_UNSPECIFIED) {
param.sSpatialLayers[0].bColorDescriptionPresent = true;
}
if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED)
param.sSpatialLayers[0].uiColorMatrix = avctx->colorspace;
if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED)
param.sSpatialLayers[0].uiColorPrimaries = avctx->color_primaries;
if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED)
param.sSpatialLayers[0].uiTransferCharacteristics = avctx->color_trc;
param.sSpatialLayers[0].bVideoSignalTypePresent =
(param.sSpatialLayers[0].bFullRange || param.sSpatialLayers[0].bColorDescriptionPresent);
#endif
if ((*s->encoder)->InitializeExt(s->encoder, &param) != cmResultSuccess) {
av_log(avctx, AV_LOG_ERROR, "Initialize failed\n");
return AVERROR_UNKNOWN;
}
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
SFrameBSInfo fbi = { 0 };
int i, size = 0;
(*s->encoder)->EncodeParameterSets(s->encoder, &fbi);
for (i = 0; i < fbi.sLayerInfo[0].iNalCount; i++)
size += fbi.sLayerInfo[0].pNalLengthInByte[i];
avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return AVERROR(ENOMEM);
avctx->extradata_size = size;
memcpy(avctx->extradata, fbi.sLayerInfo[0].pBsBuf, size);
}
props = ff_encode_add_cpb_side_data(avctx);
if (!props)
return AVERROR(ENOMEM);
props->max_bitrate = param.iMaxBitrate;
props->avg_bitrate = param.iTargetBitrate;
return 0;
}
static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet)
{
SVCContext *s = avctx->priv_data;
SFrameBSInfo fbi = { 0 };
int i, ret;
int encoded;
SSourcePicture sp = { 0 };
int size = 0, layer, first_layer = 0;
int layer_size[MAX_LAYER_NUM_OF_FRAME] = { 0 };
sp.iColorFormat = videoFormatI420;
for (i = 0; i < 3; i++) {
sp.iStride[i] = frame->linesize[i];
sp.pData[i] = frame->data[i];
}
sp.iPicWidth = avctx->width;
sp.iPicHeight = avctx->height;
if (frame->pict_type == AV_PICTURE_TYPE_I) {
(*s->encoder)->ForceIntraFrame(s->encoder, true);
}
encoded = (*s->encoder)->EncodeFrame(s->encoder, &sp, &fbi);
if (encoded != cmResultSuccess) {
av_log(avctx, AV_LOG_ERROR, "EncodeFrame failed\n");
return AVERROR_UNKNOWN;
}
if (fbi.eFrameType == videoFrameTypeSkip) {
s->skipped++;
av_log(avctx, AV_LOG_DEBUG, "frame skipped\n");
return 0;
}
first_layer = 0;
// Normal frames are returned with one single layer, while IDR
// frames have two layers, where the first layer contains the SPS/PPS.
// If using global headers, don't include the SPS/PPS in the returned
// packet - thus, only return one layer.
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
first_layer = fbi.iLayerNum - 1;
for (layer = first_layer; layer < fbi.iLayerNum; layer++) {
for (i = 0; i < fbi.sLayerInfo[layer].iNalCount; i++)
layer_size[layer] += fbi.sLayerInfo[layer].pNalLengthInByte[i];
size += layer_size[layer];
}
av_log(avctx, AV_LOG_DEBUG, "%d slices\n", fbi.sLayerInfo[fbi.iLayerNum - 1].iNalCount);
if ((ret = ff_get_encode_buffer(avctx, avpkt, size, 0)))
return ret;
size = 0;
for (layer = first_layer; layer < fbi.iLayerNum; layer++) {
memcpy(avpkt->data + size, fbi.sLayerInfo[layer].pBsBuf, layer_size[layer]);
size += layer_size[layer];
}
avpkt->pts = frame->pts;
if (fbi.eFrameType == videoFrameTypeIDR)
avpkt->flags |= AV_PKT_FLAG_KEY;
*got_packet = 1;
return 0;
}
static const FFCodecDefault svc_enc_defaults[] = {
{ "b", "0" },
{ "g", "-1" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ NULL },
};
const FFCodec ff_libopenh264_encoder = {
.p.name = "libopenh264",
CODEC_LONG_NAME("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_H264,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS |
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(SVCContext),
.init = svc_encode_init,
FF_CODEC_ENCODE_CB(svc_encode_frame),
.close = svc_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP |
FF_CODEC_CAP_AUTO_THREADS,
.p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUVJ420P,
AV_PIX_FMT_NONE },
.defaults = svc_enc_defaults,
.p.priv_class = &class,
.p.wrapper_name = "libopenh264",
};