ffmpeg_vdpau.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * This file is part of FFmpeg.
  3. *
  4. * FFmpeg is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * FFmpeg is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with FFmpeg; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include <stdint.h>
  19. #include <vdpau/vdpau.h>
  20. #include <vdpau/vdpau_x11.h>
  21. #include <X11/Xlib.h>
  22. #include "ffmpeg.h"
  23. #include "libavcodec/vdpau.h"
  24. #include "libavutil/avassert.h"
  25. #include "libavutil/buffer.h"
  26. #include "libavutil/frame.h"
  27. #include "libavutil/pixfmt.h"
  28. typedef struct VDPAUContext {
  29. Display *dpy;
  30. VdpDevice device;
  31. VdpDecoder decoder;
  32. VdpGetProcAddress *get_proc_address;
  33. VdpGetErrorString *get_error_string;
  34. VdpGetInformationString *get_information_string;
  35. VdpDeviceDestroy *device_destroy;
  36. VdpDecoderCreate *decoder_create;
  37. VdpDecoderDestroy *decoder_destroy;
  38. VdpDecoderRender *decoder_render;
  39. VdpVideoSurfaceCreate *video_surface_create;
  40. VdpVideoSurfaceDestroy *video_surface_destroy;
  41. VdpVideoSurfaceGetBitsYCbCr *video_surface_get_bits;
  42. VdpVideoSurfaceGetParameters *video_surface_get_parameters;
  43. VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities *video_surface_query;
  44. AVFrame *tmp_frame;
  45. enum AVPixelFormat pix_fmt;
  46. VdpYCbCrFormat vdpau_format;
  47. } VDPAUContext;
  48. static void vdpau_uninit(AVCodecContext *s)
  49. {
  50. InputStream *ist = s->opaque;
  51. VDPAUContext *ctx = ist->hwaccel_ctx;
  52. ist->hwaccel_uninit = NULL;
  53. ist->hwaccel_get_buffer = NULL;
  54. ist->hwaccel_retrieve_data = NULL;
  55. if (ctx->decoder_destroy)
  56. ctx->decoder_destroy(ctx->decoder);
  57. if (ctx->device_destroy)
  58. ctx->device_destroy(ctx->device);
  59. if (ctx->dpy)
  60. XCloseDisplay(ctx->dpy);
  61. av_frame_free(&ctx->tmp_frame);
  62. av_freep(&ist->hwaccel_ctx);
  63. av_freep(&s->hwaccel_context);
  64. }
  65. static void vdpau_release_buffer(void *opaque, uint8_t *data)
  66. {
  67. VdpVideoSurface surface = *(VdpVideoSurface*)data;
  68. VDPAUContext *ctx = opaque;
  69. ctx->video_surface_destroy(surface);
  70. av_freep(&data);
  71. }
  72. static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
  73. {
  74. InputStream *ist = s->opaque;
  75. VDPAUContext *ctx = ist->hwaccel_ctx;
  76. VdpVideoSurface *surface;
  77. VdpStatus err;
  78. av_assert0(frame->format == AV_PIX_FMT_VDPAU);
  79. surface = av_malloc(sizeof(*surface));
  80. if (!surface)
  81. return AVERROR(ENOMEM);
  82. frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface),
  83. vdpau_release_buffer, ctx,
  84. AV_BUFFER_FLAG_READONLY);
  85. if (!frame->buf[0]) {
  86. av_freep(&surface);
  87. return AVERROR(ENOMEM);
  88. }
  89. // properly we should keep a pool of surfaces instead of creating
  90. // them anew for each frame, but since we don't care about speed
  91. // much in this code, we don't bother
  92. err = ctx->video_surface_create(ctx->device, VDP_CHROMA_TYPE_420,
  93. frame->width, frame->height, surface);
  94. if (err != VDP_STATUS_OK) {
  95. av_log(NULL, AV_LOG_ERROR, "Error allocating a VDPAU video surface: %s\n",
  96. ctx->get_error_string(err));
  97. av_buffer_unref(&frame->buf[0]);
  98. return AVERROR_UNKNOWN;
  99. }
  100. frame->data[3] = (uint8_t*)(uintptr_t)*surface;
  101. return 0;
  102. }
  103. static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame)
  104. {
  105. VdpVideoSurface surface = (VdpVideoSurface)(uintptr_t)frame->data[3];
  106. InputStream *ist = s->opaque;
  107. VDPAUContext *ctx = ist->hwaccel_ctx;
  108. VdpStatus err;
  109. int ret, chroma_type;
  110. err = ctx->video_surface_get_parameters(surface, &chroma_type,
  111. &ctx->tmp_frame->width,
  112. &ctx->tmp_frame->height);
  113. if (err != VDP_STATUS_OK) {
  114. av_log(NULL, AV_LOG_ERROR, "Error getting surface parameters: %s\n",
  115. ctx->get_error_string(err));
  116. return AVERROR_UNKNOWN;
  117. }
  118. ctx->tmp_frame->format = ctx->pix_fmt;
  119. ret = av_frame_get_buffer(ctx->tmp_frame, 32);
  120. if (ret < 0)
  121. return ret;
  122. ctx->tmp_frame->width = frame->width;
  123. ctx->tmp_frame->height = frame->height;
  124. err = ctx->video_surface_get_bits(surface, ctx->vdpau_format,
  125. (void * const *)ctx->tmp_frame->data,
  126. ctx->tmp_frame->linesize);
  127. if (err != VDP_STATUS_OK) {
  128. av_log(NULL, AV_LOG_ERROR, "Error retrieving frame data from VDPAU: %s\n",
  129. ctx->get_error_string(err));
  130. ret = AVERROR_UNKNOWN;
  131. goto fail;
  132. }
  133. if (ctx->vdpau_format == VDP_YCBCR_FORMAT_YV12)
  134. FFSWAP(uint8_t*, ctx->tmp_frame->data[1], ctx->tmp_frame->data[2]);
  135. ret = av_frame_copy_props(ctx->tmp_frame, frame);
  136. if (ret < 0)
  137. goto fail;
  138. av_frame_unref(frame);
  139. av_frame_move_ref(frame, ctx->tmp_frame);
  140. return 0;
  141. fail:
  142. av_frame_unref(ctx->tmp_frame);
  143. return ret;
  144. }
  145. static const int vdpau_formats[][2] = {
  146. { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV420P },
  147. { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV12 },
  148. { VDP_YCBCR_FORMAT_YUYV, AV_PIX_FMT_YUYV422 },
  149. { VDP_YCBCR_FORMAT_UYVY, AV_PIX_FMT_UYVY422 },
  150. };
  151. static int vdpau_alloc(AVCodecContext *s)
  152. {
  153. InputStream *ist = s->opaque;
  154. int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
  155. AVVDPAUContext *vdpau_ctx;
  156. VDPAUContext *ctx;
  157. const char *display, *vendor;
  158. VdpStatus err;
  159. int i;
  160. ctx = av_mallocz(sizeof(*ctx));
  161. if (!ctx)
  162. return AVERROR(ENOMEM);
  163. ist->hwaccel_ctx = ctx;
  164. ist->hwaccel_uninit = vdpau_uninit;
  165. ist->hwaccel_get_buffer = vdpau_get_buffer;
  166. ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  167. ctx->tmp_frame = av_frame_alloc();
  168. if (!ctx->tmp_frame)
  169. goto fail;
  170. ctx->dpy = XOpenDisplay(ist->hwaccel_device);
  171. if (!ctx->dpy) {
  172. av_log(NULL, loglevel, "Cannot open the X11 display %s.\n",
  173. XDisplayName(ist->hwaccel_device));
  174. goto fail;
  175. }
  176. display = XDisplayString(ctx->dpy);
  177. err = vdp_device_create_x11(ctx->dpy, XDefaultScreen(ctx->dpy), &ctx->device,
  178. &ctx->get_proc_address);
  179. if (err != VDP_STATUS_OK) {
  180. av_log(NULL, loglevel, "VDPAU device creation on X11 display %s failed.\n",
  181. display);
  182. goto fail;
  183. }
  184. #define GET_CALLBACK(id, result) \
  185. do { \
  186. void *tmp; \
  187. err = ctx->get_proc_address(ctx->device, id, &tmp); \
  188. if (err != VDP_STATUS_OK) { \
  189. av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \
  190. goto fail; \
  191. } \
  192. ctx->result = tmp; \
  193. } while (0)
  194. GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING, get_error_string);
  195. GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING, get_information_string);
  196. GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY, device_destroy);
  197. GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE, decoder_create);
  198. GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY, decoder_destroy);
  199. GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER, decoder_render);
  200. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE, video_surface_create);
  201. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, video_surface_destroy);
  202. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, video_surface_get_bits);
  203. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, video_surface_get_parameters);
  204. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES,
  205. video_surface_query);
  206. for (i = 0; i < FF_ARRAY_ELEMS(vdpau_formats); i++) {
  207. VdpBool supported;
  208. err = ctx->video_surface_query(ctx->device, VDP_CHROMA_TYPE_420,
  209. vdpau_formats[i][0], &supported);
  210. if (err != VDP_STATUS_OK) {
  211. av_log(NULL, loglevel,
  212. "Error querying VDPAU surface capabilities: %s\n",
  213. ctx->get_error_string(err));
  214. goto fail;
  215. }
  216. if (supported)
  217. break;
  218. }
  219. if (i == FF_ARRAY_ELEMS(vdpau_formats)) {
  220. av_log(NULL, loglevel,
  221. "No supported VDPAU format for retrieving the data.\n");
  222. return AVERROR(EINVAL);
  223. }
  224. ctx->vdpau_format = vdpau_formats[i][0];
  225. ctx->pix_fmt = vdpau_formats[i][1];
  226. vdpau_ctx = av_vdpau_alloc_context();
  227. if (!vdpau_ctx)
  228. goto fail;
  229. vdpau_ctx->render = ctx->decoder_render;
  230. s->hwaccel_context = vdpau_ctx;
  231. ctx->get_information_string(&vendor);
  232. av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X11 display %s, "
  233. "to decode input stream #%d:%d.\n", vendor,
  234. display, ist->file_index, ist->st->index);
  235. return 0;
  236. fail:
  237. av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n",
  238. ist->file_index, ist->st->index);
  239. vdpau_uninit(s);
  240. return AVERROR(EINVAL);
  241. }
  242. int vdpau_init(AVCodecContext *s)
  243. {
  244. InputStream *ist = s->opaque;
  245. int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
  246. AVVDPAUContext *vdpau_ctx;
  247. VDPAUContext *ctx;
  248. VdpStatus err;
  249. int profile, ret;
  250. if (!ist->hwaccel_ctx) {
  251. ret = vdpau_alloc(s);
  252. if (ret < 0)
  253. return ret;
  254. }
  255. ctx = ist->hwaccel_ctx;
  256. vdpau_ctx = s->hwaccel_context;
  257. ret = av_vdpau_get_profile(s, &profile);
  258. if (ret < 0) {
  259. av_log(NULL, loglevel, "No known VDPAU decoder profile for this stream.\n");
  260. return AVERROR(EINVAL);
  261. }
  262. if (ctx->decoder)
  263. ctx->decoder_destroy(ctx->decoder);
  264. err = ctx->decoder_create(ctx->device, profile,
  265. s->coded_width, s->coded_height,
  266. 16, &ctx->decoder);
  267. if (err != VDP_STATUS_OK) {
  268. av_log(NULL, loglevel, "Error creating the VDPAU decoder: %s\n",
  269. ctx->get_error_string(err));
  270. return AVERROR_UNKNOWN;
  271. }
  272. vdpau_ctx->decoder = ctx->decoder;
  273. ist->hwaccel_get_buffer = vdpau_get_buffer;
  274. ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  275. return 0;
  276. }