1346 lines · 48.1 KB · C
RawDownload
/* * 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 <string.h> #include <va/va.h>#include <va/va_enc_h264.h> #include "libavutil/avassert.h"#include "libavutil/common.h"#include "libavutil/internal.h"#include "libavutil/opt.h" #include "avcodec.h"#include "cbs.h"#include "cbs_h264.h"#include "h264.h"#include "h264_levels.h"#include "h264_sei.h"#include "internal.h"#include "vaapi_encode.h" enum {    SEI_TIMING         = 0x01,    SEI_IDENTIFIER     = 0x02,    SEI_RECOVERY_POINT = 0x04,}; // Random (version 4) ISO 11578 UUID.static const uint8_t vaapi_encode_h264_sei_identifier_uuid[16] = {    0x59, 0x94, 0x8b, 0x28, 0x11, 0xec, 0x45, 0xaf,    0x96, 0x75, 0x19, 0xd4, 0x1f, 0xea, 0xa9, 0x4d,}; typedef struct VAAPIEncodeH264Picture {    int frame_num;    int pic_order_cnt;     int64_t last_idr_frame;    uint16_t idr_pic_id;     int primary_pic_type;    int slice_type;     int cpb_delay;    int dpb_delay;} VAAPIEncodeH264Picture; typedef struct VAAPIEncodeH264Context {    VAAPIEncodeContext common;     // User options.    int qp;    int quality;    int coder;    int aud;    int sei;    int profile;    int level;     // Derived settings.    int mb_width;    int mb_height;     int fixed_qp_idr;    int fixed_qp_p;    int fixed_qp_b;     int dpb_frames;     // Writer structures.    CodedBitstreamContext *cbc;    CodedBitstreamFragment current_access_unit;     H264RawAUD   raw_aud;    H264RawSPS   raw_sps;    H264RawPPS   raw_pps;    H264RawSlice raw_slice;     H264RawSEIBufferingPeriod      sei_buffering_period;    H264RawSEIPicTiming            sei_pic_timing;    H264RawSEIRecoveryPoint        sei_recovery_point;    SEIRawUserDataUnregistered     sei_identifier;    char                          *sei_identifier_string;     int aud_needed;    int sei_needed;    int sei_cbr_workaround_needed;} VAAPIEncodeH264Context;  static int vaapi_encode_h264_write_access_unit(AVCodecContext *avctx,                                               char *data, size_t *data_len,                                               CodedBitstreamFragment *au){    VAAPIEncodeH264Context *priv = avctx->priv_data;    int err;     err = ff_cbs_write_fragment_data(priv->cbc, au);    if (err < 0) {        av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n");        return err;    }     if (*data_len < 8 * au->data_size - au->data_bit_padding) {        av_log(avctx, AV_LOG_ERROR, "Access unit too large: "               "%zu < %zu.\n", *data_len,               8 * au->data_size - au->data_bit_padding);        return AVERROR(ENOSPC);    }     memcpy(data, au->data, au->data_size);    *data_len = 8 * au->data_size - au->data_bit_padding;     return 0;} static int vaapi_encode_h264_add_nal(AVCodecContext *avctx,                                     CodedBitstreamFragment *au,                                     void *nal_unit){    H264RawNALUnitHeader *header = nal_unit;    int err;     err = ff_cbs_insert_unit_content(au, -1,                                     header->nal_unit_type, nal_unit, NULL);    if (err < 0) {        av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: "               "type = %d.\n", header->nal_unit_type);        return err;    }     return 0;} static int vaapi_encode_h264_write_sequence_header(AVCodecContext *avctx,                                                   char *data, size_t *data_len){    VAAPIEncodeH264Context *priv = avctx->priv_data;    CodedBitstreamFragment   *au = &priv->current_access_unit;    int err;     if (priv->aud_needed) {        err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);        if (err < 0)            goto fail;        priv->aud_needed = 0;    }     err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_sps);    if (err < 0)        goto fail;     err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_pps);    if (err < 0)        goto fail;     err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au);fail:    ff_cbs_fragment_reset(au);    return err;} static int vaapi_encode_h264_write_slice_header(AVCodecContext *avctx,                                                VAAPIEncodePicture *pic,                                                VAAPIEncodeSlice *slice,                                                char *data, size_t *data_len){    VAAPIEncodeH264Context *priv = avctx->priv_data;    CodedBitstreamFragment   *au = &priv->current_access_unit;    int err;     if (priv->aud_needed) {        err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);        if (err < 0)            goto fail;        priv->aud_needed = 0;    }     err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_slice);    if (err < 0)        goto fail;     err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au);fail:    ff_cbs_fragment_reset(au);    return err;} static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,                                                VAAPIEncodePicture *pic,                                                int index, int *type,                                                char *data, size_t *data_len){    VAAPIEncodeH264Context *priv = avctx->priv_data;    CodedBitstreamFragment   *au = &priv->current_access_unit;    int err;     if (priv->sei_needed) {        if (priv->aud_needed) {            err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);            if (err < 0)                goto fail;            priv->aud_needed = 0;        }         if (priv->sei_needed & SEI_IDENTIFIER) {            err = ff_cbs_sei_add_message(priv->cbc, au, 1,                                         SEI_TYPE_USER_DATA_UNREGISTERED,                                         &priv->sei_identifier, NULL);            if (err < 0)                goto fail;        }        if (priv->sei_needed & SEI_TIMING) {            if (pic->type == PICTURE_TYPE_IDR) {                err = ff_cbs_sei_add_message(priv->cbc, au, 1,                                             SEI_TYPE_BUFFERING_PERIOD,                                             &priv->sei_buffering_period, NULL);                if (err < 0)                    goto fail;            }            err = ff_cbs_sei_add_message(priv->cbc, au, 1,                                         SEI_TYPE_PIC_TIMING,                                         &priv->sei_pic_timing, NULL);            if (err < 0)                goto fail;        }        if (priv->sei_needed & SEI_RECOVERY_POINT) {            err = ff_cbs_sei_add_message(priv->cbc, au, 1,                                         SEI_TYPE_RECOVERY_POINT,                                         &priv->sei_recovery_point, NULL);            if (err < 0)                goto fail;        }         priv->sei_needed = 0;         err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au);        if (err < 0)            goto fail;         ff_cbs_fragment_reset(au);         *type = VAEncPackedHeaderRawData;        return 0; #if !CONFIG_VAAPI_1    } else if (priv->sei_cbr_workaround_needed) {        // Insert a zero-length header using the old SEI type.  This is        // required to avoid triggering broken behaviour on Intel platforms        // in CBR mode where an invalid SEI message is generated by the        // driver and inserted into the stream.        *data_len = 0;        *type = VAEncPackedHeaderH264_SEI;        priv->sei_cbr_workaround_needed = 0;        return 0;#endif     } else {        return AVERROR_EOF;    } fail:    ff_cbs_fragment_reset(au);    return err;} static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx){    VAAPIEncodeContext                *ctx = avctx->priv_data;    VAAPIEncodeH264Context           *priv = avctx->priv_data;    H264RawSPS                        *sps = &priv->raw_sps;    H264RawPPS                        *pps = &priv->raw_pps;    VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;    VAEncPictureParameterBufferH264  *vpic = ctx->codec_picture_params;     memset(sps, 0, sizeof(*sps));    memset(pps, 0, sizeof(*pps));     sps->nal_unit_header.nal_ref_idc   = 3;    sps->nal_unit_header.nal_unit_type = H264_NAL_SPS;     sps->profile_idc = avctx->profile & 0xff;     if (avctx->profile == FF_PROFILE_H264_CONSTRAINED_BASELINE ||        avctx->profile == FF_PROFILE_H264_MAIN)        sps->constraint_set1_flag = 1;     if (avctx->profile == FF_PROFILE_H264_HIGH)        sps->constraint_set3_flag = ctx->gop_size == 1;     if (avctx->profile == FF_PROFILE_H264_MAIN ||        avctx->profile == FF_PROFILE_H264_HIGH) {        sps->constraint_set4_flag = 1;        sps->constraint_set5_flag = ctx->b_per_p == 0;    }     if (ctx->gop_size == 1)        priv->dpb_frames = 0;    else        priv->dpb_frames = 1 + ctx->max_b_depth;     if (avctx->level != FF_LEVEL_UNKNOWN) {        sps->level_idc = avctx->level;    } else {        const H264LevelDescriptor *level;        int framerate;         if (avctx->framerate.num > 0 && avctx->framerate.den > 0)            framerate = avctx->framerate.num / avctx->framerate.den;        else            framerate = 0;         level = ff_h264_guess_level(sps->profile_idc,                                    avctx->bit_rate,                                    framerate,                                    priv->mb_width  * 16,                                    priv->mb_height * 16,                                    priv->dpb_frames);        if (level) {            av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);            if (level->constraint_set3_flag)                sps->constraint_set3_flag = 1;            sps->level_idc = level->level_idc;        } else {            av_log(avctx, AV_LOG_WARNING, "Stream will not conform "                   "to any level: using level 6.2.\n");            sps->level_idc = 62;        }    }     sps->seq_parameter_set_id = 0;    sps->chroma_format_idc    = 1;     sps->log2_max_frame_num_minus4 = 4;    sps->pic_order_cnt_type        = 0;    sps->log2_max_pic_order_cnt_lsb_minus4 = 4;     sps->max_num_ref_frames = priv->dpb_frames;     sps->pic_width_in_mbs_minus1        = priv->mb_width  - 1;    sps->pic_height_in_map_units_minus1 = priv->mb_height - 1;     sps->frame_mbs_only_flag = 1;    sps->direct_8x8_inference_flag = 1;     if (avctx->width  != 16 * priv->mb_width ||        avctx->height != 16 * priv->mb_height) {        sps->frame_cropping_flag = 1;         sps->frame_crop_left_offset   = 0;        sps->frame_crop_right_offset  =            (16 * priv->mb_width - avctx->width) / 2;        sps->frame_crop_top_offset    = 0;        sps->frame_crop_bottom_offset =            (16 * priv->mb_height - avctx->height) / 2;    } else {        sps->frame_cropping_flag = 0;    }     sps->vui_parameters_present_flag = 1;     if (avctx->sample_aspect_ratio.num != 0 &&        avctx->sample_aspect_ratio.den != 0) {        static const AVRational sar_idc[] = {            {   0,  0 },            {   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 }, {   4,  3 }, {   3,  2 }, {   2,  1 },        };        int num, den, i;        av_reduce(&num, &den, avctx->sample_aspect_ratio.num,                  avctx->sample_aspect_ratio.den, 65535);        for (i = 0; i < FF_ARRAY_ELEMS(sar_idc); i++) {            if (num == sar_idc[i].num &&                den == sar_idc[i].den) {                sps->vui.aspect_ratio_idc = i;                break;            }        }        if (i >= FF_ARRAY_ELEMS(sar_idc)) {            sps->vui.aspect_ratio_idc = 255;            sps->vui.sar_width  = num;            sps->vui.sar_height = den;        }        sps->vui.aspect_ratio_info_present_flag = 1;    }     // Unspecified video format, from table E-2.    sps->vui.video_format             = 5;    sps->vui.video_full_range_flag    =        avctx->color_range == AVCOL_RANGE_JPEG;    sps->vui.colour_primaries         = avctx->color_primaries;    sps->vui.transfer_characteristics = avctx->color_trc;    sps->vui.matrix_coefficients      = avctx->colorspace;    if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||        avctx->color_trc       != AVCOL_TRC_UNSPECIFIED ||        avctx->colorspace      != AVCOL_SPC_UNSPECIFIED)        sps->vui.colour_description_present_flag = 1;    if (avctx->color_range     != AVCOL_RANGE_UNSPECIFIED ||        sps->vui.colour_description_present_flag)        sps->vui.video_signal_type_present_flag = 1;     if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) {        sps->vui.chroma_loc_info_present_flag = 1;        sps->vui.chroma_sample_loc_type_top_field    =        sps->vui.chroma_sample_loc_type_bottom_field =            avctx->chroma_sample_location - 1;    }     sps->vui.timing_info_present_flag = 1;    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {        sps->vui.num_units_in_tick = avctx->framerate.den;        sps->vui.time_scale        = 2 * avctx->framerate.num;        sps->vui.fixed_frame_rate_flag = 1;    } else {        sps->vui.num_units_in_tick = avctx->time_base.num;        sps->vui.time_scale        = 2 * avctx->time_base.den;        sps->vui.fixed_frame_rate_flag = 0;    }     if (priv->sei & SEI_TIMING) {        H264RawHRD *hrd = &sps->vui.nal_hrd_parameters;        H264RawSEIBufferingPeriod *bp = &priv->sei_buffering_period;         sps->vui.nal_hrd_parameters_present_flag = 1;         hrd->cpb_cnt_minus1 = 0;         // Try to scale these to a sensible range so that the        // golomb encode of the value is not overlong.        hrd->bit_rate_scale =            av_clip_uintp2(av_log2(ctx->va_bit_rate) - 15 - 6, 4);        hrd->bit_rate_value_minus1[0] =            (ctx->va_bit_rate >> hrd->bit_rate_scale + 6) - 1;         hrd->cpb_size_scale =            av_clip_uintp2(av_log2(ctx->hrd_params.buffer_size) - 15 - 4, 4);        hrd->cpb_size_value_minus1[0] =            (ctx->hrd_params.buffer_size >> hrd->cpb_size_scale + 4) - 1;         // CBR mode as defined for the HRD cannot be achieved without filler        // data, so this flag cannot be set even with VAAPI CBR modes.        hrd->cbr_flag[0] = 0;         hrd->initial_cpb_removal_delay_length_minus1 = 23;        hrd->cpb_removal_delay_length_minus1         = 23;        hrd->dpb_output_delay_length_minus1          = 7;        hrd->time_offset_length                      = 0;         bp->seq_parameter_set_id = sps->seq_parameter_set_id;         // This calculation can easily overflow 32 bits.        bp->nal.initial_cpb_removal_delay[0] = 90000 *            (uint64_t)ctx->hrd_params.initial_buffer_fullness /            ctx->hrd_params.buffer_size;        bp->nal.initial_cpb_removal_delay_offset[0] = 0;    } else {        sps->vui.nal_hrd_parameters_present_flag = 0;        sps->vui.low_delay_hrd_flag = 1 - sps->vui.fixed_frame_rate_flag;    }     sps->vui.bitstream_restriction_flag    = 1;    sps->vui.motion_vectors_over_pic_boundaries_flag = 1;    sps->vui.log2_max_mv_length_horizontal = 15;    sps->vui.log2_max_mv_length_vertical   = 15;    sps->vui.max_num_reorder_frames        = ctx->max_b_depth;    sps->vui.max_dec_frame_buffering       = ctx->max_b_depth + 1;     pps->nal_unit_header.nal_ref_idc = 3;    pps->nal_unit_header.nal_unit_type = H264_NAL_PPS;     pps->pic_parameter_set_id = 0;    pps->seq_parameter_set_id = 0;     pps->entropy_coding_mode_flag =        !(sps->profile_idc == FF_PROFILE_H264_BASELINE ||          sps->profile_idc == FF_PROFILE_H264_EXTENDED ||          sps->profile_idc == FF_PROFILE_H264_CAVLC_444);    if (!priv->coder && pps->entropy_coding_mode_flag)        pps->entropy_coding_mode_flag = 0;     pps->num_ref_idx_l0_default_active_minus1 = 0;    pps->num_ref_idx_l1_default_active_minus1 = 0;

Lines 1–500 of 1346.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t