You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 lines
9.7KB

  1. /*
  2. * Intel MediaSDK QSV codec-independent code
  3. *
  4. * copyright (c) 2013 Luca Barbato
  5. * copyright (c) 2015 Anton Khirnov <anton@khirnov.net>
  6. *
  7. * This file is part of Libav.
  8. *
  9. * Libav 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.
  13. *
  14. * Libav 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.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with Libav; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. */
  23. #include <string.h>
  24. #include <sys/types.h>
  25. #include <mfx/mfxvideo.h>
  26. #include "libavutil/common.h"
  27. #include "libavutil/mem.h"
  28. #include "libavutil/log.h"
  29. #include "libavutil/pixfmt.h"
  30. #include "libavutil/time.h"
  31. #include "avcodec.h"
  32. #include "internal.h"
  33. #include "qsv_internal.h"
  34. int ff_qsv_error(int mfx_err)
  35. {
  36. switch (mfx_err) {
  37. case MFX_ERR_NONE:
  38. return 0;
  39. case MFX_ERR_MEMORY_ALLOC:
  40. case MFX_ERR_NOT_ENOUGH_BUFFER:
  41. return AVERROR(ENOMEM);
  42. case MFX_ERR_INVALID_HANDLE:
  43. return AVERROR(EINVAL);
  44. case MFX_ERR_DEVICE_FAILED:
  45. case MFX_ERR_DEVICE_LOST:
  46. case MFX_ERR_LOCK_MEMORY:
  47. return AVERROR(EIO);
  48. case MFX_ERR_NULL_PTR:
  49. case MFX_ERR_UNDEFINED_BEHAVIOR:
  50. case MFX_ERR_NOT_INITIALIZED:
  51. return AVERROR_BUG;
  52. case MFX_ERR_UNSUPPORTED:
  53. case MFX_ERR_NOT_FOUND:
  54. return AVERROR(ENOSYS);
  55. case MFX_ERR_MORE_DATA:
  56. case MFX_ERR_MORE_SURFACE:
  57. case MFX_ERR_MORE_BITSTREAM:
  58. return AVERROR(EAGAIN);
  59. case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM:
  60. case MFX_ERR_INVALID_VIDEO_PARAM:
  61. return AVERROR(EINVAL);
  62. case MFX_ERR_ABORTED:
  63. case MFX_ERR_UNKNOWN:
  64. default:
  65. return AVERROR_UNKNOWN;
  66. }
  67. }
  68. int ff_qsv_map_pixfmt(enum AVPixelFormat format)
  69. {
  70. switch (format) {
  71. case AV_PIX_FMT_YUV420P:
  72. case AV_PIX_FMT_YUVJ420P:
  73. return AV_PIX_FMT_NV12;
  74. default:
  75. return AVERROR(ENOSYS);
  76. }
  77. }
  78. static int codec_id_to_mfx(enum AVCodecID codec_id)
  79. {
  80. switch (codec_id) {
  81. case AV_CODEC_ID_H264:
  82. return MFX_CODEC_AVC;
  83. case AV_CODEC_ID_MPEG1VIDEO:
  84. case AV_CODEC_ID_MPEG2VIDEO:
  85. return MFX_CODEC_MPEG2;
  86. case AV_CODEC_ID_VC1:
  87. return MFX_CODEC_VC1;
  88. default:
  89. break;
  90. }
  91. return AVERROR(ENOSYS);
  92. }
  93. static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession session)
  94. {
  95. if (!session) {
  96. if (!q->internal_session) {
  97. mfxIMPL impl = MFX_IMPL_AUTO_ANY;
  98. mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } };
  99. const char *desc;
  100. int ret;
  101. ret = MFXInit(impl, &ver, &q->internal_session);
  102. if (ret < 0) {
  103. av_log(avctx, AV_LOG_ERROR, "Error initializing an internal MFX session\n");
  104. return ff_qsv_error(ret);
  105. }
  106. MFXQueryIMPL(q->internal_session, &impl);
  107. if (impl & MFX_IMPL_SOFTWARE)
  108. desc = "software";
  109. else if (impl & MFX_IMPL_HARDWARE)
  110. desc = "hardware accelerated";
  111. else
  112. desc = "unknown";
  113. av_log(avctx, AV_LOG_VERBOSE,
  114. "Initialized an internal MFX session using %s implementation\n",
  115. desc);
  116. }
  117. q->session = q->internal_session;
  118. } else {
  119. q->session = session;
  120. }
  121. /* make sure the decoder is uninitialized */
  122. MFXVideoDECODE_Close(q->session);
  123. return 0;
  124. }
  125. int ff_qsv_init(AVCodecContext *avctx, QSVContext *q, mfxSession session)
  126. {
  127. mfxVideoParam param = { { 0 } };
  128. int ret;
  129. ret = qsv_init_session(avctx, q, session);
  130. if (ret < 0) {
  131. av_log(avctx, AV_LOG_ERROR, "Error initializing an MFX session\n");
  132. return ret;
  133. }
  134. ret = codec_id_to_mfx(avctx->codec_id);
  135. if (ret < 0)
  136. return ret;
  137. param.mfx.CodecId = ret;
  138. param.mfx.CodecProfile = avctx->profile;
  139. param.mfx.CodecLevel = avctx->level;
  140. param.mfx.FrameInfo.BitDepthLuma = 8;
  141. param.mfx.FrameInfo.BitDepthChroma = 8;
  142. param.mfx.FrameInfo.Shift = 0;
  143. param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
  144. param.mfx.FrameInfo.Width = avctx->coded_width;
  145. param.mfx.FrameInfo.Height = avctx->coded_height;
  146. param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
  147. param.IOPattern = q->iopattern;
  148. param.AsyncDepth = q->async_depth;
  149. param.ExtParam = q->ext_buffers;
  150. param.NumExtParam = q->nb_ext_buffers;
  151. ret = MFXVideoDECODE_Init(q->session, &param);
  152. if (ret < 0) {
  153. av_log(avctx, AV_LOG_ERROR, "Error initializing the MFX video decoder\n");
  154. return ff_qsv_error(ret);
  155. }
  156. return 0;
  157. }
  158. static int alloc_frame(AVCodecContext *avctx, QSVFrame *frame)
  159. {
  160. int ret;
  161. ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF);
  162. if (ret < 0)
  163. return ret;
  164. if (frame->frame->format == AV_PIX_FMT_QSV) {
  165. frame->surface = (mfxFrameSurface1*)frame->frame->data[3];
  166. } else {
  167. frame->surface_internal.Info.BitDepthLuma = 8;
  168. frame->surface_internal.Info.BitDepthChroma = 8;
  169. frame->surface_internal.Info.FourCC = MFX_FOURCC_NV12;
  170. frame->surface_internal.Info.Width = avctx->coded_width;
  171. frame->surface_internal.Info.Height = avctx->coded_height;
  172. frame->surface_internal.Info.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
  173. frame->surface_internal.Data.PitchLow = frame->frame->linesize[0];
  174. frame->surface_internal.Data.Y = frame->frame->data[0];
  175. frame->surface_internal.Data.UV = frame->frame->data[1];
  176. frame->surface = &frame->surface_internal;
  177. }
  178. return 0;
  179. }
  180. static void qsv_clear_unused_frames(QSVContext *q)
  181. {
  182. QSVFrame *cur = q->work_frames;
  183. while (cur) {
  184. if (cur->surface && !cur->surface->Data.Locked) {
  185. cur->surface = NULL;
  186. av_frame_unref(cur->frame);
  187. }
  188. cur = cur->next;
  189. }
  190. }
  191. static int get_surface(AVCodecContext *avctx, QSVContext *q, mfxFrameSurface1 **surf)
  192. {
  193. QSVFrame *frame, **last;
  194. int ret;
  195. qsv_clear_unused_frames(q);
  196. frame = q->work_frames;
  197. last = &q->work_frames;
  198. while (frame) {
  199. if (!frame->surface) {
  200. ret = alloc_frame(avctx, frame);
  201. if (ret < 0)
  202. return ret;
  203. *surf = frame->surface;
  204. return 0;
  205. }
  206. last = &frame->next;
  207. frame = frame->next;
  208. }
  209. frame = av_mallocz(sizeof(*frame));
  210. if (!frame)
  211. return AVERROR(ENOMEM);
  212. frame->frame = av_frame_alloc();
  213. if (!frame->frame) {
  214. av_freep(&frame);
  215. return AVERROR(ENOMEM);
  216. }
  217. *last = frame;
  218. ret = alloc_frame(avctx, frame);
  219. if (ret < 0)
  220. return ret;
  221. *surf = frame->surface;
  222. return 0;
  223. }
  224. static AVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf)
  225. {
  226. QSVFrame *cur = q->work_frames;
  227. while (cur) {
  228. if (surf == cur->surface)
  229. return cur->frame;
  230. cur = cur->next;
  231. }
  232. return NULL;
  233. }
  234. int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q,
  235. AVFrame *frame, int *got_frame,
  236. AVPacket *avpkt)
  237. {
  238. mfxFrameSurface1 *insurf;
  239. mfxFrameSurface1 *outsurf;
  240. mfxSyncPoint sync;
  241. mfxBitstream bs = { { { 0 } } };
  242. int ret;
  243. if (avpkt->size) {
  244. bs.Data = avpkt->data;
  245. bs.DataLength = avpkt->size;
  246. bs.MaxLength = bs.DataLength;
  247. bs.TimeStamp = avpkt->pts;
  248. }
  249. do {
  250. ret = get_surface(avctx, q, &insurf);
  251. if (ret < 0)
  252. return ret;
  253. ret = MFXVideoDECODE_DecodeFrameAsync(q->session, avpkt->size ? &bs : NULL,
  254. insurf, &outsurf, &sync);
  255. if (ret == MFX_WRN_DEVICE_BUSY)
  256. av_usleep(1);
  257. } while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_ERR_MORE_SURFACE);
  258. if (ret != MFX_ERR_NONE &&
  259. ret != MFX_ERR_MORE_DATA &&
  260. ret != MFX_WRN_VIDEO_PARAM_CHANGED &&
  261. ret != MFX_ERR_MORE_SURFACE) {
  262. av_log(avctx, AV_LOG_ERROR, "Error during QSV decoding.\n");
  263. return ff_qsv_error(ret);
  264. }
  265. if (sync) {
  266. AVFrame *src_frame;
  267. MFXVideoCORE_SyncOperation(q->session, sync, 60000);
  268. src_frame = find_frame(q, outsurf);
  269. if (!src_frame) {
  270. av_log(avctx, AV_LOG_ERROR,
  271. "The returned surface does not correspond to any frame\n");
  272. return AVERROR_BUG;
  273. }
  274. ret = av_frame_ref(frame, src_frame);
  275. if (ret < 0)
  276. return ret;
  277. frame->pkt_pts = frame->pts = outsurf->Data.TimeStamp;
  278. frame->repeat_pict =
  279. outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_TRIPLING ? 4 :
  280. outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_DOUBLING ? 2 :
  281. outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED ? 1 : 0;
  282. frame->top_field_first =
  283. outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF;
  284. frame->interlaced_frame =
  285. !(outsurf->Info.PicStruct & MFX_PICSTRUCT_PROGRESSIVE);
  286. *got_frame = 1;
  287. }
  288. return bs.DataOffset;
  289. }
  290. int ff_qsv_close(QSVContext *q)
  291. {
  292. QSVFrame *cur = q->work_frames;
  293. while (cur) {
  294. q->work_frames = cur->next;
  295. av_frame_free(&cur->frame);
  296. av_freep(&cur);
  297. cur = q->work_frames;
  298. }
  299. if (q->internal_session)
  300. MFXClose(q->internal_session);
  301. return 0;
  302. }