2 * Intel MediaSDK QSV codec-independent code
4 * copyright (c) 2013 Luca Barbato
5 * copyright (c) 2015 Anton Khirnov <anton@khirnov.net>
7 * This file is part of FFmpeg.
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <sys/types.h>
27 #include <mfx/mfxvideo.h>
29 #include "libavutil/common.h"
30 #include "libavutil/mem.h"
31 #include "libavutil/log.h"
32 #include "libavutil/pixfmt.h"
33 #include "libavutil/time.h"
38 #include "qsv_internal.h"
41 int ff_qsv_map_pixfmt(enum AVPixelFormat format)
44 case AV_PIX_FMT_YUV420P:
45 case AV_PIX_FMT_YUVJ420P:
46 return AV_PIX_FMT_NV12;
48 return AVERROR(ENOSYS);
52 int ff_qsv_decode_init(AVCodecContext *avctx, QSVContext *q, AVPacket *avpkt)
54 mfxVideoParam param = { { 0 } };
55 mfxBitstream bs = { { { 0 } } };
57 enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_QSV,
61 q->iopattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
63 if (avctx->hwaccel_context) {
64 AVQSVContext *qsv = avctx->hwaccel_context;
66 q->session = qsv->session;
67 q->iopattern = qsv->iopattern;
68 q->ext_buffers = qsv->ext_buffers;
69 q->nb_ext_buffers = qsv->nb_ext_buffers;
72 ret = ff_qsv_init_internal_session(avctx, &q->internal_qs,
77 q->session = q->internal_qs.session;
82 bs.Data = avpkt->data;
83 bs.DataLength = avpkt->size;
84 bs.MaxLength = bs.DataLength;
85 bs.TimeStamp = avpkt->pts;
87 return AVERROR_INVALIDDATA;
89 ret = ff_qsv_codec_id_to_mfx(avctx->codec_id);
91 av_log(avctx, AV_LOG_ERROR, "Unsupported codec_id %08x\n", avctx->codec_id);
95 param.mfx.CodecId = ret;
97 ret = MFXVideoDECODE_DecodeHeader(q->session, &bs, ¶m);
98 if (MFX_ERR_MORE_DATA==ret) {
99 /* this code means that header not found so we return packet size to skip
103 } else if (ret < 0) {
104 av_log(avctx, AV_LOG_ERROR, "Decode header error %d\n", ret);
105 return ff_qsv_error(ret);
107 param.IOPattern = q->iopattern;
108 param.AsyncDepth = q->async_depth;
109 param.ExtParam = q->ext_buffers;
110 param.NumExtParam = q->nb_ext_buffers;
111 param.mfx.FrameInfo.BitDepthLuma = 8;
112 param.mfx.FrameInfo.BitDepthChroma = 8;
114 ret = MFXVideoDECODE_Init(q->session, ¶m);
116 if (MFX_ERR_INVALID_VIDEO_PARAM==ret) {
117 av_log(avctx, AV_LOG_ERROR,
118 "Error initializing the MFX video decoder, unsupported video\n");
120 av_log(avctx, AV_LOG_ERROR,
121 "Error initializing the MFX video decoder %d\n", ret);
123 return ff_qsv_error(ret);
126 ret = ff_get_format(avctx, pix_fmts);
130 avctx->pix_fmt = ret;
131 avctx->profile = param.mfx.CodecProfile;
132 avctx->level = param.mfx.CodecLevel;
133 avctx->coded_width = param.mfx.FrameInfo.Width;
134 avctx->coded_height = param.mfx.FrameInfo.Height;
135 avctx->width = param.mfx.FrameInfo.CropW - param.mfx.FrameInfo.CropX;
136 avctx->height = param.mfx.FrameInfo.CropH - param.mfx.FrameInfo.CropY;
138 /* maximum decoder latency should be not exceed max DPB size for h.264 and
139 HEVC which is 16 for both cases.
140 So weare pre-allocating fifo big enough for 17 elements:
142 if (!q->async_fifo) {
143 q->async_fifo = av_fifo_alloc((1 + 16) *
144 (sizeof(mfxSyncPoint) + sizeof(QSVFrame*)));
146 return AVERROR(ENOMEM);
149 if (!q->input_fifo) {
150 q->input_fifo = av_fifo_alloc(1024*16);
152 return AVERROR(ENOMEM);
156 q->pkt_fifo = av_fifo_alloc( sizeof(AVPacket) * (1 + 16) );
158 return AVERROR(ENOMEM);
165 static int alloc_frame(AVCodecContext *avctx, QSVFrame *frame)
169 ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF);
173 if (frame->frame->format == AV_PIX_FMT_QSV) {
174 frame->surface = (mfxFrameSurface1*)frame->frame->data[3];
176 frame->surface_internal.Info.BitDepthLuma = 8;
177 frame->surface_internal.Info.BitDepthChroma = 8;
178 frame->surface_internal.Info.FourCC = MFX_FOURCC_NV12;
179 frame->surface_internal.Info.Width = avctx->coded_width;
180 frame->surface_internal.Info.Height = avctx->coded_height;
181 frame->surface_internal.Info.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
183 frame->surface_internal.Data.PitchLow = frame->frame->linesize[0];
184 frame->surface_internal.Data.Y = frame->frame->data[0];
185 frame->surface_internal.Data.UV = frame->frame->data[1];
187 frame->surface = &frame->surface_internal;
193 static void qsv_clear_unused_frames(QSVContext *q)
195 QSVFrame *cur = q->work_frames;
197 if (cur->surface && !cur->surface->Data.Locked && !cur->queued) {
199 av_frame_unref(cur->frame);
205 static int get_surface(AVCodecContext *avctx, QSVContext *q, mfxFrameSurface1 **surf)
207 QSVFrame *frame, **last;
210 qsv_clear_unused_frames(q);
212 frame = q->work_frames;
213 last = &q->work_frames;
215 if (!frame->surface) {
216 ret = alloc_frame(avctx, frame);
219 *surf = frame->surface;
227 frame = av_mallocz(sizeof(*frame));
229 return AVERROR(ENOMEM);
230 frame->frame = av_frame_alloc();
233 return AVERROR(ENOMEM);
237 ret = alloc_frame(avctx, frame);
241 *surf = frame->surface;
246 static QSVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf)
248 QSVFrame *cur = q->work_frames;
250 if (surf == cur->surface)
257 /* This function uses for 'smart' releasing of consumed data
258 from the input bitstream fifo.
259 Since the input fifo mapped to mfxBitstream which does not understand
260 a wrapping of data over fifo end, we should also to relocate a possible
261 data rest to fifo begin. If rest of data is absent then we just reset fifo's
262 pointers to initial positions.
263 NOTE the case when fifo does contain unconsumed data is rare and typical
264 amount of such data is 1..4 bytes.
266 static void qsv_fifo_relocate(AVFifoBuffer *f, int bytes_to_free)
271 av_fifo_drain(f, bytes_to_free);
273 data_size = av_fifo_size(f);
275 if (f->buffer!=f->rptr) {
276 if ( (f->end - f->rptr) < data_size) {
277 data_rest = data_size - (f->end - f->rptr);
278 data_size-=data_rest;
279 memmove(f->buffer+data_size, f->buffer, data_rest);
281 memmove(f->buffer, f->rptr, data_size);
282 data_size+= data_rest;
286 f->wptr = f->buffer + data_size;
292 static void close_decoder(QSVContext *q)
296 MFXVideoDECODE_Close(q->session);
298 cur = q->work_frames;
300 q->work_frames = cur->next;
301 av_frame_free(&cur->frame);
303 cur = q->work_frames;
307 q->reinit_pending = 0;
310 static int do_qsv_decode(AVCodecContext *avctx, QSVContext *q,
311 AVFrame *frame, int *got_frame,
315 mfxFrameSurface1 *insurf;
316 mfxFrameSurface1 *outsurf;
318 mfxBitstream bs = { { { 0 } } };
322 int flush = !avpkt->size || q->reinit_pending;
324 if (!q->engine_ready) {
325 ret = ff_qsv_decode_init(avctx, q, avpkt);
331 if (av_fifo_size(q->input_fifo)) {
332 /* we have got rest of previous packet into buffer */
333 if (av_fifo_space(q->input_fifo) < avpkt->size) {
334 ret = av_fifo_grow(q->input_fifo, avpkt->size);
338 av_fifo_generic_write(q->input_fifo, avpkt->data, avpkt->size, NULL);
339 bs.Data = q->input_fifo->rptr;
340 bs.DataLength = av_fifo_size(q->input_fifo);
343 bs.Data = avpkt->data;
344 bs.DataLength = avpkt->size;
346 bs.MaxLength = bs.DataLength;
347 bs.TimeStamp = avpkt->pts;
351 ret = get_surface(avctx, q, &insurf);
355 ret = MFXVideoDECODE_DecodeFrameAsync(q->session, flush ? NULL : &bs,
356 insurf, &outsurf, &sync);
357 if (ret != MFX_WRN_DEVICE_BUSY)
362 if (MFX_WRN_VIDEO_PARAM_CHANGED==ret) {
363 /* TODO: handle here minor sequence header changing */
364 } else if (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM==ret) {
365 av_fifo_reset(q->input_fifo);
366 flush = q->reinit_pending = 1;
371 QSVFrame *out_frame = find_frame(q, outsurf);
374 av_log(avctx, AV_LOG_ERROR,
375 "The returned surface does not correspond to any frame\n");
379 out_frame->queued = 1;
380 av_fifo_generic_write(q->async_fifo, &out_frame, sizeof(out_frame), NULL);
381 av_fifo_generic_write(q->async_fifo, &sync, sizeof(sync), NULL);
385 if (MFX_ERR_MORE_SURFACE != ret && ret < 0)
389 /* make sure we do not enter an infinite loop if the SDK
390 * did not consume any data and did not return anything */
391 if (!sync && !bs.DataOffset && !flush) {
392 av_log(avctx, AV_LOG_WARNING, "A decode call did not consume any data\n");
393 bs.DataOffset = avpkt->size;
397 qsv_fifo_relocate(q->input_fifo, bs.DataOffset);
398 } else if (bs.DataOffset!=avpkt->size) {
399 /* some data of packet was not consumed. store it to local buffer */
400 av_fifo_generic_write(q->input_fifo, avpkt->data+bs.DataOffset,
401 avpkt->size - bs.DataOffset, NULL);
404 if (MFX_ERR_MORE_DATA!=ret && ret < 0) {
405 av_log(avctx, AV_LOG_ERROR, "Error %d during QSV decoding.\n", ret);
406 return ff_qsv_error(ret);
408 n_out_frames = av_fifo_size(q->async_fifo) / (sizeof(out_frame)+sizeof(sync));
410 if (n_out_frames > q->async_depth || (flush && n_out_frames) ) {
413 av_fifo_generic_read(q->async_fifo, &out_frame, sizeof(out_frame), NULL);
414 av_fifo_generic_read(q->async_fifo, &sync, sizeof(sync), NULL);
415 out_frame->queued = 0;
417 MFXVideoCORE_SyncOperation(q->session, sync, 60000);
419 src_frame = out_frame->frame;
421 ret = av_frame_ref(frame, src_frame);
425 outsurf = out_frame->surface;
427 frame->pkt_pts = frame->pts = outsurf->Data.TimeStamp;
430 outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_TRIPLING ? 4 :
431 outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_DOUBLING ? 2 :
432 outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED ? 1 : 0;
433 frame->top_field_first =
434 outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF;
435 frame->interlaced_frame =
436 !(outsurf->Info.PicStruct & MFX_PICSTRUCT_PROGRESSIVE);
444 This function inserts a packet at fifo front.
446 static void qsv_packet_push_front(QSVContext *q, AVPacket *avpkt)
448 int fifo_size = av_fifo_size(q->pkt_fifo);
450 /* easy case fifo is empty */
451 av_fifo_generic_write(q->pkt_fifo, avpkt, sizeof(*avpkt), NULL);
453 /* realloc necessary */
455 AVFifoBuffer *fifo = av_fifo_alloc(fifo_size+av_fifo_space(q->pkt_fifo));
457 av_fifo_generic_write(fifo, avpkt, sizeof(*avpkt), NULL);
459 while (av_fifo_size(q->pkt_fifo)) {
460 av_fifo_generic_read(q->pkt_fifo, &pkt, sizeof(pkt), NULL);
461 av_fifo_generic_write(fifo, &pkt, sizeof(pkt), NULL);
463 av_fifo_free(q->pkt_fifo);
467 int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q,
468 AVFrame *frame, int *got_frame,
471 AVPacket pkt_ref = { 0 };
474 if (q->pkt_fifo && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) {
475 /* we already have got some buffered packets. so add new to tail */
476 ret = av_packet_ref(&pkt_ref, avpkt);
479 av_fifo_generic_write(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL);
481 if (q->reinit_pending) {
482 ret = do_qsv_decode(avctx, q, frame, got_frame, avpkt);
485 /* Flushing complete, no more frames */
487 //return ff_qsv_decode(avctx, q, frame, got_frame, avpkt);
490 if (!q->reinit_pending) {
491 if (q->pkt_fifo && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) {
492 /* process buffered packets */
493 while (!*got_frame && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) {
494 av_fifo_generic_read(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL);
495 ret = do_qsv_decode(avctx, q, frame, got_frame, &pkt_ref);
496 if (q->reinit_pending) {
498 A rare case: new reinit pending when buffering existing.
499 We should to return the pkt_ref back to same place of fifo
501 qsv_packet_push_front(q, &pkt_ref);
503 av_packet_unref(&pkt_ref);
507 /* general decoding */
508 ret = do_qsv_decode(avctx, q, frame, got_frame, avpkt);
509 if (q->reinit_pending) {
510 ret = av_packet_ref(&pkt_ref, avpkt);
513 av_fifo_generic_write(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL);
521 int ff_qsv_decode_close(QSVContext *q)
527 ff_qsv_close_internal_session(&q->internal_qs);
529 av_fifo_free(q->async_fifo);
530 q->async_fifo = NULL;
532 av_fifo_free(q->input_fifo);
533 q->input_fifo = NULL;
535 av_fifo_free(q->pkt_fifo);