From 34e051d16850701694410a0e72e0e4ff3a5ec293 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Mon, 9 Jan 2017 23:08:49 +0000 Subject: [PATCH 1/6] vp9: Add bsf to fix reordering in raw streams Takes a raw input stream containing frames with correct timestamps but possibly out of order and inserts additional show-existing-frame packets to correct the ordering. --- doc/bitstream_filters.texi | 5 + libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/vp9_raw_reorder_bsf.c | 411 +++++++++++++++++++++++++++++++ 4 files changed, 418 insertions(+) create mode 100644 libavcodec/vp9_raw_reorder_bsf.c diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index ddadd7653e..64f91f4b54 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -103,4 +103,9 @@ Combine VP9 frames into superframes. Split VP9 superframes into single frames. +@section vp9_raw_reorder + +Given a VP9 stream with correct timestamps but possibly out of order, +insert additional show-existing-frame packets to correct the ordering. + @c man end BITSTREAM FILTERS diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 80c572c7c4..674c8d0445 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -772,6 +772,7 @@ OBJS-$(CONFIG_NOISE_BSF) += noise_bsf.o OBJS-$(CONFIG_NULL_BSF) += null_bsf.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o +OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index d46fdad81b..79ce40f9ed 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -38,6 +38,7 @@ extern const AVBitStreamFilter ff_null_bsf; extern const AVBitStreamFilter ff_text2movsub_bsf; extern const AVBitStreamFilter ff_noise_bsf; extern const AVBitStreamFilter ff_remove_extradata_bsf; +extern const AVBitStreamFilter ff_vp9_raw_reorder_bsf; extern const AVBitStreamFilter ff_vp9_superframe_bsf; extern const AVBitStreamFilter ff_vp9_superframe_split_bsf; diff --git a/libavcodec/vp9_raw_reorder_bsf.c b/libavcodec/vp9_raw_reorder_bsf.c new file mode 100644 index 0000000000..7f00f5f103 --- /dev/null +++ b/libavcodec/vp9_raw_reorder_bsf.c @@ -0,0 +1,411 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/intmath.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "bitstream.h" +#include "bsf.h" +#include "put_bits.h" + +#define FRAME_SLOTS 8 + +typedef struct VP9RawReorderFrame { + AVPacket *packet; + int needs_output; + int needs_display; + + int64_t pts; + int64_t sequence; + unsigned int slots; + + unsigned int profile; + + unsigned int show_existing_frame; + unsigned int frame_to_show; + + unsigned int frame_type; + unsigned int show_frame; + unsigned int refresh_frame_flags; +} VP9RawReorderFrame; + +typedef struct VP9RawReorderContext { + int64_t sequence; + VP9RawReorderFrame *slot[FRAME_SLOTS]; + VP9RawReorderFrame *next_frame; +} VP9RawReorderContext; + +static void vp9_raw_reorder_frame_free(VP9RawReorderFrame **frame) +{ + if (*frame) + av_packet_free(&(*frame)->packet); + av_freep(frame); +} + +static void vp9_raw_reorder_clear_slot(VP9RawReorderContext *ctx, int s) +{ + if (ctx->slot[s]) { + ctx->slot[s]->slots &= ~(1 << s); + if (ctx->slot[s]->slots == 0) + vp9_raw_reorder_frame_free(&ctx->slot[s]); + else + ctx->slot[s] = NULL; + } +} + +static int vp9_raw_reorder_frame_parse(AVBSFContext *bsf, VP9RawReorderFrame *frame) +{ + BitstreamContext bc; + int err; + + unsigned int frame_marker; + unsigned int profile_low_bit, profile_high_bit, reserved_zero; + unsigned int error_resilient_mode; + unsigned int frame_sync_code; + + err = bitstream_init8(&bc, frame->packet->data, frame->packet->size); + if (err) + return err; + + frame_marker = bitstream_read(&bc, 2); + if (frame_marker != 2) { + av_log(bsf, AV_LOG_ERROR, "Invalid frame marker: %u.\n", + frame_marker); + return AVERROR_INVALIDDATA; + } + + profile_low_bit = bitstream_read_bit(&bc); + profile_high_bit = bitstream_read_bit(&bc); + frame->profile = (profile_high_bit << 1) | profile_low_bit; + if (frame->profile == 3) { + reserved_zero = bitstream_read_bit(&bc); + if (reserved_zero != 0) { + av_log(bsf, AV_LOG_ERROR, "Profile reserved_zero bit set: " + "unsupported profile or invalid bitstream.\n"); + return AVERROR_INVALIDDATA; + } + } + + frame->show_existing_frame = bitstream_read_bit(&bc); + if (frame->show_existing_frame) { + frame->frame_to_show = bitstream_read(&bc, 3); + return 0; + } + + frame->frame_type = bitstream_read_bit(&bc); + frame->show_frame = bitstream_read_bit(&bc); + error_resilient_mode = bitstream_read_bit(&bc); + + if (frame->frame_type == 0) { + frame_sync_code = bitstream_read(&bc, 24); + if (frame_sync_code != 0x498342) { + av_log(bsf, AV_LOG_ERROR, "Invalid frame sync code: %06x.\n", + frame_sync_code); + return AVERROR_INVALIDDATA; + } + frame->refresh_frame_flags = 0xff; + } else { + unsigned int intra_only; + + if (frame->show_frame == 0) + intra_only = bitstream_read_bit(&bc); + else + intra_only = 0; + if (error_resilient_mode == 0) { + // reset_frame_context + bitstream_skip(&bc, 2); + } + if (intra_only) { + frame_sync_code = bitstream_read(&bc, 24); + if (frame_sync_code != 0x498342) { + av_log(bsf, AV_LOG_ERROR, "Invalid frame sync code: " + "%06x.\n", frame_sync_code); + return AVERROR_INVALIDDATA; + } + if (frame->profile > 0) { + unsigned int color_space; + if (frame->profile >= 2) { + // ten_or_twelve_bit + bitstream_skip(&bc, 1); + } + color_space = bitstream_read(&bc, 3); + if (color_space != 7 /* CS_RGB */) { + // color_range + bitstream_skip(&bc, 1); + if (frame->profile == 1 || frame->profile == 3) { + // subsampling + bitstream_skip(&bc, 3); + } + } else { + if (frame->profile == 1 || frame->profile == 3) + bitstream_skip(&bc, 1); + } + } + frame->refresh_frame_flags = bitstream_read(&bc, 8); + } else { + frame->refresh_frame_flags = bitstream_read(&bc, 8); + } + } + + return 0; +} + +static int vp9_raw_reorder_make_output(AVBSFContext *bsf, + AVPacket *out, + VP9RawReorderFrame *last_frame) +{ + VP9RawReorderContext *ctx = bsf->priv_data; + VP9RawReorderFrame *next_output = last_frame, + *next_display = last_frame, *frame; + int s, err; + + for (s = 0; s < FRAME_SLOTS; s++) { + frame = ctx->slot[s]; + if (!frame) + continue; + if (frame->needs_output && (!next_output || + frame->sequence < next_output->sequence)) + next_output = frame; + if (frame->needs_display && (!next_display || + frame->pts < next_display->pts)) + next_display = frame; + } + + if (!next_output && !next_display) + return AVERROR_EOF; + + if (!next_display || (next_output && + next_output->sequence < next_display->sequence)) + frame = next_output; + else + frame = next_display; + + if (frame->needs_output && frame->needs_display && + next_output == next_display) { + av_log(bsf, AV_LOG_DEBUG, "Output and display frame " + "%"PRId64" (%"PRId64") in order.\n", + frame->sequence, frame->pts); + + av_packet_move_ref(out, frame->packet); + + frame->needs_output = frame->needs_display = 0; + } else if (frame->needs_output) { + if (frame->needs_display) { + av_log(bsf, AV_LOG_DEBUG, "Output frame %"PRId64" " + "(%"PRId64") for later display.\n", + frame->sequence, frame->pts); + } else { + av_log(bsf, AV_LOG_DEBUG, "Output unshown frame " + "%"PRId64" (%"PRId64") to keep order.\n", + frame->sequence, frame->pts); + } + + av_packet_move_ref(out, frame->packet); + out->pts = out->dts; + + frame->needs_output = 0; + } else { + PutBitContext pb; + + av_assert0(!frame->needs_output && frame->needs_display); + + if (frame->slots == 0) { + av_log(bsf, AV_LOG_ERROR, "Attempting to display frame " + "which is no longer available?\n"); + frame->needs_display = 0; + return AVERROR_INVALIDDATA; + } + + s = ff_ctz(frame->slots); + av_assert0(s < FRAME_SLOTS); + + av_log(bsf, AV_LOG_DEBUG, "Display frame %"PRId64" " + "(%"PRId64") from slot %d.\n", + frame->sequence, frame->pts, s); + + frame->packet = av_packet_alloc(); + if (!frame->packet) + return AVERROR(ENOMEM); + + err = av_new_packet(out, 2); + if (err < 0) + return err; + + init_put_bits(&pb, out->data, 2); + + // frame_marker + put_bits(&pb, 2, 2); + // profile_low_bit + put_bits(&pb, 1, frame->profile & 1); + // profile_high_bit + put_bits(&pb, 1, (frame->profile >> 1) & 1); + if (frame->profile == 3) { + // reserved_zero + put_bits(&pb, 1, 0); + } + // show_existing_frame + put_bits(&pb, 1, 1); + // frame_to_show_map_idx + put_bits(&pb, 3, s); + + while (put_bits_count(&pb) < 16) + put_bits(&pb, 1, 0); + + flush_put_bits(&pb); + out->pts = out->dts = frame->pts; + + frame->needs_display = 0; + } + + return 0; +} + +static int vp9_raw_reorder_filter(AVBSFContext *bsf, AVPacket *out) +{ + VP9RawReorderContext *ctx = bsf->priv_data; + VP9RawReorderFrame *frame; + AVPacket *in; + int err, s; + + if (ctx->next_frame) { + frame = ctx->next_frame; + + } else { + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) { + if (err == AVERROR_EOF) + return vp9_raw_reorder_make_output(bsf, out, NULL); + return err; + } + + if (in->data[in->size - 1] & 0xe0 == 0xc0) { + av_log(bsf, AV_LOG_ERROR, "Input in superframes is not " + "supported.\n"); + av_packet_free(&in); + return AVERROR(ENOSYS); + } + + frame = av_mallocz(sizeof(*frame)); + if (!frame) { + av_packet_free(&in); + return AVERROR(ENOMEM); + } + + frame->packet = in; + frame->pts = in->pts; + frame->sequence = ++ctx->sequence; + err = vp9_raw_reorder_frame_parse(bsf, frame); + if (err) { + av_log(bsf, AV_LOG_ERROR, "Failed to parse input " + "frame: %d.\n", err); + goto fail; + } + + frame->needs_output = 1; + frame->needs_display = frame->pts != AV_NOPTS_VALUE; + + if (frame->show_existing_frame) + av_log(bsf, AV_LOG_DEBUG, "Show frame %"PRId64" " + "(%"PRId64"): show %u.\n", frame->sequence, + frame->pts, frame->frame_to_show); + else + av_log(bsf, AV_LOG_DEBUG, "New frame %"PRId64" " + "(%"PRId64"): type %u show %u refresh %02x.\n", + frame->sequence, frame->pts, frame->frame_type, + frame->show_frame, frame->refresh_frame_flags); + + ctx->next_frame = frame; + } + + for (s = 0; s < FRAME_SLOTS; s++) { + if (!(frame->refresh_frame_flags & (1 << s))) + continue; + if (ctx->slot[s] && ctx->slot[s]->needs_display && + ctx->slot[s]->slots == (1 << s)) { + // We are overwriting this slot, which is last reference + // to the frame previously present in it. In order to be + // a valid stream, that frame must already have been + // displayed before the pts of the current frame. + err = vp9_raw_reorder_make_output(bsf, out, ctx->slot[s]); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to create " + "output overwriting slot %d: %d.\n", + s, err); + // Clear the slot anyway, so we don't end up + // in an infinite loop. + vp9_raw_reorder_clear_slot(ctx, s); + return AVERROR_INVALIDDATA; + } + return 0; + } + vp9_raw_reorder_clear_slot(ctx, s); + } + + for (s = 0; s < FRAME_SLOTS; s++) { + if (!(frame->refresh_frame_flags & (1 << s))) + continue; + ctx->slot[s] = frame; + } + frame->slots = frame->refresh_frame_flags; + + if (!frame->refresh_frame_flags) { + err = vp9_raw_reorder_make_output(bsf, out, frame); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to create output " + "for transient frame.\n"); + ctx->next_frame = NULL; + return AVERROR_INVALIDDATA; + } + if (!frame->needs_display) { + vp9_raw_reorder_frame_free(&frame); + ctx->next_frame = NULL; + } + return 0; + } + + ctx->next_frame = NULL; + return AVERROR(EAGAIN); + +fail: + vp9_raw_reorder_frame_free(&frame); + return err; +} + +static void vp9_raw_reorder_close(AVBSFContext *bsf) +{ + VP9RawReorderContext *ctx = bsf->priv_data; + int s; + + for (s = 0; s < FRAME_SLOTS; s++) + vp9_raw_reorder_clear_slot(ctx, s); +} + +static const enum AVCodecID vp9_raw_reorder_codec_ids[] = { + AV_CODEC_ID_VP9, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_vp9_raw_reorder_bsf = { + .name = "vp9_raw_reorder", + .priv_data_size = sizeof(VP9RawReorderContext), + .close = &vp9_raw_reorder_close, + .filter = &vp9_raw_reorder_filter, + .codec_ids = vp9_raw_reorder_codec_ids, +}; From 0fd91e4bfc00a6609b59d1ce3a9f152184e62601 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Mon, 28 Nov 2016 23:54:57 +0000 Subject: [PATCH 2/6] vaapi_encode: Add VP9 support --- configure | 3 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/vaapi_encode_vp9.c | 311 ++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- 5 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 libavcodec/vaapi_encode_vp9.c diff --git a/configure b/configure index f1488cf494..30eba73c21 100755 --- a/configure +++ b/configure @@ -2273,6 +2273,8 @@ vp8_qsv_decoder_deps="libmfx" vp8_qsv_decoder_select="qsvdec vp8_qsv_hwaccel vp8_parser" vp8_vaapi_encoder_deps="VAEncPictureParameterBufferVP8" vp8_vaapi_encoder_select="vaapi_encode" +vp9_vaapi_encoder_deps="VAEncPictureParameterBufferVP9" +vp9_vaapi_encoder_select="vaapi_encode" nvenc_h264_encoder_select="h264_nvenc_encoder" nvenc_hevc_encoder_select="hevc_nvenc_encoder" @@ -4666,6 +4668,7 @@ check_type "va/va.h va/va_enc_hevc.h" "VAEncPictureParameterBufferHEVC" check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG" check_type "va/va.h va/va_enc_mpeg2.h" "VAEncPictureParameterBufferMPEG2" check_type "va/va.h va/va_enc_vp8.h" "VAEncPictureParameterBufferVP8" +check_type "va/va.h va/va_enc_vp9.h" "VAEncPictureParameterBufferVP9" check_type "vdpau/vdpau.h" "VdpPictureInfoHEVC" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 674c8d0445..ec62f514a7 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -485,6 +485,7 @@ OBJS-$(CONFIG_VP8_QSV_DECODER) += qsvdec_other.o OBJS-$(CONFIG_VP8_VAAPI_ENCODER) += vaapi_encode_vp8.o OBJS-$(CONFIG_VP9_DECODER) += vp9.o vp9data.o vp9dsp.o \ vp9block.o vp9prob.o vp9mvs.o vp56rac.o +OBJS-$(CONFIG_VP9_VAAPI_ENCODER) += vaapi_encode_vp9.o OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o OBJS-$(CONFIG_WEBP_DECODER) += webp.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 556bdb43f3..36f839c852 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -511,6 +511,7 @@ void avcodec_register_all(void) REGISTER_ENCODER(NVENC_HEVC, nvenc_hevc); #endif REGISTER_ENCODER(VP8_VAAPI, vp8_vaapi); + REGISTER_ENCODER(VP9_VAAPI, vp9_vaapi); /* parsers */ REGISTER_PARSER(AAC, aac); diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c new file mode 100644 index 0000000000..5d3eec1775 --- /dev/null +++ b/libavcodec/vaapi_encode_vp9.c @@ -0,0 +1,311 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/common.h" +#include "libavutil/internal.h" +#include "libavutil/opt.h" +#include "libavutil/pixfmt.h" + +#include "avcodec.h" +#include "internal.h" +#include "vaapi_encode.h" + + +typedef struct VAAPIEncodeVP9Context { + int q_idx_idr; + int q_idx_p; + int q_idx_b; + + // Reference direction for B-like frames: + // 0 - most recent P/IDR frame is last. + // 1 - most recent P frame is golden. + int last_ref_dir; +} VAAPIEncodeVP9Context; + +typedef struct VAAPIEncodeVP9Options { + int loop_filter_level; + int loop_filter_sharpness; +} VAAPIEncodeVP9Options; + + +#define vseq_var(name) vseq->name, name +#define vseq_field(name) vseq->seq_fields.bits.name, name +#define vpic_var(name) vpic->name, name +#define vpic_field(name) vpic->pic_fields.bits.name, name + + +static int vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAEncSequenceParameterBufferVP9 *vseq = ctx->codec_sequence_params; + VAEncPictureParameterBufferVP9 *vpic = ctx->codec_picture_params; + + vseq->max_frame_width = avctx->width; + vseq->max_frame_height = avctx->height; + + vseq->kf_auto = 0; + + if (!(ctx->va_rc_mode & VA_RC_CQP)) { + vseq->bits_per_second = avctx->bit_rate; + vseq->intra_period = avctx->gop_size; + } + + vpic->frame_width_src = avctx->width; + vpic->frame_height_src = avctx->height; + vpic->frame_width_dst = avctx->width; + vpic->frame_height_dst = avctx->height; + + return 0; +} + +static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx, + VAAPIEncodePicture *pic) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAEncPictureParameterBufferVP9 *vpic = pic->codec_picture_params; + VAAPIEncodeVP9Context *priv = ctx->priv_data; + VAAPIEncodeVP9Options *opt = ctx->codec_options; + int i; + + vpic->reconstructed_frame = pic->recon_surface; + vpic->coded_buf = pic->output_buffer; + + switch (pic->type) { + case PICTURE_TYPE_IDR: + av_assert0(pic->nb_refs == 0); + vpic->ref_flags.bits.force_kf = 1; + vpic->refresh_frame_flags = 0x01; + priv->last_ref_dir = 0; + break; + case PICTURE_TYPE_P: + av_assert0(pic->nb_refs == 1); + if (avctx->max_b_frames > 0) { + if (priv->last_ref_dir) { + vpic->ref_flags.bits.ref_frame_ctrl_l0 = 2; + vpic->ref_flags.bits.ref_gf_idx = 1; + vpic->ref_flags.bits.ref_gf_sign_bias = 1; + vpic->refresh_frame_flags = 0x01; + } else { + vpic->ref_flags.bits.ref_frame_ctrl_l0 = 1; + vpic->ref_flags.bits.ref_last_idx = 0; + vpic->ref_flags.bits.ref_last_sign_bias = 1; + vpic->refresh_frame_flags = 0x02; + } + } else { + vpic->ref_flags.bits.ref_frame_ctrl_l0 = 1; + vpic->ref_flags.bits.ref_last_idx = 0; + vpic->ref_flags.bits.ref_last_sign_bias = 1; + vpic->refresh_frame_flags = 0x01; + } + break; + case PICTURE_TYPE_B: + av_assert0(pic->nb_refs == 2); + if (priv->last_ref_dir) { + vpic->ref_flags.bits.ref_frame_ctrl_l0 = 1; + vpic->ref_flags.bits.ref_frame_ctrl_l1 = 2; + vpic->ref_flags.bits.ref_last_idx = 0; + vpic->ref_flags.bits.ref_last_sign_bias = 1; + vpic->ref_flags.bits.ref_gf_idx = 1; + vpic->ref_flags.bits.ref_gf_sign_bias = 0; + } else { + vpic->ref_flags.bits.ref_frame_ctrl_l0 = 2; + vpic->ref_flags.bits.ref_frame_ctrl_l1 = 1; + vpic->ref_flags.bits.ref_last_idx = 0; + vpic->ref_flags.bits.ref_last_sign_bias = 0; + vpic->ref_flags.bits.ref_gf_idx = 1; + vpic->ref_flags.bits.ref_gf_sign_bias = 1; + } + vpic->refresh_frame_flags = 0x00; + break; + default: + av_assert0(0 && "invalid picture type"); + } + + for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) + vpic->reference_frames[i] = VA_INVALID_SURFACE; + if (pic->type == PICTURE_TYPE_P) { + av_assert0(pic->refs[0]); + vpic->reference_frames[priv->last_ref_dir] = + pic->refs[0]->recon_surface; + } else if (pic->type == PICTURE_TYPE_B) { + av_assert0(pic->refs[0] && pic->refs[1]); + vpic->reference_frames[!priv->last_ref_dir] = + pic->refs[0]->recon_surface; + vpic->reference_frames[priv->last_ref_dir] = + pic->refs[1]->recon_surface; + } + + vpic->pic_flags.bits.frame_type = (pic->type != PICTURE_TYPE_IDR); + vpic->pic_flags.bits.show_frame = pic->display_order <= pic->encode_order; + + if (pic->type == PICTURE_TYPE_IDR) + vpic->luma_ac_qindex = priv->q_idx_idr; + else if (pic->type == PICTURE_TYPE_P) + vpic->luma_ac_qindex = priv->q_idx_p; + else + vpic->luma_ac_qindex = priv->q_idx_b; + vpic->luma_dc_qindex_delta = 0; + vpic->chroma_ac_qindex_delta = 0; + vpic->chroma_dc_qindex_delta = 0; + + vpic->filter_level = opt->loop_filter_level; + vpic->sharpness_level = opt->loop_filter_sharpness; + + if (avctx->max_b_frames > 0 && pic->type == PICTURE_TYPE_P) + priv->last_ref_dir = !priv->last_ref_dir; + + return 0; +} + +static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeVP9Context *priv = ctx->priv_data; + + priv->q_idx_p = av_clip(avctx->global_quality, 0, 255); + if (avctx->i_quant_factor > 0.0) + priv->q_idx_idr = av_clip((avctx->global_quality * + avctx->i_quant_factor + + avctx->i_quant_offset) + 0.5, + 0, 255); + else + priv->q_idx_idr = priv->q_idx_p; + if (avctx->b_quant_factor > 0.0) + priv->q_idx_b = av_clip((avctx->global_quality * + avctx->b_quant_factor + + avctx->b_quant_offset) + 0.5, + 0, 255); + else + priv->q_idx_b = priv->q_idx_p; + + return 0; +} + +static const VAAPIEncodeType vaapi_encode_type_vp9 = { + .configure = &vaapi_encode_vp9_configure, + + .priv_data_size = sizeof(VAAPIEncodeVP9Context), + + .sequence_params_size = sizeof(VAEncSequenceParameterBufferVP9), + .init_sequence_params = &vaapi_encode_vp9_init_sequence_params, + + .picture_params_size = sizeof(VAEncPictureParameterBufferVP9), + .init_picture_params = &vaapi_encode_vp9_init_picture_params, +}; + +static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + + ctx->codec = &vaapi_encode_type_vp9; + + switch (avctx->profile) { + case FF_PROFILE_VP9_0: + case FF_PROFILE_UNKNOWN: + ctx->va_profile = VAProfileVP9Profile0; + ctx->va_rt_format = VA_RT_FORMAT_YUV420; + break; + case FF_PROFILE_VP9_1: + av_log(avctx, AV_LOG_ERROR, "VP9 profile 1 is not " + "supported.\n"); + return AVERROR_PATCHWELCOME; + case FF_PROFILE_VP9_2: + ctx->va_profile = VAProfileVP9Profile2; + ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP; + break; + case FF_PROFILE_VP9_3: + av_log(avctx, AV_LOG_ERROR, "VP9 profile 3 is not " + "supported.\n"); + return AVERROR_PATCHWELCOME; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown VP9 profile %d.\n", + avctx->profile); + return AVERROR(EINVAL); + } + ctx->va_entrypoint = VAEntrypointEncSlice; + + if (avctx->flags & AV_CODEC_FLAG_QSCALE) { + ctx->va_rc_mode = VA_RC_CQP; + } else if (avctx->bit_rate > 0) { + if (avctx->bit_rate == avctx->rc_max_rate) + ctx->va_rc_mode = VA_RC_CBR; + else + ctx->va_rc_mode = VA_RC_VBR; + } else { + ctx->va_rc_mode = VA_RC_CQP; + } + + // Packed headers are not currently supported. + ctx->va_packed_headers = 0; + + // Surfaces must be aligned to superblock boundaries. + ctx->surface_width = FFALIGN(avctx->width, 64); + ctx->surface_height = FFALIGN(avctx->height, 64); + + return ff_vaapi_encode_init(avctx); +} + +#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ + offsetof(VAAPIEncodeVP9Options, x)) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) +static const AVOption vaapi_encode_vp9_options[] = { + { "loop_filter_level", "Loop filter level", + OFFSET(loop_filter_level), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 63, FLAGS }, + { "loop_filter_sharpness", "Loop filter sharpness", + OFFSET(loop_filter_sharpness), AV_OPT_TYPE_INT, { .i64 = 4 }, 0, 15, FLAGS }, + { NULL }, +}; + +static const AVCodecDefault vaapi_encode_vp9_defaults[] = { + { "profile", "0" }, + { "b", "0" }, + { "bf", "0" }, + { "g", "250" }, + { "global_quality", "100" }, + { NULL }, +}; + +static const AVClass vaapi_encode_vp9_class = { + .class_name = "vp9_vaapi", + .item_name = av_default_item_name, + .option = vaapi_encode_vp9_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_vp9_vaapi_encoder = { + .name = "vp9_vaapi", + .long_name = NULL_IF_CONFIG_SMALL("VP9 (VAAPI)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_VP9, + .priv_data_size = (sizeof(VAAPIEncodeContext) + + sizeof(VAAPIEncodeVP9Options)), + .init = &vaapi_encode_vp9_init, + .encode2 = &ff_vaapi_encode2, + .close = &ff_vaapi_encode_close, + .priv_class = &vaapi_encode_vp9_class, + .capabilities = AV_CODEC_CAP_DELAY, + .defaults = vaapi_encode_vp9_defaults, + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_VAAPI, + AV_PIX_FMT_NONE, + }, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 4fb0a97a27..8943ef3d0f 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 0 +#define LIBAVCODEC_VERSION_MINOR 1 #define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ From 41dda860870fb1566b17f6b0b61922f0ef89be47 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 19 Mar 2017 17:30:52 +0000 Subject: [PATCH 3/6] doc: Add VAAPI encoders --- doc/encoders.texi | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/doc/encoders.texi b/doc/encoders.texi index 41b8398127..7cebe39c18 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -922,4 +922,100 @@ encoder use CAVLC instead of CABAC. @end itemize +@section VAAPI encoders + +Wrappers for hardware encoders accessible via VAAPI. + +These encoders only accept input in VAAPI hardware surfaces. If you have input +in software frames, use the @option{hwupload} filter to upload them to the GPU. + +The following standard libavcodec options are used: +@itemize +@item +@option{g} / @option{gop_size} +@item +@option{bf} / @option{max_b_frames} +@item +@option{profile} +@item +@option{level} +@item +@option{b} / @option{bit_rate} +@item +@option{maxrate} / @option{rc_max_rate} +@item +@option{bufsize} / @option{rc_buffer_size} +@item +@option{rc_init_occupancy} / @option{rc_initial_buffer_occupancy} +@item +@option{q} / @option{global_quality} +@item +@option{qmin} +(only: @option{qmax} is not supported) +@item +@option{i_qfactor} / @option{i_quant_factor} +@item +@option{i_qoffset} / @option{i_quant_offset} +@item +@option{b_qfactor} / @option{b_quant_factor} +@item +@option{b_qoffset} / @option{b_quant_offset} +@end itemize + +@table @option + +@item vaapi_h264 +@option{profile} sets the value of @emph{profile_idc} and the @emph{constraint_set*_flag}s. +@option{level} sets the value of @emph{level_idc}. + +@table @option +@item quality +Set the local encoding quality/speed tradeoff (range 1-8, higher values are faster; not all +systems implement all levels). +@item low_power +Use low-power encoding mode. +@end table + +@item vaapi_hevc +@option{profile} and @option{level} set the values of +@emph{general_profile_idc} and @emph{general_level_idc} respectively. + +@item vaapi_mjpeg +Always encodes using the standard quantisation and huffman tables - +@option{global_quality} scales the standard quantisation table (range 1-100). + +@item vaapi_mpeg2 +@option{profile} and @option{level} set the value of @emph{profile_and_level_indication}. + +No rate control is supported. + +@item vaapi_vp8 +B-frames are not supported. + +@option{global_quality} sets the @emph{q_idx} used for non-key frames (range 0-127). + +@table @option +@item loop_filter_level +@item loop_filter_sharpness +Manually set the loop filter parameters. +@end table + +@item vaapi_vp9 +@option{global_quality} sets the @emph{q_idx} used for P-frames (range 0-255). + +@table @option +@item loop_filter_level +@item loop_filter_sharpness +Manually set the loop filter parameters. +@end table + +B-frames are supported, but the output stream is always in encode order rather than display +order. If B-frames are enabled, it may be necessary to use the @option{vp9_raw_reorder} +bitstream filter to modify the output stream to display frames in the correct order. + +Only normal frames are produced - the @option{vp9_superframe} bitstream filter may be +required to produce a stream usable with all decoders. + +@end table + @c man end VIDEO ENCODERS From 4936a48b1e6fc2147599541f8b25f43a8a9d1f16 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:35 +0000 Subject: [PATCH 4/6] qsv: Add ability to create a session from a device --- libavcodec/qsv.c | 49 +++++++++++++++++++++++++++------------ libavcodec/qsv_internal.h | 9 ++++--- libavcodec/qsvdec.c | 6 ++--- libavcodec/qsvenc.c | 6 ++--- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c index 735e1536f8..30d612f0e1 100644 --- a/libavcodec/qsv.c +++ b/libavcodec/qsv.c @@ -537,27 +537,16 @@ static mfxStatus qsv_frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) return MFX_ERR_NONE; } -int ff_qsv_init_session_hwcontext(AVCodecContext *avctx, mfxSession *psession, - QSVFramesContext *qsv_frames_ctx, - const char *load_plugins, int opaque) +int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, + AVBufferRef *device_ref, const char *load_plugins) { static const mfxHandleType handle_types[] = { MFX_HANDLE_VA_DISPLAY, MFX_HANDLE_D3D9_DEVICE_MANAGER, MFX_HANDLE_D3D11_DEVICE, }; - mfxFrameAllocator frame_allocator = { - .pthis = qsv_frames_ctx, - .Alloc = qsv_frame_alloc, - .Lock = qsv_frame_lock, - .Unlock = qsv_frame_unlock, - .GetHDL = qsv_frame_get_hdl, - .Free = qsv_frame_free, - }; - - AVHWFramesContext *frames_ctx = (AVHWFramesContext*)qsv_frames_ctx->hw_frames_ctx->data; - AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; - AVQSVDeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)device_ref->data; + AVQSVDeviceContext *device_hwctx = device_ctx->hwctx; mfxSession parent_session = device_hwctx->session; mfxSession session; @@ -607,6 +596,36 @@ int ff_qsv_init_session_hwcontext(AVCodecContext *avctx, mfxSession *psession, return ret; } + *psession = session; + return 0; +} + +int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *psession, + QSVFramesContext *qsv_frames_ctx, + const char *load_plugins, int opaque) +{ + mfxFrameAllocator frame_allocator = { + .pthis = qsv_frames_ctx, + .Alloc = qsv_frame_alloc, + .Lock = qsv_frame_lock, + .Unlock = qsv_frame_unlock, + .GetHDL = qsv_frame_get_hdl, + .Free = qsv_frame_free, + }; + + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)qsv_frames_ctx->hw_frames_ctx->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + + mfxSession session; + mfxStatus err; + + int ret; + + ret = ff_qsv_init_session_device(avctx, &session, + frames_ctx->device_ref, load_plugins); + if (ret < 0) + return ret; + if (!opaque) { qsv_frames_ctx->logctx = avctx; diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h index 7ac347ea21..975c8de441 100644 --- a/libavcodec/qsv_internal.h +++ b/libavcodec/qsv_internal.h @@ -87,9 +87,12 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc); int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, const char *load_plugins); -int ff_qsv_init_session_hwcontext(AVCodecContext *avctx, mfxSession *session, - QSVFramesContext *qsv_frames_ctx, - const char *load_plugins, int opaque); +int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, + AVBufferRef *device_ref, const char *load_plugins); + +int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *session, + QSVFramesContext *qsv_frames_ctx, + const char *load_plugins, int opaque); int ff_qsv_find_surface_idx(QSVFramesContext *ctx, QSVFrame *frame); diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 0cbe5094c7..96526a100a 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -59,9 +59,9 @@ static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession ses if (!q->frames_ctx.hw_frames_ctx) return AVERROR(ENOMEM); - ret = ff_qsv_init_session_hwcontext(avctx, &q->internal_session, - &q->frames_ctx, q->load_plugins, - q->iopattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY); + ret = ff_qsv_init_session_frames(avctx, &q->internal_session, + &q->frames_ctx, q->load_plugins, + q->iopattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY); if (ret < 0) { av_buffer_unref(&q->frames_ctx.hw_frames_ctx); return ret; diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index 85af146ad8..b2f2b4db71 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -684,9 +684,9 @@ static int qsvenc_init_session(AVCodecContext *avctx, QSVEncContext *q) if (!q->frames_ctx.hw_frames_ctx) return AVERROR(ENOMEM); - ret = ff_qsv_init_session_hwcontext(avctx, &q->internal_session, - &q->frames_ctx, q->load_plugins, - q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY); + ret = ff_qsv_init_session_frames(avctx, &q->internal_session, + &q->frames_ctx, q->load_plugins, + q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY); if (ret < 0) { av_buffer_unref(&q->frames_ctx.hw_frames_ctx); return ret; From 8848ba0bd6b035af77d4f13aa0d8aaaad9806fe8 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:36 +0000 Subject: [PATCH 5/6] qsvdec: Allow use of hw_device_ctx to make the internal session --- libavcodec/qsvdec.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 96526a100a..2c90436a17 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -42,7 +42,7 @@ #include "qsvdec.h" static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession session, - AVBufferRef *hw_frames_ref) + AVBufferRef *hw_frames_ref, AVBufferRef *hw_device_ref) { int ret; @@ -67,6 +67,18 @@ static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession ses return ret; } + q->session = q->internal_session; + } else if (hw_device_ref) { + if (q->internal_session) { + MFXClose(q->internal_session); + q->internal_session = NULL; + } + + ret = ff_qsv_init_session_device(avctx, &q->internal_session, + hw_device_ref, q->load_plugins); + if (ret < 0) + return ret; + q->session = q->internal_session; } else { if (!q->internal_session) { @@ -133,7 +145,7 @@ static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q) iopattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; q->iopattern = iopattern; - ret = qsv_init_session(avctx, q, session, avctx->hw_frames_ctx); + ret = qsv_init_session(avctx, q, session, avctx->hw_frames_ctx, avctx->hw_device_ctx); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error initializing an MFX session\n"); return ret; From 3d197514e613ccd9eab43180c0a7c8b09a307606 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:37 +0000 Subject: [PATCH 6/6] qsvenc: Allow use of hw_device_ctx to make the internal session --- libavcodec/qsvenc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index b2f2b4db71..bd8c24321e 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -692,6 +692,13 @@ static int qsvenc_init_session(AVCodecContext *avctx, QSVEncContext *q) return ret; } + q->session = q->internal_session; + } else if (avctx->hw_device_ctx) { + ret = ff_qsv_init_session_device(avctx, &q->internal_session, + avctx->hw_device_ctx, q->load_plugins); + if (ret < 0) + return ret; + q->session = q->internal_session; } else { ret = ff_qsv_init_internal_session(avctx, &q->internal_session,