* commit '66acb76bb0492b263215ca9b4d927a7be39ace02':
lavc: add Intel libmfx-based HEVC encoder
Conflicts:
Changelog
configure
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/qsv.c
libavcodec/qsvenc.c
libavcodec/version.h
Merged-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -14,7 +14,7 @@ version <next>: |
14 | 14 |
- Go2Meeting decoding support |
15 | 15 |
- adrawgraph audio and drawgraph video filter |
16 | 16 |
- removegrain video filter |
17 |
-- Intel QSV-accelerated MPEG-2 video encoding |
|
17 |
+- Intel QSV-accelerated MPEG-2 video and HEVC encoding |
|
18 | 18 |
|
19 | 19 |
|
20 | 20 |
version 2.7: |
... | ... |
@@ -2214,6 +2214,7 @@ hap_decoder_select="snappy texturedsp" |
2214 | 2214 |
hap_encoder_deps="libsnappy" |
2215 | 2215 |
hap_encoder_select="texturedspenc" |
2216 | 2216 |
hevc_decoder_select="bswapdsp cabac golomb videodsp" |
2217 |
+hevc_qsv_encoder_select="qsvenc" |
|
2217 | 2218 |
huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp" |
2218 | 2219 |
huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llviddsp" |
2219 | 2220 |
iac_decoder_select="imc_decoder" |
... | ... |
@@ -286,6 +286,7 @@ OBJS-$(CONFIG_HAP_ENCODER) += hapenc.o |
286 | 286 |
OBJS-$(CONFIG_HEVC_DECODER) += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \ |
287 | 287 |
hevc_cabac.o hevc_refs.o hevcpred.o \ |
288 | 288 |
hevcdsp.o hevc_filter.o hevc_parse.o |
289 |
+OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc_ps_enc.o hevc_parse.o |
|
289 | 290 |
OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o |
290 | 291 |
OBJS-$(CONFIG_HQ_HQA_DECODER) += hq_hqa.o hq_hqadata.o hq_hqadsp.o \ |
291 | 292 |
canopus.o |
... | ... |
@@ -569,6 +569,7 @@ void avcodec_register_all(void) |
569 | 569 |
REGISTER_ENCODER(NVENC, nvenc); |
570 | 570 |
REGISTER_ENCODER(NVENC_H264, nvenc_h264); |
571 | 571 |
REGISTER_ENCODER(NVENC_HEVC, nvenc_hevc); |
572 |
+ REGISTER_ENCODER(HEVC_QSV, hevc_qsv); |
|
572 | 573 |
REGISTER_ENCODER(MPEG2_QSV, mpeg2_qsv); |
573 | 574 |
|
574 | 575 |
/* parsers */ |
... | ... |
@@ -1054,6 +1054,9 @@ void ff_hevc_hls_mvd_coding(HEVCContext *s, int x0, int y0, int log2_cb_size); |
1054 | 1054 |
int ff_hevc_extract_rbsp(HEVCContext *s, const uint8_t *src, int length, |
1055 | 1055 |
HEVCNAL *nal); |
1056 | 1056 |
|
1057 |
+int ff_hevc_encode_nal_vps(HEVCVPS *vps, unsigned int id, |
|
1058 |
+ uint8_t *buf, int buf_size); |
|
1059 |
+ |
|
1057 | 1060 |
extern const uint8_t ff_hevc_qpel_extra_before[4]; |
1058 | 1061 |
extern const uint8_t ff_hevc_qpel_extra_after[4]; |
1059 | 1062 |
extern const uint8_t ff_hevc_qpel_extra[4]; |
1060 | 1063 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,116 @@ |
0 |
+/* |
|
1 |
+ * HEVC Parameter Set encoding |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+#include "golomb.h" |
|
21 |
+#include "hevc.h" |
|
22 |
+#include "put_bits.h" |
|
23 |
+ |
|
24 |
+static void write_ptl_layer(PutBitContext *pb, PTLCommon *ptl) |
|
25 |
+{ |
|
26 |
+ int i; |
|
27 |
+ |
|
28 |
+ put_bits(pb, 2, ptl->profile_space); |
|
29 |
+ put_bits(pb, 1, ptl->tier_flag); |
|
30 |
+ put_bits(pb, 5, ptl->profile_idc); |
|
31 |
+ for (i = 0; i < 32; i++) |
|
32 |
+ put_bits(pb, 1, ptl->profile_compatibility_flag[i]); |
|
33 |
+ put_bits(pb, 1, ptl->progressive_source_flag); |
|
34 |
+ put_bits(pb, 1, ptl->interlaced_source_flag); |
|
35 |
+ put_bits(pb, 1, ptl->non_packed_constraint_flag); |
|
36 |
+ put_bits(pb, 1, ptl->frame_only_constraint_flag); |
|
37 |
+ put_bits32(pb, 0); // reserved |
|
38 |
+ put_bits(pb, 12, 0); // reserved |
|
39 |
+} |
|
40 |
+ |
|
41 |
+static void write_ptl(PutBitContext *pb, PTL *ptl, int max_num_sub_layers) |
|
42 |
+{ |
|
43 |
+ int i; |
|
44 |
+ |
|
45 |
+ write_ptl_layer(pb, &ptl->general_ptl); |
|
46 |
+ put_bits(pb, 8, ptl->general_ptl.level_idc); |
|
47 |
+ |
|
48 |
+ for (i = 0; i < max_num_sub_layers - 1; i++) { |
|
49 |
+ put_bits(pb, 1, ptl->sub_layer_profile_present_flag[i]); |
|
50 |
+ put_bits(pb, 1, ptl->sub_layer_level_present_flag[i]); |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ if (max_num_sub_layers > 1) |
|
54 |
+ for (i = max_num_sub_layers - 1; i < 8; i++) |
|
55 |
+ put_bits(pb, 2, 0); // reserved |
|
56 |
+ |
|
57 |
+ for (i = 0; i < max_num_sub_layers - 1; i++) { |
|
58 |
+ if (ptl->sub_layer_profile_present_flag[i]) |
|
59 |
+ write_ptl_layer(pb, &ptl->sub_layer_ptl[i]); |
|
60 |
+ if (ptl->sub_layer_level_present_flag[i]) |
|
61 |
+ put_bits(pb, 8, ptl->sub_layer_ptl[i].level_idc); |
|
62 |
+ } |
|
63 |
+} |
|
64 |
+ |
|
65 |
+int ff_hevc_encode_nal_vps(HEVCVPS *vps, unsigned int id, |
|
66 |
+ uint8_t *buf, int buf_size) |
|
67 |
+{ |
|
68 |
+ PutBitContext pb; |
|
69 |
+ int i; |
|
70 |
+ |
|
71 |
+ init_put_bits(&pb, buf, buf_size); |
|
72 |
+ put_bits(&pb, 4, id); |
|
73 |
+ put_bits(&pb, 2, 3); // reserved |
|
74 |
+ put_bits(&pb, 6, vps->vps_max_layers - 1); |
|
75 |
+ put_bits(&pb, 3, vps->vps_max_sub_layers - 1); |
|
76 |
+ put_bits(&pb, 1, vps->vps_temporal_id_nesting_flag); |
|
77 |
+ put_bits(&pb, 16, 0xffff); // reserved |
|
78 |
+ |
|
79 |
+ write_ptl(&pb, &vps->ptl, vps->vps_max_sub_layers); |
|
80 |
+ |
|
81 |
+ put_bits(&pb, 1, vps->vps_sub_layer_ordering_info_present_flag); |
|
82 |
+ for (i = vps->vps_sub_layer_ordering_info_present_flag ? 0 : vps->vps_max_layers - 1; |
|
83 |
+ i < vps->vps_max_sub_layers; i++) { |
|
84 |
+ set_ue_golomb(&pb, vps->vps_max_dec_pic_buffering[i] - 1); |
|
85 |
+ set_ue_golomb(&pb, vps->vps_num_reorder_pics[i]); |
|
86 |
+ set_ue_golomb(&pb, vps->vps_max_latency_increase[i] + 1); |
|
87 |
+ } |
|
88 |
+ |
|
89 |
+ put_bits(&pb, 6, vps->vps_max_layer_id); |
|
90 |
+ set_ue_golomb(&pb, vps->vps_num_layer_sets - 1); |
|
91 |
+ |
|
92 |
+ // writing layer_id_included_flag not supported |
|
93 |
+ if (vps->vps_num_layer_sets > 1) |
|
94 |
+ return AVERROR_PATCHWELCOME; |
|
95 |
+ |
|
96 |
+ put_bits(&pb, 1, vps->vps_timing_info_present_flag); |
|
97 |
+ if (vps->vps_timing_info_present_flag) { |
|
98 |
+ put_bits32(&pb, vps->vps_num_units_in_tick); |
|
99 |
+ put_bits32(&pb, vps->vps_time_scale); |
|
100 |
+ put_bits(&pb, 1, vps->vps_poc_proportional_to_timing_flag); |
|
101 |
+ if (vps->vps_poc_proportional_to_timing_flag) |
|
102 |
+ set_ue_golomb(&pb, vps->vps_num_ticks_poc_diff_one - 1); |
|
103 |
+ |
|
104 |
+ // writing HRD parameters not supported |
|
105 |
+ if (vps->vps_num_hrd_parameters) |
|
106 |
+ return AVERROR_PATCHWELCOME; |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ put_bits(&pb, 1, 0); // extension flag |
|
110 |
+ |
|
111 |
+ put_bits(&pb, 1, 1); // stop bit |
|
112 |
+ avpriv_align_put_bits(&pb); |
|
113 |
+ |
|
114 |
+ return put_bits_count(&pb) / 8; |
|
115 |
+} |
... | ... |
@@ -19,7 +19,12 @@ |
19 | 19 |
*/ |
20 | 20 |
|
21 | 21 |
#include <mfx/mfxvideo.h> |
22 |
+#include <mfx/mfxplugin.h> |
|
22 | 23 |
|
24 |
+#include <stdio.h> |
|
25 |
+#include <string.h> |
|
26 |
+ |
|
27 |
+#include "libavutil/avstring.h" |
|
23 | 28 |
#include "libavutil/error.h" |
24 | 29 |
|
25 | 30 |
#include "avcodec.h" |
... | ... |
@@ -30,6 +35,10 @@ int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id) |
30 | 30 |
switch (codec_id) { |
31 | 31 |
case AV_CODEC_ID_H264: |
32 | 32 |
return MFX_CODEC_AVC; |
33 |
+#if QSV_VERSION_ATLEAST(1, 8) |
|
34 |
+ case AV_CODEC_ID_HEVC: |
|
35 |
+ return MFX_CODEC_HEVC; |
|
36 |
+#endif |
|
33 | 37 |
case AV_CODEC_ID_MPEG1VIDEO: |
34 | 38 |
case AV_CODEC_ID_MPEG2VIDEO: |
35 | 39 |
return MFX_CODEC_MPEG2; |
... | ... |
@@ -154,7 +163,8 @@ static int ff_qsv_set_display_handle(AVCodecContext *avctx, mfxSession session) |
154 | 154 |
* @param avctx ffmpeg metadata for this codec context |
155 | 155 |
* @param session the MSDK session used |
156 | 156 |
*/ |
157 |
-int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session) |
|
157 |
+int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, |
|
158 |
+ const char *load_plugins) |
|
158 | 159 |
{ |
159 | 160 |
mfxIMPL impl = MFX_IMPL_AUTO_ANY; |
160 | 161 |
mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; |
... | ... |
@@ -188,6 +198,45 @@ int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session) |
188 | 188 |
desc = "unknown"; |
189 | 189 |
} |
190 | 190 |
|
191 |
+ if (load_plugins && *load_plugins) { |
|
192 |
+ while (*load_plugins) { |
|
193 |
+ mfxPluginUID uid; |
|
194 |
+ int i, err = 0; |
|
195 |
+ |
|
196 |
+ char *plugin = av_get_token(&load_plugins, ":"); |
|
197 |
+ if (!plugin) |
|
198 |
+ return AVERROR(ENOMEM); |
|
199 |
+ if (strlen(plugin) != 2 * sizeof(uid.Data)) { |
|
200 |
+ av_log(avctx, AV_LOG_ERROR, "Invalid plugin UID length\n"); |
|
201 |
+ err = AVERROR(EINVAL); |
|
202 |
+ goto load_plugin_fail; |
|
203 |
+ } |
|
204 |
+ |
|
205 |
+ for (i = 0; i < sizeof(uid.Data); i++) { |
|
206 |
+ err = sscanf(plugin + 2 * i, "%2hhx", uid.Data + i); |
|
207 |
+ if (err != 1) { |
|
208 |
+ av_log(avctx, AV_LOG_ERROR, "Invalid plugin UID\n"); |
|
209 |
+ err = AVERROR(EINVAL); |
|
210 |
+ goto load_plugin_fail; |
|
211 |
+ } |
|
212 |
+ |
|
213 |
+ } |
|
214 |
+ |
|
215 |
+ ret = MFXVideoUSER_Load(*session, &uid, 1); |
|
216 |
+ if (ret < 0) { |
|
217 |
+ av_log(avctx, AV_LOG_ERROR, "Could not load the requested plugin: %s\n", |
|
218 |
+ plugin); |
|
219 |
+ err = ff_qsv_error(ret); |
|
220 |
+ goto load_plugin_fail; |
|
221 |
+ } |
|
222 |
+ |
|
223 |
+load_plugin_fail: |
|
224 |
+ av_freep(&plugin); |
|
225 |
+ if (err < 0) |
|
226 |
+ return err; |
|
227 |
+ } |
|
228 |
+ } |
|
229 |
+ |
|
191 | 230 |
av_log(avctx, AV_LOG_VERBOSE, |
192 | 231 |
"Initialized an internal MFX session using %s implementation\n", |
193 | 232 |
desc); |
... | ... |
@@ -45,6 +45,10 @@ |
45 | 45 |
|
46 | 46 |
#define ASYNC_DEPTH_DEFAULT 4 // internal parallelism |
47 | 47 |
|
48 |
+#define QSV_VERSION_ATLEAST(MAJOR, MINOR) \ |
|
49 |
+ (MFX_VERSION_MAJOR > (MAJOR) || \ |
|
50 |
+ MFX_VERSION_MAJOR == (MAJOR) && MFX_VERSION_MINOR >= (MINOR)) |
|
51 |
+ |
|
48 | 52 |
typedef struct QSVFrame { |
49 | 53 |
AVFrame *frame; |
50 | 54 |
mfxFrameSurface1 *surface; |
... | ... |
@@ -61,6 +65,7 @@ int ff_qsv_error(int mfx_err); |
61 | 61 |
|
62 | 62 |
int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id); |
63 | 63 |
|
64 |
-int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session); |
|
64 |
+int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, |
|
65 |
+ const char *load_plugins); |
|
65 | 66 |
|
66 | 67 |
#endif /* AVCODEC_QSV_INTERNAL_H */ |
... | ... |
@@ -52,7 +52,7 @@ static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession ses |
52 | 52 |
{ |
53 | 53 |
if (!session) { |
54 | 54 |
if (!q->internal_session) { |
55 |
- int ret = ff_qsv_init_internal_session(avctx, &q->internal_session); |
|
55 |
+ int ret = ff_qsv_init_internal_session(avctx, &q->internal_session, NULL); |
|
56 | 56 |
if (ret < 0) |
57 | 57 |
return ret; |
58 | 58 |
} |
... | ... |
@@ -49,6 +49,8 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) |
49 | 49 |
return AVERROR_BUG; |
50 | 50 |
q->param.mfx.CodecId = ret; |
51 | 51 |
|
52 |
+ q->width_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16; |
|
53 |
+ |
|
52 | 54 |
if (avctx->level > 0) |
53 | 55 |
q->param.mfx.CodecLevel = avctx->level; |
54 | 56 |
|
... | ... |
@@ -74,7 +76,7 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) |
74 | 74 |
q->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; |
75 | 75 |
q->param.mfx.FrameInfo.BitDepthLuma = 8; |
76 | 76 |
q->param.mfx.FrameInfo.BitDepthChroma = 8; |
77 |
- q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, 16); |
|
77 |
+ q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, q->width_align); |
|
78 | 78 |
|
79 | 79 |
if (avctx->flags & CODEC_FLAG_INTERLACED_DCT) { |
80 | 80 |
/* A true field layout (TFF or BFF) is not important here, |
... | ... |
@@ -86,7 +88,7 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) |
86 | 86 |
q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, 32); |
87 | 87 |
} else { |
88 | 88 |
q->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; |
89 |
- q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, 16); |
|
89 |
+ q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16); |
|
90 | 90 |
} |
91 | 91 |
|
92 | 92 |
if (avctx->framerate.den > 0 && avctx->framerate.num > 0) { |
... | ... |
@@ -135,15 +137,19 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) |
135 | 135 |
break; |
136 | 136 |
} |
137 | 137 |
|
138 |
- q->extco.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; |
|
139 |
- q->extco.Header.BufferSz = sizeof(q->extco); |
|
140 |
- q->extco.CAVLC = avctx->coder_type == FF_CODER_TYPE_VLC ? |
|
141 |
- MFX_CODINGOPTION_ON : MFX_CODINGOPTION_UNKNOWN; |
|
138 |
+ // the HEVC encoder plugin currently fails if coding options |
|
139 |
+ // are provided |
|
140 |
+ if (avctx->codec_id != AV_CODEC_ID_HEVC) { |
|
141 |
+ q->extco.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; |
|
142 |
+ q->extco.Header.BufferSz = sizeof(q->extco); |
|
143 |
+ q->extco.CAVLC = avctx->coder_type == FF_CODER_TYPE_VLC ? |
|
144 |
+ MFX_CODINGOPTION_ON : MFX_CODINGOPTION_UNKNOWN; |
|
142 | 145 |
|
143 |
- q->extparam[0] = (mfxExtBuffer *)&q->extco; |
|
146 |
+ q->extparam[0] = (mfxExtBuffer *)&q->extco; |
|
144 | 147 |
|
145 |
- q->param.ExtParam = q->extparam; |
|
146 |
- q->param.NumExtParam = FF_ARRAY_ELEMS(q->extparam); |
|
148 |
+ q->param.ExtParam = q->extparam; |
|
149 |
+ q->param.NumExtParam = FF_ARRAY_ELEMS(q->extparam); |
|
150 |
+ } |
|
147 | 151 |
|
148 | 152 |
return 0; |
149 | 153 |
} |
... | ... |
@@ -210,7 +216,8 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q) |
210 | 210 |
} |
211 | 211 |
|
212 | 212 |
if (!q->session) { |
213 |
- ret = ff_qsv_init_internal_session(avctx, &q->internal_session); |
|
213 |
+ ret = ff_qsv_init_internal_session(avctx, &q->internal_session, |
|
214 |
+ q->load_plugins); |
|
214 | 215 |
if (ret < 0) |
215 | 216 |
return ret; |
216 | 217 |
|
... | ... |
@@ -316,9 +323,9 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, |
316 | 316 |
} |
317 | 317 |
|
318 | 318 |
/* make a copy if the input is not padded as libmfx requires */ |
319 |
- if (frame->height & 31 || frame->linesize[0] & 15) { |
|
319 |
+ if (frame->height & 31 || frame->linesize[0] & (q->width_align - 1)) { |
|
320 | 320 |
qf->frame->height = FFALIGN(frame->height, 32); |
321 |
- qf->frame->width = FFALIGN(frame->width, 16); |
|
321 |
+ qf->frame->width = FFALIGN(frame->width, q->width_align); |
|
322 | 322 |
|
323 | 323 |
ret = ff_get_buffer(q->avctx, qf->frame, AV_GET_BUFFER_FLAG_REF); |
324 | 324 |
if (ret < 0) |
... | ... |
@@ -42,6 +42,7 @@ typedef struct QSVEncContext { |
42 | 42 |
mfxSession internal_session; |
43 | 43 |
|
44 | 44 |
int packet_size; |
45 |
+ int width_align; |
|
45 | 46 |
|
46 | 47 |
mfxVideoParam param; |
47 | 48 |
mfxFrameAllocRequest req; |
... | ... |
@@ -56,6 +57,8 @@ typedef struct QSVEncContext { |
56 | 56 |
int preset; |
57 | 57 |
int avbr_accuracy; |
58 | 58 |
int avbr_convergence; |
59 |
+ |
|
60 |
+ char *load_plugins; |
|
59 | 61 |
} QSVEncContext; |
60 | 62 |
|
61 | 63 |
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); |
62 | 64 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,272 @@ |
0 |
+/* |
|
1 |
+ * Intel MediaSDK QSV based HEVC encoder |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+ |
|
21 |
+#include <stdint.h> |
|
22 |
+#include <sys/types.h> |
|
23 |
+ |
|
24 |
+#include <mfx/mfxvideo.h> |
|
25 |
+ |
|
26 |
+#include "libavutil/common.h" |
|
27 |
+#include "libavutil/opt.h" |
|
28 |
+ |
|
29 |
+#include "avcodec.h" |
|
30 |
+#include "bytestream.h" |
|
31 |
+#include "get_bits.h" |
|
32 |
+#include "hevc.h" |
|
33 |
+#include "internal.h" |
|
34 |
+#include "qsv.h" |
|
35 |
+#include "qsv_internal.h" |
|
36 |
+#include "qsvenc.h" |
|
37 |
+ |
|
38 |
+enum { |
|
39 |
+ LOAD_PLUGIN_NONE, |
|
40 |
+ LOAD_PLUGIN_HEVC_SW, |
|
41 |
+ LOAD_PLUGIN_HEVC_HW, |
|
42 |
+} LoadPlugin; |
|
43 |
+ |
|
44 |
+typedef struct QSVHEVCEncContext { |
|
45 |
+ AVClass *class; |
|
46 |
+ QSVEncContext qsv; |
|
47 |
+ int load_plugin; |
|
48 |
+} QSVHEVCEncContext; |
|
49 |
+ |
|
50 |
+static int generate_fake_vps(QSVEncContext *q, AVCodecContext *avctx) |
|
51 |
+{ |
|
52 |
+ GetByteContext gbc; |
|
53 |
+ PutByteContext pbc; |
|
54 |
+ |
|
55 |
+ GetBitContext gb; |
|
56 |
+ HEVCNAL sps_nal = { NULL }; |
|
57 |
+ HEVCSPS sps = { 0 }; |
|
58 |
+ HEVCVPS vps = { 0 }; |
|
59 |
+ uint8_t vps_buf[128], vps_rbsp_buf[128]; |
|
60 |
+ uint8_t *new_extradata; |
|
61 |
+ unsigned int sps_id; |
|
62 |
+ int ret, i, type, vps_size; |
|
63 |
+ |
|
64 |
+ if (!avctx->extradata_size) { |
|
65 |
+ av_log(avctx, AV_LOG_ERROR, "No extradata returned from libmfx\n"); |
|
66 |
+ return AVERROR_UNKNOWN; |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ /* parse the SPS */ |
|
70 |
+ ret = ff_hevc_extract_rbsp(avctx->extradata + 4, avctx->extradata_size - 4, &sps_nal); |
|
71 |
+ if (ret < 0) { |
|
72 |
+ av_log(avctx, AV_LOG_ERROR, "Error unescaping the SPS buffer\n"); |
|
73 |
+ return ret; |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ ret = init_get_bits8(&gb, sps_nal.data, sps_nal.size); |
|
77 |
+ if (ret < 0) { |
|
78 |
+ av_freep(&sps_nal.rbsp_buffer); |
|
79 |
+ return ret; |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ get_bits(&gb, 1); |
|
83 |
+ type = get_bits(&gb, 6); |
|
84 |
+ if (type != NAL_SPS) { |
|
85 |
+ av_log(avctx, AV_LOG_ERROR, "Unexpected NAL type in the extradata: %d\n", |
|
86 |
+ type); |
|
87 |
+ av_freep(&sps_nal.rbsp_buffer); |
|
88 |
+ return AVERROR_INVALIDDATA; |
|
89 |
+ } |
|
90 |
+ get_bits(&gb, 9); |
|
91 |
+ |
|
92 |
+ ret = ff_hevc_parse_sps(&sps, &gb, &sps_id, 0, NULL, avctx); |
|
93 |
+ av_freep(&sps_nal.rbsp_buffer); |
|
94 |
+ if (ret < 0) { |
|
95 |
+ av_log(avctx, AV_LOG_ERROR, "Error parsing the SPS\n"); |
|
96 |
+ return ret; |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ /* generate the VPS */ |
|
100 |
+ vps.vps_max_layers = 1; |
|
101 |
+ vps.vps_max_sub_layers = sps.max_sub_layers; |
|
102 |
+ memcpy(&vps.ptl, &sps.ptl, sizeof(vps.ptl)); |
|
103 |
+ vps.vps_sub_layer_ordering_info_present_flag = 1; |
|
104 |
+ for (i = 0; i < MAX_SUB_LAYERS; i++) { |
|
105 |
+ vps.vps_max_dec_pic_buffering[i] = sps.temporal_layer[i].max_dec_pic_buffering; |
|
106 |
+ vps.vps_num_reorder_pics[i] = sps.temporal_layer[i].num_reorder_pics; |
|
107 |
+ vps.vps_max_latency_increase[i] = sps.temporal_layer[i].max_latency_increase; |
|
108 |
+ } |
|
109 |
+ |
|
110 |
+ vps.vps_num_layer_sets = 1; |
|
111 |
+ vps.vps_timing_info_present_flag = sps.vui.vui_timing_info_present_flag; |
|
112 |
+ vps.vps_num_units_in_tick = sps.vui.vui_num_units_in_tick; |
|
113 |
+ vps.vps_time_scale = sps.vui.vui_time_scale; |
|
114 |
+ vps.vps_poc_proportional_to_timing_flag = sps.vui.vui_poc_proportional_to_timing_flag; |
|
115 |
+ vps.vps_num_ticks_poc_diff_one = sps.vui.vui_num_ticks_poc_diff_one_minus1 + 1; |
|
116 |
+ |
|
117 |
+ /* generate the encoded RBSP form of the VPS */ |
|
118 |
+ ret = ff_hevc_encode_nal_vps(&vps, sps.vps_id, vps_rbsp_buf, sizeof(vps_rbsp_buf)); |
|
119 |
+ if (ret < 0) { |
|
120 |
+ av_log(avctx, AV_LOG_ERROR, "Error writing the VPS\n"); |
|
121 |
+ return ret; |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ /* escape and add the startcode */ |
|
125 |
+ bytestream2_init(&gbc, vps_rbsp_buf, ret); |
|
126 |
+ bytestream2_init_writer(&pbc, vps_buf, sizeof(vps_buf)); |
|
127 |
+ |
|
128 |
+ bytestream2_put_be32(&pbc, 1); // startcode |
|
129 |
+ bytestream2_put_byte(&pbc, NAL_VPS << 1); // NAL |
|
130 |
+ bytestream2_put_byte(&pbc, 1); // header |
|
131 |
+ |
|
132 |
+ while (bytestream2_get_bytes_left(&gbc)) { |
|
133 |
+ uint32_t b = bytestream2_peek_be24(&gbc); |
|
134 |
+ if (b <= 3) { |
|
135 |
+ bytestream2_put_be24(&pbc, 3); |
|
136 |
+ bytestream2_skip(&gbc, 2); |
|
137 |
+ } else |
|
138 |
+ bytestream2_put_byte(&pbc, bytestream2_get_byte(&gbc)); |
|
139 |
+ } |
|
140 |
+ |
|
141 |
+ vps_size = bytestream2_tell_p(&pbc); |
|
142 |
+ new_extradata = av_mallocz(vps_size + avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); |
|
143 |
+ if (!new_extradata) |
|
144 |
+ return AVERROR(ENOMEM); |
|
145 |
+ memcpy(new_extradata, vps_buf, vps_size); |
|
146 |
+ memcpy(new_extradata + vps_size, avctx->extradata, avctx->extradata_size); |
|
147 |
+ |
|
148 |
+ av_freep(&avctx->extradata); |
|
149 |
+ avctx->extradata = new_extradata; |
|
150 |
+ avctx->extradata_size += vps_size; |
|
151 |
+ |
|
152 |
+ return 0; |
|
153 |
+} |
|
154 |
+ |
|
155 |
+static av_cold int qsv_enc_init(AVCodecContext *avctx) |
|
156 |
+{ |
|
157 |
+ QSVHEVCEncContext *q = avctx->priv_data; |
|
158 |
+ int ret; |
|
159 |
+ |
|
160 |
+ if (q->load_plugin != LOAD_PLUGIN_NONE) { |
|
161 |
+ static const char *uid_hevcenc_sw = "2fca99749fdb49aeb121a5b63ef568f7"; |
|
162 |
+ static const char *uid_hevcenc_hw = "e5400a06c74d41f5b12d430bbaa23d0b"; |
|
163 |
+ |
|
164 |
+ if (q->qsv.load_plugins[0]) { |
|
165 |
+ av_log(avctx, AV_LOG_WARNING, |
|
166 |
+ "load_plugins is not empty, but load_plugin is not set to 'none'." |
|
167 |
+ "The load_plugin value will be ignored.\n"); |
|
168 |
+ } else { |
|
169 |
+ av_freep(&q->qsv.load_plugins); |
|
170 |
+ |
|
171 |
+ if (q->load_plugin == LOAD_PLUGIN_HEVC_SW) |
|
172 |
+ q->qsv.load_plugins = av_strdup(uid_hevcenc_sw); |
|
173 |
+ else |
|
174 |
+ q->qsv.load_plugins = av_strdup(uid_hevcenc_hw); |
|
175 |
+ |
|
176 |
+ if (!q->qsv.load_plugins) |
|
177 |
+ return AVERROR(ENOMEM); |
|
178 |
+ } |
|
179 |
+ } |
|
180 |
+ |
|
181 |
+ ret = ff_qsv_enc_init(avctx, &q->qsv); |
|
182 |
+ if (ret < 0) |
|
183 |
+ return ret; |
|
184 |
+ |
|
185 |
+ ret = generate_fake_vps(&q->qsv, avctx); |
|
186 |
+ if (ret < 0) { |
|
187 |
+ ff_qsv_enc_close(avctx, &q->qsv); |
|
188 |
+ return ret; |
|
189 |
+ } |
|
190 |
+ |
|
191 |
+ return 0; |
|
192 |
+} |
|
193 |
+ |
|
194 |
+static int qsv_enc_frame(AVCodecContext *avctx, AVPacket *pkt, |
|
195 |
+ const AVFrame *frame, int *got_packet) |
|
196 |
+{ |
|
197 |
+ QSVHEVCEncContext *q = avctx->priv_data; |
|
198 |
+ |
|
199 |
+ return ff_qsv_encode(avctx, &q->qsv, pkt, frame, got_packet); |
|
200 |
+} |
|
201 |
+ |
|
202 |
+static av_cold int qsv_enc_close(AVCodecContext *avctx) |
|
203 |
+{ |
|
204 |
+ QSVHEVCEncContext *q = avctx->priv_data; |
|
205 |
+ |
|
206 |
+ return ff_qsv_enc_close(avctx, &q->qsv); |
|
207 |
+} |
|
208 |
+ |
|
209 |
+#define OFFSET(x) offsetof(QSVHEVCEncContext, x) |
|
210 |
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM |
|
211 |
+static const AVOption options[] = { |
|
212 |
+ { "async_depth", "Maximum processing parallelism", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VE }, |
|
213 |
+ { "avbr_accuracy", "Accuracy of the AVBR ratecontrol", OFFSET(qsv.avbr_accuracy), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, |
|
214 |
+ { "avbr_convergence", "Convergence of the AVBR ratecontrol", OFFSET(qsv.avbr_convergence), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, |
|
215 |
+ |
|
216 |
+ { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_SW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VE, "load_plugin" }, |
|
217 |
+ { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VE, "load_plugin" }, |
|
218 |
+ { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VE, "load_plugin" }, |
|
219 |
+ { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VE, "load_plugin" }, |
|
220 |
+ |
|
221 |
+ { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session", |
|
222 |
+ OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VE }, |
|
223 |
+ |
|
224 |
+ { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, |
|
225 |
+ { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, |
|
226 |
+ { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, |
|
227 |
+ { "main10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" }, |
|
228 |
+ { "mainsp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAINSP }, INT_MIN, INT_MAX, VE, "profile" }, |
|
229 |
+ |
|
230 |
+ { "preset", NULL, OFFSET(qsv.preset), AV_OPT_TYPE_INT, { .i64 = MFX_TARGETUSAGE_BALANCED }, 0, 7, VE, "preset" }, |
|
231 |
+ { "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_SPEED }, INT_MIN, INT_MAX, VE, "preset" }, |
|
232 |
+ { "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BALANCED }, INT_MIN, INT_MAX, VE, "preset" }, |
|
233 |
+ { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_QUALITY }, INT_MIN, INT_MAX, VE, "preset" }, |
|
234 |
+ |
|
235 |
+ { NULL }, |
|
236 |
+}; |
|
237 |
+ |
|
238 |
+static const AVClass class = { |
|
239 |
+ .class_name = "hevc_qsv encoder", |
|
240 |
+ .item_name = av_default_item_name, |
|
241 |
+ .option = options, |
|
242 |
+ .version = LIBAVUTIL_VERSION_INT, |
|
243 |
+}; |
|
244 |
+ |
|
245 |
+static const AVCodecDefault qsv_enc_defaults[] = { |
|
246 |
+ { "b", "1M" }, |
|
247 |
+ { "refs", "0" }, |
|
248 |
+ // same as the x264 default |
|
249 |
+ { "g", "250" }, |
|
250 |
+ { "bf", "3" }, |
|
251 |
+ |
|
252 |
+ { "flags", "+cgop" }, |
|
253 |
+ { NULL }, |
|
254 |
+}; |
|
255 |
+ |
|
256 |
+AVCodec ff_hevc_qsv_encoder = { |
|
257 |
+ .name = "hevc_qsv", |
|
258 |
+ .long_name = NULL_IF_CONFIG_SMALL("HEVC (Intel Quick Sync Video acceleration)"), |
|
259 |
+ .priv_data_size = sizeof(QSVHEVCEncContext), |
|
260 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
261 |
+ .id = AV_CODEC_ID_HEVC, |
|
262 |
+ .init = qsv_enc_init, |
|
263 |
+ .encode2 = qsv_enc_frame, |
|
264 |
+ .close = qsv_enc_close, |
|
265 |
+ .capabilities = CODEC_CAP_DELAY, |
|
266 |
+ .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, |
|
267 |
+ AV_PIX_FMT_QSV, |
|
268 |
+ AV_PIX_FMT_NONE }, |
|
269 |
+ .priv_class = &class, |
|
270 |
+ .defaults = qsv_enc_defaults, |
|
271 |
+}; |
... | ... |
@@ -29,8 +29,8 @@ |
29 | 29 |
#include "libavutil/version.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVCODEC_VERSION_MAJOR 56 |
32 |
-#define LIBAVCODEC_VERSION_MINOR 46 |
|
33 |
-#define LIBAVCODEC_VERSION_MICRO 102 |
|
32 |
+#define LIBAVCODEC_VERSION_MINOR 47 |
|
33 |
+#define LIBAVCODEC_VERSION_MICRO 100 |
|
34 | 34 |
|
35 | 35 |
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ |
36 | 36 |
LIBAVCODEC_VERSION_MINOR, \ |