It mimics same options in libx264 and qsv_h264 encoders --- libavcodec/internal.h | 15 +++++++++ libavcodec/libx264.c | 49 ++++++++++------------------- libavcodec/nvenc.c | 28 +++++++++++++++-- libavcodec/nvenc.h | 1 + libavcodec/nvenc_h264.c | 1 + libavcodec/nvenc_hevc.c | 1 + libavcodec/qsvenc.c | 4 +-- libavcodec/qsvenc_h264.c | 82 +++++++++++++++--------------------------------- libavcodec/utils.c | 44 ++++++++++++++++++++++++++ 9 files changed, 131 insertions(+), 94 deletions(-)
diff --git a/libavcodec/internal.h b/libavcodec/internal.h index be54471..000fe26 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -345,4 +345,19 @@ AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx); int ff_side_data_set_encoder_stats(AVPacket *pkt, int quality, int64_t *error, int error_count, int pict_type); +/** + * Check AVFrame for A53 side data and allocate and fill SEI message with A53 info + * + * @param frame Raw frame to get A53 side data from + * @param prefix_len Number of bytes to allocate before SEI message + * @param data Pointer to a variable to store allocated memory + * Upon return the variable will hold NULL on error or if frame has no A53 info. + * Otherwise it will point to prefix_len uninitialized bytes followed by + * *sei_size SEI message + * @param sei_size Pointer to a variable to store generated SEI message length + * @return Zero on success, negative error code on failure + */ +int ff_alloc_a53_sei(const AVFrame *frame, size_t prefix_len, + void **data, size_t *sei_size); + #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 449d76d..0063ae0 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -310,46 +310,29 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, reconfig_encoder(ctx, frame); if (x4->a53_cc) { - side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); - if (side_data) { + void *sei_data; + size_t sei_size; + + ret = ff_alloc_a53_sei(frame, 0, &sei_data, &sei_size); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); + } else if (sei_data) { x4->pic.extra_sei.payloads = av_mallocz(sizeof(x4->pic.extra_sei.payloads[0])); if (x4->pic.extra_sei.payloads == NULL) { av_log(ctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); - goto skip_a53cc; + av_free(sei_data); + } else { + x4->pic.extra_sei.sei_free = av_free; + + x4->pic.extra_sei.payloads[0].payload_size = sei_size; + x4->pic.extra_sei.payloads[0].payload = sei_data; + x4->pic.extra_sei.num_payloads = 1; + x4->pic.extra_sei.payloads[0].payload_type = 4; } - x4->pic.extra_sei.sei_free = av_free; - - x4->pic.extra_sei.payloads[0].payload_size = side_data->size + 11; - x4->pic.extra_sei.payloads[0].payload = av_mallocz(x4->pic.extra_sei.payloads[0].payload_size); - if (x4->pic.extra_sei.payloads[0].payload == NULL) { - av_log(ctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); - av_freep(&x4->pic.extra_sei.payloads); - goto skip_a53cc; - } - x4->pic.extra_sei.num_payloads = 1; - x4->pic.extra_sei.payloads[0].payload_type = 4; - memcpy(x4->pic.extra_sei.payloads[0].payload + 10, side_data->data, side_data->size); - x4->pic.extra_sei.payloads[0].payload[0] = 181; - x4->pic.extra_sei.payloads[0].payload[1] = 0; - x4->pic.extra_sei.payloads[0].payload[2] = 49; - - /** - * 'GA94' is standard in North America for ATSC, but hard coding - * this style may not be the right thing to do -- other formats - * do exist. This information is not available in the side_data - * so we are going with this right now. - */ - AV_WL32(x4->pic.extra_sei.payloads[0].payload + 3, - MKTAG('G', 'A', '9', '4')); - x4->pic.extra_sei.payloads[0].payload[7] = 3; - x4->pic.extra_sei.payloads[0].payload[8] = - ((side_data->size/3) & 0x1f) | 0x40; - x4->pic.extra_sei.payloads[0].payload[9] = 0; - x4->pic.extra_sei.payloads[0].payload[side_data->size+10] = 255; } } } -skip_a53cc: + do { if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0) return AVERROR_EXTERNAL; diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c index d3115f4..010819a 100644 --- a/libavcodec/nvenc.c +++ b/libavcodec/nvenc.c @@ -1374,7 +1374,8 @@ static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame, } static void nvenc_codec_specific_pic_params(AVCodecContext *avctx, - NV_ENC_PIC_PARAMS *params) + NV_ENC_PIC_PARAMS *params, + NV_ENC_SEI_PAYLOAD *sei_data) { NvencContext *ctx = avctx->priv_data; @@ -1384,12 +1385,20 @@ static void nvenc_codec_specific_pic_params(AVCodecContext *avctx, ctx->encode_config.encodeCodecConfig.h264Config.sliceMode; params->codecPicParams.h264PicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData; + if (sei_data) { + params->codecPicParams.h264PicParams.seiPayloadArray = sei_data; + params->codecPicParams.h264PicParams.seiPayloadArrayCnt = 1; + } break; case AV_CODEC_ID_HEVC: params->codecPicParams.hevcPicParams.sliceMode = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceMode; params->codecPicParams.hevcPicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData; + if (sei_data) { + params->codecPicParams.hevcPicParams.seiPayloadArray = sei_data; + params->codecPicParams.hevcPicParams.seiPayloadArrayCnt = 1; + } break; } } @@ -1576,6 +1585,8 @@ int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, { NVENCSTATUS nv_status; NvencSurface *tmpoutsurf, *inSurf; + NV_ENC_SEI_PAYLOAD *sei_data = NULL; + size_t sei_size; int res; NvencContext *ctx = avctx->priv_data; @@ -1616,12 +1627,25 @@ int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, pic_params.encodePicFlags = 0; pic_params.inputTimeStamp = frame->pts; - nvenc_codec_specific_pic_params(avctx, &pic_params); + if (ctx->a53_cc) { + if (ff_alloc_a53_sei(frame, sizeof(NV_ENC_SEI_PAYLOAD), (void**)&sei_data, &sei_size) < 0) { + av_log(ctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); + } + + if (sei_data) { + sei_data->payloadSize = (uint32_t)sei_size; + sei_data->payloadType = 4; + sei_data->payload = (uint8_t*)(sei_data + 1); + } + } + + nvenc_codec_specific_pic_params(avctx, &pic_params, sei_data); } else { pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; } nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params); + av_free(sei_data); if (nv_status != NV_ENC_SUCCESS && nv_status != NV_ENC_ERR_NEED_MORE_INPUT) return nvenc_print_error(avctx, nv_status, "EncodePicture failed!"); diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h index 961cbc7..71f538a 100644 --- a/libavcodec/nvenc.h +++ b/libavcodec/nvenc.h @@ -174,6 +174,7 @@ typedef struct NvencContext int device; int flags; int async_depth; + int a53_cc; } NvencContext; int ff_nvenc_encode_init(AVCodecContext *avctx); diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c index cdb80f2..eb9cb31 100644 --- a/libavcodec/nvenc_h264.c +++ b/libavcodec/nvenc_h264.c @@ -83,6 +83,7 @@ static const AVOption options[] = { { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, "gpu" }, { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, "gpu" }, { "delay", "Delay frame output by the given amount of frames", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 0, INT_MAX, VE }, + { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE}, { NULL } }; diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c index cef19f7..5458eef 100644 --- a/libavcodec/nvenc_hevc.c +++ b/libavcodec/nvenc_hevc.c @@ -80,6 +80,7 @@ static const AVOption options[] = { { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, "device" }, { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, "device" }, { "delay", "Delay frame output by the given amount of frames", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 0, INT_MAX, VE }, + { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE}, { NULL } }; diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index 132cf47..f56cb61 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -781,9 +781,7 @@ static void free_encoder_ctrl_payloads(mfxEncodeCtrl* enc_ctrl) if (enc_ctrl) { int i; for (i = 0; i < enc_ctrl->NumPayload && i < QSV_MAX_ENC_PAYLOAD; i++) { - mfxPayload* pay = enc_ctrl->Payload[i]; - av_free(enc_ctrl->Payload[i]->Data); - av_free(pay); + av_free(enc_ctrl->Payload[i]); } enc_ctrl->NumPayload = 0; } diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index 66e5ee8..cd08d86 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -43,63 +43,33 @@ typedef struct QSVH264EncContext { static int qsv_h264_set_encode_ctrl(AVCodecContext *avctx, const AVFrame *frame, mfxEncodeCtrl* enc_ctrl) { - AVFrameSideData *side_data = NULL; - QSVH264EncContext *qh264 = avctx->priv_data; - QSVEncContext *q = &qh264->qsv; - if (q->a53_cc && frame) { - side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); - if (side_data) { - - int sei_payload_size = 0; - mfxU8* sei_data = NULL; - mfxPayload* payload = NULL; - - sei_payload_size = side_data->size + 13; - - sei_data = av_mallocz(sei_payload_size); - if (!sei_data) { - av_log(avctx, AV_LOG_ERROR, "No memory for CC, skipping...\n"); - return AVERROR(ENOMEM); - } - - // SEI header - sei_data[0] = 4; - sei_data[1] = sei_payload_size - 2; // size of SEI data - - // country code - sei_data[2] = 181; - sei_data[3] = 0; - sei_data[4] = 49; - - // ATSC_identifier - using 'GA94' only - AV_WL32(sei_data + 5, - MKTAG('G', 'A', '9', '4')); - sei_data[9] = 3; - sei_data[10] = - ((side_data->size/3) & 0x1f) | 0xC0; - - sei_data[11] = 0xFF; // reserved - - memcpy(sei_data + 12, side_data->data, side_data->size); - - sei_data[side_data->size+12] = 255; - - payload = av_mallocz(sizeof(mfxPayload)); - if (!payload) { - av_log(avctx, AV_LOG_ERROR, "No memory, skipping captions\n"); - av_freep(&sei_data); - return AVERROR(ENOMEM); - } - payload->BufSize = side_data->size + 13; - payload->NumBit = payload->BufSize * 8; - payload->Type = 4; - payload->Data = sei_data; - - enc_ctrl->NumExtParam = 0; - enc_ctrl->NumPayload = 1; - enc_ctrl->Payload[0] = payload; - } + AVFrameSideData *side_data = NULL; + QSVH264EncContext *qh264 = avctx->priv_data; + QSVEncContext *q = &qh264->qsv; + mfxPayload* payload; + mfxU8* sei_data; + size_t sei_size; + int res; + + res = ff_alloc_a53_sei(frame, sizeof(mfxPayload) + 2, (void**)&payload, &sei_size); + if (res < 0) + return res; + + sei_data = (mfxU8*)(payload + 1); + // SEI header + sei_data[0] = 4; + sei_data[1] = (mfxU8)sei_size; // size of SEI data + // SEI data filled in by ff_alloc_a53_sei + + payload->BufSize = sei_size + 2; + payload->NumBit = payload->BufSize * 8; + payload->Type = 4; + payload->Data = sei_data; + + enc_ctrl->NumExtParam = 0; + enc_ctrl->NumPayload = 1; + enc_ctrl->Payload[0] = payload; } return 0; } diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 7b99526..0367e20 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -4177,3 +4177,47 @@ int avcodec_parameters_to_context(AVCodecContext *codec, return 0; } + +int ff_alloc_a53_sei(const AVFrame *frame, size_t prefix_len, + void **data, size_t *sei_size) +{ + AVFrameSideData *side_data = NULL; + uint8_t *sei_data; + + if (frame) + side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); + + if (!side_data) + { + *data = NULL; + return 0; + } + + *sei_size = side_data->size + 11; + *data = av_mallocz(*sei_size + prefix_len); + if (!*data) + return AVERROR(ENOMEM); + sei_data = (uint8_t*)*data + prefix_len; + + // country code + sei_data[0] = 181; + sei_data[1] = 0; + sei_data[2] = 49; + + /** + * 'GA94' is standard in North America for ATSC, but hard coding + * this style may not be the right thing to do -- other formats + * do exist. This information is not available in the side_data + * so we are going with this right now. + */ + AV_WL32(sei_data + 3, MKTAG('G', 'A', '9', '4')); + sei_data[7] = 3; + sei_data[8] = ((side_data->size/3) & 0x1f) | 0x40; + sei_data[9] = 0; + + memcpy(sei_data + 10, side_data->data, side_data->size); + + sei_data[side_data->size+10] = 255; + + return 0; +}
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel