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.

295 lines
9.8KB

  1. /*
  2. * This file is part of Libav.
  3. *
  4. * Libav 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. * Libav 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 Libav; 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 "avconv.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. VdpVideoSurfaceCreate *video_surface_create;
  37. VdpVideoSurfaceDestroy *video_surface_destroy;
  38. VdpVideoSurfaceGetBitsYCbCr *video_surface_get_bits;
  39. VdpVideoSurfaceGetParameters *video_surface_get_parameters;
  40. VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities *video_surface_query;
  41. AVFrame *tmp_frame;
  42. enum AVPixelFormat pix_fmt;
  43. VdpYCbCrFormat vdpau_format;
  44. } VDPAUContext;
  45. static void vdpau_uninit(AVCodecContext *s)
  46. {
  47. InputStream *ist = s->opaque;
  48. VDPAUContext *ctx = ist->hwaccel_ctx;
  49. ist->hwaccel_uninit = NULL;
  50. ist->hwaccel_get_buffer = NULL;
  51. ist->hwaccel_retrieve_data = NULL;
  52. if (ctx->device_destroy)
  53. ctx->device_destroy(ctx->device);
  54. if (ctx->dpy)
  55. XCloseDisplay(ctx->dpy);
  56. av_frame_free(&ctx->tmp_frame);
  57. av_freep(&ist->hwaccel_ctx);
  58. av_freep(&s->hwaccel_context);
  59. }
  60. static void vdpau_release_buffer(void *opaque, uint8_t *data)
  61. {
  62. VdpVideoSurface surface = *(VdpVideoSurface*)data;
  63. VDPAUContext *ctx = opaque;
  64. ctx->video_surface_destroy(surface);
  65. av_freep(&data);
  66. }
  67. static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
  68. {
  69. InputStream *ist = s->opaque;
  70. VDPAUContext *ctx = ist->hwaccel_ctx;
  71. VdpVideoSurface *surface;
  72. VdpStatus err;
  73. av_assert0(frame->format == AV_PIX_FMT_VDPAU);
  74. surface = av_malloc(sizeof(*surface));
  75. if (!surface)
  76. return AVERROR(ENOMEM);
  77. frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface),
  78. vdpau_release_buffer, ctx,
  79. AV_BUFFER_FLAG_READONLY);
  80. if (!frame->buf[0]) {
  81. av_freep(&surface);
  82. return AVERROR(ENOMEM);
  83. }
  84. // properly we should keep a pool of surfaces instead of creating
  85. // them anew for each frame, but since we don't care about speed
  86. // much in this code, we don't bother
  87. err = ctx->video_surface_create(ctx->device, VDP_CHROMA_TYPE_420,
  88. frame->width, frame->height, surface);
  89. if (err != VDP_STATUS_OK) {
  90. av_log(NULL, AV_LOG_ERROR, "Error allocating a VDPAU video surface: %s\n",
  91. ctx->get_error_string(err));
  92. av_buffer_unref(&frame->buf[0]);
  93. return AVERROR_UNKNOWN;
  94. }
  95. frame->data[3] = (uint8_t*)(uintptr_t)*surface;
  96. return 0;
  97. }
  98. static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame)
  99. {
  100. VdpVideoSurface surface = (VdpVideoSurface)(uintptr_t)frame->data[3];
  101. InputStream *ist = s->opaque;
  102. VDPAUContext *ctx = ist->hwaccel_ctx;
  103. VdpStatus err;
  104. int ret, chroma_type;
  105. err = ctx->video_surface_get_parameters(surface, &chroma_type,
  106. &ctx->tmp_frame->width,
  107. &ctx->tmp_frame->height);
  108. if (err != VDP_STATUS_OK) {
  109. av_log(NULL, AV_LOG_ERROR, "Error getting surface parameters: %s\n",
  110. ctx->get_error_string(err));
  111. return AVERROR_UNKNOWN;
  112. }
  113. ctx->tmp_frame->format = ctx->pix_fmt;
  114. ret = av_frame_get_buffer(ctx->tmp_frame, 32);
  115. if (ret < 0)
  116. return ret;
  117. ctx->tmp_frame->width = frame->width;
  118. ctx->tmp_frame->height = frame->height;
  119. err = ctx->video_surface_get_bits(surface, ctx->vdpau_format,
  120. (void * const *)ctx->tmp_frame->data,
  121. ctx->tmp_frame->linesize);
  122. if (err != VDP_STATUS_OK) {
  123. av_log(NULL, AV_LOG_ERROR, "Error retrieving frame data from VDPAU: %s\n",
  124. ctx->get_error_string(err));
  125. ret = AVERROR_UNKNOWN;
  126. goto fail;
  127. }
  128. if (ctx->vdpau_format == VDP_YCBCR_FORMAT_YV12)
  129. FFSWAP(uint8_t*, ctx->tmp_frame->data[1], ctx->tmp_frame->data[2]);
  130. ret = av_frame_copy_props(ctx->tmp_frame, frame);
  131. if (ret < 0)
  132. goto fail;
  133. av_frame_unref(frame);
  134. av_frame_move_ref(frame, ctx->tmp_frame);
  135. return 0;
  136. fail:
  137. av_frame_unref(ctx->tmp_frame);
  138. return ret;
  139. }
  140. static const int vdpau_formats[][2] = {
  141. { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV420P },
  142. { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV12 },
  143. { VDP_YCBCR_FORMAT_YUYV, AV_PIX_FMT_YUYV422 },
  144. { VDP_YCBCR_FORMAT_UYVY, AV_PIX_FMT_UYVY422 },
  145. };
  146. static int vdpau_alloc(AVCodecContext *s)
  147. {
  148. InputStream *ist = s->opaque;
  149. int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
  150. VDPAUContext *ctx;
  151. const char *display, *vendor;
  152. VdpStatus err;
  153. int i;
  154. ctx = av_mallocz(sizeof(*ctx));
  155. if (!ctx)
  156. return AVERROR(ENOMEM);
  157. ist->hwaccel_ctx = ctx;
  158. ist->hwaccel_uninit = vdpau_uninit;
  159. ist->hwaccel_get_buffer = vdpau_get_buffer;
  160. ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  161. ctx->tmp_frame = av_frame_alloc();
  162. if (!ctx->tmp_frame)
  163. goto fail;
  164. ctx->dpy = XOpenDisplay(ist->hwaccel_device);
  165. if (!ctx->dpy) {
  166. av_log(NULL, loglevel, "Cannot open the X11 display %s.\n",
  167. XDisplayName(ist->hwaccel_device));
  168. goto fail;
  169. }
  170. display = XDisplayString(ctx->dpy);
  171. err = vdp_device_create_x11(ctx->dpy, XDefaultScreen(ctx->dpy), &ctx->device,
  172. &ctx->get_proc_address);
  173. if (err != VDP_STATUS_OK) {
  174. av_log(NULL, loglevel, "VDPAU device creation on X11 display %s failed.\n",
  175. display);
  176. goto fail;
  177. }
  178. #define GET_CALLBACK(id, result) \
  179. do { \
  180. void *tmp; \
  181. err = ctx->get_proc_address(ctx->device, id, &tmp); \
  182. if (err != VDP_STATUS_OK) { \
  183. av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \
  184. goto fail; \
  185. } \
  186. ctx->result = tmp; \
  187. } while (0)
  188. GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING, get_error_string);
  189. GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING, get_information_string);
  190. GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY, device_destroy);
  191. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE, video_surface_create);
  192. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, video_surface_destroy);
  193. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, video_surface_get_bits);
  194. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, video_surface_get_parameters);
  195. GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES,
  196. video_surface_query);
  197. for (i = 0; i < FF_ARRAY_ELEMS(vdpau_formats); i++) {
  198. VdpBool supported;
  199. err = ctx->video_surface_query(ctx->device, VDP_CHROMA_TYPE_420,
  200. vdpau_formats[i][0], &supported);
  201. if (err != VDP_STATUS_OK) {
  202. av_log(NULL, loglevel,
  203. "Error querying VDPAU surface capabilities: %s\n",
  204. ctx->get_error_string(err));
  205. goto fail;
  206. }
  207. if (supported)
  208. break;
  209. }
  210. if (i == FF_ARRAY_ELEMS(vdpau_formats)) {
  211. av_log(NULL, loglevel,
  212. "No supported VDPAU format for retrieving the data.\n");
  213. return AVERROR(EINVAL);
  214. }
  215. ctx->vdpau_format = vdpau_formats[i][0];
  216. ctx->pix_fmt = vdpau_formats[i][1];
  217. if (av_vdpau_bind_context(s, ctx->device, ctx->get_proc_address, 0))
  218. goto fail;
  219. ctx->get_information_string(&vendor);
  220. av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X11 display %s, "
  221. "to decode input stream #%d:%d.\n", vendor,
  222. display, ist->file_index, ist->st->index);
  223. return 0;
  224. fail:
  225. av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n",
  226. ist->file_index, ist->st->index);
  227. vdpau_uninit(s);
  228. return AVERROR(EINVAL);
  229. }
  230. int vdpau_init(AVCodecContext *s)
  231. {
  232. InputStream *ist = s->opaque;
  233. if (!ist->hwaccel_ctx) {
  234. int ret = vdpau_alloc(s);
  235. if (ret < 0)
  236. return ret;
  237. }
  238. ist->hwaccel_get_buffer = vdpau_get_buffer;
  239. ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  240. return 0;
  241. }