diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2015-02-19 22:52:29 +0300 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2015-02-19 23:12:04 +0300 |
commit | 841e9f431cf4b1be0b5c3932c289d405f34dfcc2 (patch) | |
tree | 6f7c1159a0ceff4a7287b6f9725dd0fbd4fafc96 | |
parent | 560eb7179a7c46ba277c9c120840816869a47a70 (diff) | |
parent | 4e08c821106fc1d6d358864abf0d8488b12f38c6 (diff) |
Merge commit '4e08c821106fc1d6d358864abf0d8488b12f38c6'
* commit '4e08c821106fc1d6d358864abf0d8488b12f38c6':
lavc: add an Intel libmfx-based H.264 decoder
Conflicts:
configure
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/version.h
Merged-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | Changelog | 1 | ||||
-rwxr-xr-x | configure | 6 | ||||
-rw-r--r-- | libavcodec/Makefile | 5 | ||||
-rw-r--r-- | libavcodec/allcodecs.c | 2 | ||||
-rw-r--r-- | libavcodec/qsv.c | 362 | ||||
-rw-r--r-- | libavcodec/qsv.h | 41 | ||||
-rw-r--r-- | libavcodec/qsv_api.c | 28 | ||||
-rw-r--r-- | libavcodec/qsv_h264.c | 313 | ||||
-rw-r--r-- | libavcodec/qsv_internal.h | 86 | ||||
-rw-r--r-- | libavcodec/version.h | 2 |
10 files changed, 845 insertions, 1 deletions
@@ -30,6 +30,7 @@ version <next>: - DV RTP payload format (RFC 6469) depacketizer - DXVA2-accelerated HEVC decoding - AAC ELD 480 decoding +- Intel QSV-accelerated H.264 decoding version 2.5: - HEVC/H.265 RTP payload format (draft v6) packetizer @@ -219,6 +219,7 @@ External library support: --enable-libgsm enable GSM de/encoding via libgsm [no] --enable-libiec61883 enable iec61883 via libiec61883 [no] --enable-libilbc enable iLBC de/encoding via libilbc [no] + --enable-libmfx enable HW acceleration through libmfx --enable-libmodplug enable ModPlug via libmodplug [no] --enable-libmp3lame enable MP3 encoding via libmp3lame [no] --enable-libnut enable NUT (de)muxing via libnut, @@ -1362,6 +1363,7 @@ EXTERNAL_LIBRARY_LIST=" libgsm libiec61883 libilbc + libmfx libmodplug libmp3lame libnut @@ -1479,6 +1481,7 @@ SUBSYSTEM_LIST=" mdct pixelutils network + qsv rdft " @@ -2139,6 +2142,8 @@ h263i_decoder_select="h263_decoder" h263p_encoder_select="h263_encoder" h264_decoder_select="cabac golomb h264chroma h264dsp h264pred h264qpel startcode videodsp" h264_decoder_suggest="error_resilience" +h264_qsv_decoder_deps="libmfx" +h264_qsv_decoder_select="h264_mp4toannexb_bsf h264_parser qsv h264_qsv_hwaccel" hevc_decoder_select="bswapdsp cabac golomb videodsp" huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp" huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llviddsp" @@ -4900,6 +4905,7 @@ enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do check_lib "${gsm_hdr}" gsm_create -lgsm && break; done || die "ERROR: libgsm not found"; } enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc +enabled libmfx && require_pkg_config libmfx "mfx/mfxvideo.h" MFXInit enabled libmodplug && require_pkg_config libmodplug libmodplug/modplug.h ModPlug_Load enabled libmp3lame && require "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame enabled libnut && require libnut libnut.h nut_demuxer_init -lnut diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3b9cd2e4fa..50700b6512 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -7,6 +7,7 @@ HEADERS = avcodec.h \ dv_profile.h \ dxva2.h \ old_codec_ids.h \ + qsv.h \ vaapi.h \ vda.h \ vdpau.h \ @@ -28,6 +29,7 @@ OBJS = allcodecs.o \ mathtables.o \ options.o \ parser.o \ + qsv_api.o \ raw.o \ resample.o \ resample2.o \ @@ -89,6 +91,7 @@ OBJS-$(CONFIG_MPEGVIDEOENC) += mpegvideo_enc.o mpeg12data.o \ mpegvideoencdsp.o OBJS-$(CONFIG_PIXBLOCKDSP) += pixblockdsp.o OBJS-$(CONFIG_QPELDSP) += qpeldsp.o +OBJS-$(CONFIG_QSV) += qsv.o OBJS-$(CONFIG_RANGECODER) += rangecoder.o RDFT-OBJS-$(CONFIG_HARDCODED_TABLES) += sin_tables.o OBJS-$(CONFIG_RDFT) += rdft.o $(RDFT-OBJS-yes) @@ -263,6 +266,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264.o h264_cabac.o h264_cavlc.o \ h264_mb.o h264_picture.o h264_ps.o \ h264_refs.o h264_sei.o h264_slice.o OBJS-$(CONFIG_H264_VDA_DECODER) += vda_h264_dec.o +OBJS-$(CONFIG_H264_QSV_DECODER) += qsv_h264.o OBJS-$(CONFIG_HEVC_DECODER) += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \ hevc_cabac.o hevc_refs.o hevcpred.o \ hevcdsp.o hevc_filter.o @@ -856,6 +860,7 @@ SKIPHEADERS += %_tablegen.h \ SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER) += libschroedinger.h SKIPHEADERS-$(CONFIG_LIBUTVIDEO) += libutvideo.h +SKIPHEADERS-$(CONFIG_QSV) += qsv_internal.h SKIPHEADERS-$(CONFIG_XVMC) += xvmc.h SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_internal.h SKIPHEADERS-$(CONFIG_VDA) += vda.h vda_internal.h diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 59fe6811fc..ef3b270dff 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -77,6 +77,7 @@ void avcodec_register_all(void) REGISTER_HWACCEL(H263_VAAPI, h263_vaapi); REGISTER_HWACCEL(H263_VDPAU, h263_vdpau); REGISTER_HWACCEL(H264_DXVA2, h264_dxva2); + REGISTER_HWACCEL(H264_QSV, h264_qsv); REGISTER_HWACCEL(H264_VAAPI, h264_vaapi); REGISTER_HWACCEL(H264_VDA, h264_vda); REGISTER_HWACCEL(H264_VDA_OLD, h264_vda_old); @@ -170,6 +171,7 @@ void avcodec_register_all(void) REGISTER_ENCDEC (H263P, h263p); REGISTER_DECODER(H264, h264); REGISTER_DECODER(H264_CRYSTALHD, h264_crystalhd); + REGISTER_DECODER(H264_QSV, h264_qsv); REGISTER_DECODER(H264_VDA, h264_vda); REGISTER_DECODER(H264_VDPAU, h264_vdpau); REGISTER_DECODER(HEVC, hevc); diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c new file mode 100644 index 0000000000..2070d9986a --- /dev/null +++ b/libavcodec/qsv.c @@ -0,0 +1,362 @@ +/* + * Intel MediaSDK QSV codec-independent code + * + * copyright (c) 2013 Luca Barbato + * copyright (c) 2015 Anton Khirnov <anton@khirnov.net> + * + * 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 <sys/types.h> + +#include <mfx/mfxvideo.h> + +#include "libavutil/common.h" +#include "libavutil/mem.h" +#include "libavutil/log.h" +#include "libavutil/pixfmt.h" +#include "libavutil/time.h" + +#include "avcodec.h" +#include "internal.h" +#include "qsv_internal.h" + +int ff_qsv_error(int mfx_err) +{ + switch (mfx_err) { + case MFX_ERR_NONE: + return 0; + case MFX_ERR_MEMORY_ALLOC: + case MFX_ERR_NOT_ENOUGH_BUFFER: + return AVERROR(ENOMEM); + case MFX_ERR_INVALID_HANDLE: + return AVERROR(EINVAL); + case MFX_ERR_DEVICE_FAILED: + case MFX_ERR_DEVICE_LOST: + case MFX_ERR_LOCK_MEMORY: + return AVERROR(EIO); + case MFX_ERR_NULL_PTR: + case MFX_ERR_UNDEFINED_BEHAVIOR: + case MFX_ERR_NOT_INITIALIZED: + return AVERROR_BUG; + case MFX_ERR_UNSUPPORTED: + case MFX_ERR_NOT_FOUND: + return AVERROR(ENOSYS); + case MFX_ERR_MORE_DATA: + case MFX_ERR_MORE_SURFACE: + case MFX_ERR_MORE_BITSTREAM: + return AVERROR(EAGAIN); + case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: + case MFX_ERR_INVALID_VIDEO_PARAM: + return AVERROR(EINVAL); + case MFX_ERR_ABORTED: + case MFX_ERR_UNKNOWN: + default: + return AVERROR_UNKNOWN; + } +} + +int ff_qsv_map_pixfmt(enum AVPixelFormat format) +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + return AV_PIX_FMT_NV12; + default: + return AVERROR(ENOSYS); + } +} + +static int codec_id_to_mfx(enum AVCodecID codec_id) +{ + switch (codec_id) { + case AV_CODEC_ID_H264: + return MFX_CODEC_AVC; + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + return MFX_CODEC_MPEG2; + case AV_CODEC_ID_VC1: + return MFX_CODEC_VC1; + default: + break; + } + + return AVERROR(ENOSYS); +} + +static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession session) +{ + if (!session) { + if (!q->internal_session) { + mfxIMPL impl = MFX_IMPL_AUTO_ANY; + mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; + + const char *desc; + int ret; + + ret = MFXInit(impl, &ver, &q->internal_session); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error initializing an internal MFX session\n"); + return ff_qsv_error(ret); + } + + MFXQueryIMPL(q->internal_session, &impl); + + if (impl & MFX_IMPL_SOFTWARE) + desc = "software"; + else if (impl & MFX_IMPL_HARDWARE) + desc = "hardware accelerated"; + else + desc = "unknown"; + + av_log(avctx, AV_LOG_VERBOSE, + "Initialized an internal MFX session using %s implementation\n", + desc); + } + + q->session = q->internal_session; + } else { + q->session = session; + } + + /* make sure the decoder is uninitialized */ + MFXVideoDECODE_Close(q->session); + + return 0; +} + +int ff_qsv_init(AVCodecContext *avctx, QSVContext *q, mfxSession session) +{ + mfxVideoParam param = { { 0 } }; + int ret; + + ret = qsv_init_session(avctx, q, session); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error initializing an MFX session\n"); + return ret; + } + + + ret = codec_id_to_mfx(avctx->codec_id); + if (ret < 0) + return ret; + + param.mfx.CodecId = ret; + param.mfx.CodecProfile = avctx->profile; + param.mfx.CodecLevel = avctx->level; + + param.mfx.FrameInfo.BitDepthLuma = 8; + param.mfx.FrameInfo.BitDepthChroma = 8; + param.mfx.FrameInfo.Shift = 0; + param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + param.mfx.FrameInfo.Width = avctx->coded_width; + param.mfx.FrameInfo.Height = avctx->coded_height; + param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + + param.IOPattern = q->iopattern; + param.AsyncDepth = q->async_depth; + param.ExtParam = q->ext_buffers; + param.NumExtParam = q->nb_ext_buffers; + + ret = MFXVideoDECODE_Init(q->session, ¶m); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error initializing the MFX video decoder\n"); + return ff_qsv_error(ret); + } + + return 0; +} + +static int alloc_frame(AVCodecContext *avctx, QSVFrame *frame) +{ + int ret; + + ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return ret; + + if (frame->frame->format == AV_PIX_FMT_QSV) { + frame->surface = (mfxFrameSurface1*)frame->frame->data[3]; + } else { + frame->surface_internal.Info.BitDepthLuma = 8; + frame->surface_internal.Info.BitDepthChroma = 8; + frame->surface_internal.Info.FourCC = MFX_FOURCC_NV12; + frame->surface_internal.Info.Width = avctx->coded_width; + frame->surface_internal.Info.Height = avctx->coded_height; + frame->surface_internal.Info.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + + frame->surface_internal.Data.PitchLow = frame->frame->linesize[0]; + frame->surface_internal.Data.Y = frame->frame->data[0]; + frame->surface_internal.Data.UV = frame->frame->data[1]; + + frame->surface = &frame->surface_internal; + } + + return 0; +} + +static void qsv_clear_unused_frames(QSVContext *q) +{ + QSVFrame *cur = q->work_frames; + while (cur) { + if (cur->surface && !cur->surface->Data.Locked) { + cur->surface = NULL; + av_frame_unref(cur->frame); + } + cur = cur->next; + } +} + +static int get_surface(AVCodecContext *avctx, QSVContext *q, mfxFrameSurface1 **surf) +{ + QSVFrame *frame, **last; + int ret; + + qsv_clear_unused_frames(q); + + frame = q->work_frames; + last = &q->work_frames; + while (frame) { + if (!frame->surface) { + ret = alloc_frame(avctx, frame); + if (ret < 0) + return ret; + *surf = frame->surface; + return 0; + } + + last = &frame->next; + frame = frame->next; + } + + frame = av_mallocz(sizeof(*frame)); + if (!frame) + return AVERROR(ENOMEM); + frame->frame = av_frame_alloc(); + if (!frame->frame) { + av_freep(&frame); + return AVERROR(ENOMEM); + } + *last = frame; + + ret = alloc_frame(avctx, frame); + if (ret < 0) + return ret; + + *surf = frame->surface; + + return 0; +} + +static AVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf) +{ + QSVFrame *cur = q->work_frames; + while (cur) { + if (surf == cur->surface) + return cur->frame; + cur = cur->next; + } + return NULL; +} + +int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, + AVFrame *frame, int *got_frame, + AVPacket *avpkt) +{ + mfxFrameSurface1 *insurf; + mfxFrameSurface1 *outsurf; + mfxSyncPoint sync; + mfxBitstream bs = { { { 0 } } }; + int ret; + + if (avpkt->size) { + bs.Data = avpkt->data; + bs.DataLength = avpkt->size; + bs.MaxLength = bs.DataLength; + bs.TimeStamp = avpkt->pts; + } + + do { + ret = get_surface(avctx, q, &insurf); + if (ret < 0) + return ret; + + ret = MFXVideoDECODE_DecodeFrameAsync(q->session, avpkt->size ? &bs : NULL, + insurf, &outsurf, &sync); + if (ret == MFX_WRN_DEVICE_BUSY) + av_usleep(1); + + } while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_ERR_MORE_SURFACE); + + if (ret != MFX_ERR_NONE && + ret != MFX_ERR_MORE_DATA && + ret != MFX_WRN_VIDEO_PARAM_CHANGED && + ret != MFX_ERR_MORE_SURFACE) { + av_log(avctx, AV_LOG_ERROR, "Error during QSV decoding.\n"); + return ff_qsv_error(ret); + } + + if (sync) { + AVFrame *src_frame; + + MFXVideoCORE_SyncOperation(q->session, sync, 60000); + + src_frame = find_frame(q, outsurf); + if (!src_frame) { + av_log(avctx, AV_LOG_ERROR, + "The returned surface does not correspond to any frame\n"); + return AVERROR_BUG; + } + + ret = av_frame_ref(frame, src_frame); + if (ret < 0) + return ret; + + frame->pkt_pts = frame->pts = outsurf->Data.TimeStamp; + + frame->repeat_pict = + outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_TRIPLING ? 4 : + outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_DOUBLING ? 2 : + outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED ? 1 : 0; + frame->top_field_first = + outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF; + frame->interlaced_frame = + !(outsurf->Info.PicStruct & MFX_PICSTRUCT_PROGRESSIVE); + + *got_frame = 1; + } + + return bs.DataOffset; +} + +int ff_qsv_close(QSVContext *q) +{ + QSVFrame *cur = q->work_frames; + + while (cur) { + q->work_frames = cur->next; + av_frame_free(&cur->frame); + av_freep(&cur); + cur = q->work_frames; + } + + if (q->internal_session) + MFXClose(q->internal_session); + + return 0; +} diff --git a/libavcodec/qsv.h b/libavcodec/qsv.h new file mode 100644 index 0000000000..e7487c88ea --- /dev/null +++ b/libavcodec/qsv.h @@ -0,0 +1,41 @@ +/* + * Intel MediaSDK QSV public API + * + * 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 + */ + +#ifndef AVCODEC_QSV_H +#define AVCODEC_QSV_H + +#include <mfx/mfxvideo.h> + +typedef struct AVQSVContext { + mfxSession session; + int iopattern; + + mfxExtBuffer **ext_buffers; + int nb_ext_buffers; +} AVQSVContext; + +/** + * Allocate a new context. + * + * It must be freed by the caller with av_free(). + */ +AVQSVContext *av_qsv_alloc_context(void); + +#endif /* AVCODEC_QSV_H */ diff --git a/libavcodec/qsv_api.c b/libavcodec/qsv_api.c new file mode 100644 index 0000000000..8ae3d5b9b6 --- /dev/null +++ b/libavcodec/qsv_api.c @@ -0,0 +1,28 @@ +/* + * Intel MediaSDK QSV public API functions + * + * 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 "libavutil/mem.h" + +#include "qsv.h" + +AVQSVContext *av_qsv_alloc_context(void) +{ + return av_mallocz(sizeof(AVQSVContext)); +} diff --git a/libavcodec/qsv_h264.c b/libavcodec/qsv_h264.c new file mode 100644 index 0000000000..eafc352310 --- /dev/null +++ b/libavcodec/qsv_h264.c @@ -0,0 +1,313 @@ +/* + * Intel MediaSDK QSV based H.264 decoder + * + * copyright (c) 2013 Luca Barbato + * copyright (c) 2015 Anton Khirnov + * + * 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 <stdint.h> +#include <string.h> + +#include <mfx/mfxvideo.h> + +#include "libavutil/common.h" +#include "libavutil/fifo.h" +#include "libavutil/opt.h" + +#include "avcodec.h" +#include "internal.h" +#include "qsv_internal.h" +#include "qsv.h" + +typedef struct QSVH264Context { + AVClass *class; + QSVContext qsv; + + // the internal parser and codec context for parsing the data + AVCodecParserContext *parser; + AVCodecContext *avctx_internal; + enum AVPixelFormat orig_pix_fmt; + + // the filter for converting to Annex B + AVBitStreamFilterContext *bsf; + + AVFifoBuffer *packet_fifo; + + AVPacket input_ref; + AVPacket pkt_filtered; + uint8_t *filtered_data; +} QSVH264Context; + +static void qsv_clear_buffers(QSVH264Context *s) +{ + AVPacket pkt; + while (av_fifo_size(s->packet_fifo) >= sizeof(pkt)) { + av_fifo_generic_read(s->packet_fifo, &pkt, sizeof(pkt), NULL); + av_packet_unref(&pkt); + } + + if (s->filtered_data != s->input_ref.data) + av_freep(&s->filtered_data); + s->filtered_data = NULL; + av_packet_unref(&s->input_ref); +} + +static av_cold int qsv_decode_close(AVCodecContext *avctx) +{ + QSVH264Context *s = avctx->priv_data; + + ff_qsv_close(&s->qsv); + + qsv_clear_buffers(s); + + av_fifo_free(s->packet_fifo); + + av_bitstream_filter_close(s->bsf); + av_parser_close(s->parser); + avcodec_free_context(&s->avctx_internal); + + return 0; +} + +static av_cold int qsv_decode_init(AVCodecContext *avctx) +{ + QSVH264Context *s = avctx->priv_data; + int ret; + + s->orig_pix_fmt = AV_PIX_FMT_NONE; + + s->packet_fifo = av_fifo_alloc(sizeof(AVPacket)); + if (!s->packet_fifo) { + ret = AVERROR(ENOMEM); + goto fail; + } + + s->bsf = av_bitstream_filter_init("h264_mp4toannexb"); + if (!s->bsf) { + ret = AVERROR(ENOMEM); + goto fail; + } + + s->avctx_internal = avcodec_alloc_context3(NULL); + if (!s->avctx_internal) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if (avctx->extradata) { + s->avctx_internal->extradata = av_mallocz(avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!s->avctx_internal->extradata) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(s->avctx_internal->extradata, avctx->extradata, + avctx->extradata_size); + s->avctx_internal->extradata_size = avctx->extradata_size; + } + + s->parser = av_parser_init(AV_CODEC_ID_H264); + if (!s->parser) { + ret = AVERROR(ENOMEM); + goto fail; + } + s->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + + s->qsv.iopattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; + + return 0; +fail: + qsv_decode_close(avctx); + return ret; +} + +static int qsv_process_data(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *pkt) +{ + QSVH264Context *s = avctx->priv_data; + uint8_t *dummy_data; + int dummy_size; + int ret; + + /* we assume the packets are already split properly and want + * just the codec parameters here */ + av_parser_parse2(s->parser, s->avctx_internal, + &dummy_data, &dummy_size, + pkt->data, pkt->size, pkt->pts, pkt->dts, + pkt->pos); + + /* TODO: flush delayed frames on reinit */ + if (s->parser->format != s->orig_pix_fmt || + s->parser->coded_width != avctx->coded_width || + s->parser->coded_height != avctx->coded_height) { + mfxSession session = NULL; + + enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_QSV, + AV_PIX_FMT_NONE, + AV_PIX_FMT_NONE }; + enum AVPixelFormat qsv_format; + + qsv_format = ff_qsv_map_pixfmt(s->parser->format); + if (qsv_format < 0) { + av_log(avctx, AV_LOG_ERROR, + "Only 8-bit YUV420 streams are supported.\n"); + ret = AVERROR(ENOSYS); + goto reinit_fail; + } + + s->orig_pix_fmt = s->parser->format; + avctx->pix_fmt = pix_fmts[1] = qsv_format; + avctx->width = s->parser->width; + avctx->height = s->parser->height; + avctx->coded_width = s->parser->coded_width; + avctx->coded_height = s->parser->coded_height; + avctx->level = s->avctx_internal->level; + avctx->profile = s->avctx_internal->profile; + + ret = ff_get_format(avctx, pix_fmts); + if (ret < 0) + goto reinit_fail; + + avctx->pix_fmt = ret; + + if (avctx->hwaccel_context) { + AVQSVContext *user_ctx = avctx->hwaccel_context; + session = user_ctx->session; + s->qsv.iopattern = user_ctx->iopattern; + s->qsv.ext_buffers = user_ctx->ext_buffers; + s->qsv.nb_ext_buffers = user_ctx->nb_ext_buffers; + } + + ret = ff_qsv_init(avctx, &s->qsv, session); + if (ret < 0) + goto reinit_fail; + } + + return ff_qsv_decode(avctx, &s->qsv, frame, got_frame, &s->pkt_filtered); + +reinit_fail: + s->orig_pix_fmt = s->parser->format = avctx->pix_fmt = AV_PIX_FMT_NONE; + return ret; +} + +static int qsv_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + QSVH264Context *s = avctx->priv_data; + AVFrame *frame = data; + int ret; + + /* buffer the input packet */ + if (avpkt->size) { + AVPacket input_ref = { 0 }; + + if (av_fifo_space(s->packet_fifo) < sizeof(input_ref)) { + ret = av_fifo_realloc2(s->packet_fifo, + av_fifo_size(s->packet_fifo) + sizeof(input_ref)); + if (ret < 0) + return ret; + } + + ret = av_packet_ref(&input_ref, avpkt); + if (ret < 0) + return ret; + av_fifo_generic_write(s->packet_fifo, &input_ref, sizeof(input_ref), NULL); + } + + /* process buffered data */ + while (!*got_frame) { + /* prepare the input data -- convert to Annex B if needed */ + if (s->pkt_filtered.size <= 0) { + int size; + + /* no more data */ + if (av_fifo_size(s->packet_fifo) < sizeof(AVPacket)) + return avpkt->size ? avpkt->size : ff_qsv_decode(avctx, &s->qsv, frame, got_frame, avpkt); + + if (s->filtered_data != s->input_ref.data) + av_freep(&s->filtered_data); + s->filtered_data = NULL; + av_packet_unref(&s->input_ref); + + av_fifo_generic_read(s->packet_fifo, &s->input_ref, sizeof(s->input_ref), NULL); + ret = av_bitstream_filter_filter(s->bsf, avctx, NULL, + &s->filtered_data, &size, + s->input_ref.data, s->input_ref.size, 0); + if (ret < 0) { + s->filtered_data = s->input_ref.data; + size = s->input_ref.size; + } + s->pkt_filtered = s->input_ref; + s->pkt_filtered.data = s->filtered_data; + s->pkt_filtered.size = size; + } + + ret = qsv_process_data(avctx, frame, got_frame, &s->pkt_filtered); + if (ret < 0) + return ret; + + s->pkt_filtered.size -= ret; + s->pkt_filtered.data += ret; + } + + return avpkt->size; +} + +static void qsv_decode_flush(AVCodecContext *avctx) +{ + QSVH264Context *s = avctx->priv_data; + + qsv_clear_buffers(s); + s->orig_pix_fmt = AV_PIX_FMT_NONE; +} + +AVHWAccel ff_h264_qsv_hwaccel = { + .name = "h264_qsv", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .pix_fmt = AV_PIX_FMT_QSV, +}; + +#define OFFSET(x) offsetof(QSVH264Context, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD }, + { NULL }, +}; + +static const AVClass class = { + .class_name = "h264_qsv", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_h264_qsv_decoder = { + .name = "h264_qsv", + .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration)"), + .priv_data_size = sizeof(QSVH264Context), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .init = qsv_decode_init, + .decode = qsv_decode_frame, + .flush = qsv_decode_flush, + .close = qsv_decode_close, + .capabilities = CODEC_CAP_DELAY, + .priv_class = &class, +}; diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h new file mode 100644 index 0000000000..c154eaff86 --- /dev/null +++ b/libavcodec/qsv_internal.h @@ -0,0 +1,86 @@ +/* + * Intel MediaSDK QSV utility functions + * + * copyright (c) 2013 Luca Barbato + * + * 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 + */ + +#ifndef AVCODEC_QSV_INTERNAL_H +#define AVCODEC_QSV_INTERNAL_H + +#include <stdint.h> +#include <sys/types.h> + +#include <mfx/mfxvideo.h> + +#include "libavutil/frame.h" +#include "libavutil/pixfmt.h" + +#include "avcodec.h" + +#define QSV_VERSION_MAJOR 1 +#define QSV_VERSION_MINOR 1 + +#define ASYNC_DEPTH_DEFAULT 4 // internal parallelism + +typedef struct QSVFrame { + AVFrame *frame; + mfxFrameSurface1 *surface; + + mfxFrameSurface1 surface_internal; + + struct QSVFrame *next; +} QSVFrame; + +typedef struct QSVContext { + // the session used for decoding + mfxSession session; + + // the session we allocated internally, in case the caller did not provide + // one + mfxSession internal_session; + + /** + * a linked list of frames currently being used by QSV + */ + QSVFrame *work_frames; + + // options set by the caller + int async_depth; + int iopattern; + + mfxExtBuffer **ext_buffers; + int nb_ext_buffers; +} QSVContext; + +/** + * Convert a libmfx error code into a ffmpeg error code. + */ +int ff_qsv_error(int mfx_err); + +int ff_qsv_map_pixfmt(enum AVPixelFormat format); + +int ff_qsv_init(AVCodecContext *s, QSVContext *q, mfxSession session); + +int ff_qsv_decode(AVCodecContext *s, QSVContext *q, + AVFrame *frame, int *got_frame, + AVPacket *avpkt); + +int ff_qsv_close(QSVContext *q); + +#endif /* AVCODEC_QSV_INTERNAL_H */ diff --git a/libavcodec/version.h b/libavcodec/version.h index 654069db34..51bfead217 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 56 -#define LIBAVCODEC_VERSION_MINOR 23 +#define LIBAVCODEC_VERSION_MINOR 24 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ |