Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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.  
  19. #include <stdint.h>
  20.  
  21. #include <vdpau/vdpau.h>
  22. #include <vdpau/vdpau_x11.h>
  23.  
  24. #include <X11/Xlib.h>
  25.  
  26. #include "ffmpeg.h"
  27.  
  28. #include "libavcodec/vdpau.h"
  29.  
  30. #include "libavutil/avassert.h"
  31. #include "libavutil/buffer.h"
  32. #include "libavutil/frame.h"
  33. #include "libavutil/pixfmt.h"
  34.  
  35. typedef struct VDPAUContext {
  36.     Display *dpy;
  37.  
  38.     VdpDevice  device;
  39.     VdpDecoder decoder;
  40.     VdpGetProcAddress *get_proc_address;
  41.  
  42.     VdpGetErrorString                               *get_error_string;
  43.     VdpGetInformationString                         *get_information_string;
  44.     VdpDeviceDestroy                                *device_destroy;
  45. #if 1 // for ffmpegs older vdpau API, not the oldest though
  46.     VdpDecoderCreate                                *decoder_create;
  47.     VdpDecoderDestroy                               *decoder_destroy;
  48.     VdpDecoderRender                                *decoder_render;
  49. #endif
  50.     VdpVideoSurfaceCreate                           *video_surface_create;
  51.     VdpVideoSurfaceDestroy                          *video_surface_destroy;
  52.     VdpVideoSurfaceGetBitsYCbCr                     *video_surface_get_bits;
  53.     VdpVideoSurfaceGetParameters                    *video_surface_get_parameters;
  54.     VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities *video_surface_query;
  55.  
  56.     AVFrame *tmp_frame;
  57.  
  58.     enum AVPixelFormat pix_fmt;
  59.     VdpYCbCrFormat vdpau_format;
  60. } VDPAUContext;
  61.  
  62. int vdpau_api_ver = 2;
  63.  
  64. static void vdpau_uninit(AVCodecContext *s)
  65. {
  66.     InputStream  *ist = s->opaque;
  67.     VDPAUContext *ctx = ist->hwaccel_ctx;
  68.  
  69.     ist->hwaccel_uninit        = NULL;
  70.     ist->hwaccel_get_buffer    = NULL;
  71.     ist->hwaccel_retrieve_data = NULL;
  72.  
  73.     if (ctx->decoder_destroy)
  74.         ctx->decoder_destroy(ctx->decoder);
  75.  
  76.     if (ctx->device_destroy)
  77.         ctx->device_destroy(ctx->device);
  78.  
  79.     if (ctx->dpy)
  80.         XCloseDisplay(ctx->dpy);
  81.  
  82.     av_frame_free(&ctx->tmp_frame);
  83.  
  84.     av_freep(&ist->hwaccel_ctx);
  85.     av_freep(&s->hwaccel_context);
  86. }
  87.  
  88. static void vdpau_release_buffer(void *opaque, uint8_t *data)
  89. {
  90.     VdpVideoSurface surface = *(VdpVideoSurface*)data;
  91.     VDPAUContext *ctx = opaque;
  92.  
  93.     ctx->video_surface_destroy(surface);
  94.     av_freep(&data);
  95. }
  96.  
  97. static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
  98. {
  99.     InputStream         *ist = s->opaque;
  100.     VDPAUContext        *ctx = ist->hwaccel_ctx;
  101.     VdpVideoSurface *surface;
  102.     VdpStatus err;
  103.     VdpChromaType chroma;
  104.     uint32_t width, height;
  105.  
  106.     av_assert0(frame->format == AV_PIX_FMT_VDPAU);
  107.  
  108.     if (av_vdpau_get_surface_parameters(s, &chroma, &width, &height))
  109.         return AVERROR(ENOSYS);
  110.  
  111.     surface = av_malloc(sizeof(*surface));
  112.     if (!surface)
  113.         return AVERROR(ENOMEM);
  114.  
  115.     frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface),
  116.                                      vdpau_release_buffer, ctx,
  117.                                      AV_BUFFER_FLAG_READONLY);
  118.     if (!frame->buf[0]) {
  119.         av_freep(&surface);
  120.         return AVERROR(ENOMEM);
  121.     }
  122.  
  123.     // properly we should keep a pool of surfaces instead of creating
  124.     // them anew for each frame, but since we don't care about speed
  125.     // much in this code, we don't bother
  126.     err = ctx->video_surface_create(ctx->device, chroma, width, height,
  127.                                     surface);
  128.     if (err != VDP_STATUS_OK) {
  129.         av_log(NULL, AV_LOG_ERROR, "Error allocating a VDPAU video surface: %s\n",
  130.                ctx->get_error_string(err));
  131.         av_buffer_unref(&frame->buf[0]);
  132.         return AVERROR_UNKNOWN;
  133.     }
  134.  
  135.     frame->data[3] = (uint8_t*)(uintptr_t)*surface;
  136.  
  137.     return 0;
  138. }
  139.  
  140. static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame)
  141. {
  142.     VdpVideoSurface surface = (VdpVideoSurface)(uintptr_t)frame->data[3];
  143.     InputStream        *ist = s->opaque;
  144.     VDPAUContext       *ctx = ist->hwaccel_ctx;
  145.     VdpStatus err;
  146.     int ret, chroma_type;
  147.  
  148.     err = ctx->video_surface_get_parameters(surface, &chroma_type,
  149.                                             &ctx->tmp_frame->width,
  150.                                             &ctx->tmp_frame->height);
  151.     if (err != VDP_STATUS_OK) {
  152.         av_log(NULL, AV_LOG_ERROR, "Error getting surface parameters: %s\n",
  153.                ctx->get_error_string(err));
  154.         return AVERROR_UNKNOWN;
  155.     }
  156.     ctx->tmp_frame->format = ctx->pix_fmt;
  157.  
  158.     ret = av_frame_get_buffer(ctx->tmp_frame, 32);
  159.     if (ret < 0)
  160.         return ret;
  161.  
  162.     ctx->tmp_frame->width  = frame->width;
  163.     ctx->tmp_frame->height = frame->height;
  164.  
  165.     err = ctx->video_surface_get_bits(surface, ctx->vdpau_format,
  166.                                       (void * const *)ctx->tmp_frame->data,
  167.                                       ctx->tmp_frame->linesize);
  168.     if (err != VDP_STATUS_OK) {
  169.         av_log(NULL, AV_LOG_ERROR, "Error retrieving frame data from VDPAU: %s\n",
  170.                ctx->get_error_string(err));
  171.         ret = AVERROR_UNKNOWN;
  172.         goto fail;
  173.     }
  174.  
  175.     if (ctx->vdpau_format == VDP_YCBCR_FORMAT_YV12)
  176.         FFSWAP(uint8_t*, ctx->tmp_frame->data[1], ctx->tmp_frame->data[2]);
  177.  
  178.     ret = av_frame_copy_props(ctx->tmp_frame, frame);
  179.     if (ret < 0)
  180.         goto fail;
  181.  
  182.     av_frame_unref(frame);
  183.     av_frame_move_ref(frame, ctx->tmp_frame);
  184.     return 0;
  185.  
  186. fail:
  187.     av_frame_unref(ctx->tmp_frame);
  188.     return ret;
  189. }
  190.  
  191. static const int vdpau_formats[][2] = {
  192.     { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV420P },
  193.     { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV12 },
  194.     { VDP_YCBCR_FORMAT_YUYV, AV_PIX_FMT_YUYV422 },
  195.     { VDP_YCBCR_FORMAT_UYVY, AV_PIX_FMT_UYVY422 },
  196. };
  197.  
  198. static int vdpau_alloc(AVCodecContext *s)
  199. {
  200.     InputStream  *ist = s->opaque;
  201.     int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
  202.     AVVDPAUContext *vdpau_ctx;
  203.     VDPAUContext *ctx;
  204.     const char *display, *vendor;
  205.     VdpStatus err;
  206.     int i;
  207.  
  208.     ctx = av_mallocz(sizeof(*ctx));
  209.     if (!ctx)
  210.         return AVERROR(ENOMEM);
  211.  
  212.     ist->hwaccel_ctx           = ctx;
  213.     ist->hwaccel_uninit        = vdpau_uninit;
  214.     ist->hwaccel_get_buffer    = vdpau_get_buffer;
  215.     ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  216.  
  217.     ctx->tmp_frame = av_frame_alloc();
  218.     if (!ctx->tmp_frame)
  219.         goto fail;
  220.  
  221.     ctx->dpy = XOpenDisplay(ist->hwaccel_device);
  222.     if (!ctx->dpy) {
  223.         av_log(NULL, loglevel, "Cannot open the X11 display %s.\n",
  224.                XDisplayName(ist->hwaccel_device));
  225.         goto fail;
  226.     }
  227.     display = XDisplayString(ctx->dpy);
  228.  
  229.     err = vdp_device_create_x11(ctx->dpy, XDefaultScreen(ctx->dpy), &ctx->device,
  230.                                 &ctx->get_proc_address);
  231.     if (err != VDP_STATUS_OK) {
  232.         av_log(NULL, loglevel, "VDPAU device creation on X11 display %s failed.\n",
  233.                display);
  234.         goto fail;
  235.     }
  236.  
  237. #define GET_CALLBACK(id, result)                                                \
  238. do {                                                                            \
  239.     void *tmp;                                                                  \
  240.     err = ctx->get_proc_address(ctx->device, id, &tmp);                         \
  241.     if (err != VDP_STATUS_OK) {                                                 \
  242.         av_log(NULL, loglevel, "Error getting the " #id " callback.\n");        \
  243.         goto fail;                                                              \
  244.     }                                                                           \
  245.     ctx->result = tmp;                                                          \
  246. } while (0)
  247.  
  248.     GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING,               get_error_string);
  249.     GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING,         get_information_string);
  250.     GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY,                 device_destroy);
  251.     if (vdpau_api_ver == 1) {
  252.         GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE,                 decoder_create);
  253.         GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY,                decoder_destroy);
  254.         GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER,                 decoder_render);
  255.     }
  256.     GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE,           video_surface_create);
  257.     GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY,          video_surface_destroy);
  258.     GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, video_surface_get_bits);
  259.     GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS,   video_surface_get_parameters);
  260.     GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES,
  261.                  video_surface_query);
  262.  
  263.     for (i = 0; i < FF_ARRAY_ELEMS(vdpau_formats); i++) {
  264.         VdpBool supported;
  265.         err = ctx->video_surface_query(ctx->device, VDP_CHROMA_TYPE_420,
  266.                                        vdpau_formats[i][0], &supported);
  267.         if (err != VDP_STATUS_OK) {
  268.             av_log(NULL, loglevel,
  269.                    "Error querying VDPAU surface capabilities: %s\n",
  270.                    ctx->get_error_string(err));
  271.             goto fail;
  272.         }
  273.         if (supported)
  274.             break;
  275.     }
  276.     if (i == FF_ARRAY_ELEMS(vdpau_formats)) {
  277.         av_log(NULL, loglevel,
  278.                "No supported VDPAU format for retrieving the data.\n");
  279.         return AVERROR(EINVAL);
  280.     }
  281.     ctx->vdpau_format = vdpau_formats[i][0];
  282.     ctx->pix_fmt      = vdpau_formats[i][1];
  283.  
  284.     if (vdpau_api_ver == 1) {
  285.         vdpau_ctx = av_vdpau_alloc_context();
  286.         if (!vdpau_ctx)
  287.             goto fail;
  288.         vdpau_ctx->render = ctx->decoder_render;
  289.  
  290.         s->hwaccel_context = vdpau_ctx;
  291.     } else
  292.     if (av_vdpau_bind_context(s, ctx->device, ctx->get_proc_address,
  293.                               AV_HWACCEL_FLAG_IGNORE_LEVEL))
  294.         goto fail;
  295.  
  296.     ctx->get_information_string(&vendor);
  297.     av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X11 display %s, "
  298.            "to decode input stream #%d:%d.\n", vendor,
  299.            display, ist->file_index, ist->st->index);
  300.  
  301.     return 0;
  302.  
  303. fail:
  304.     av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n",
  305.            ist->file_index, ist->st->index);
  306.     vdpau_uninit(s);
  307.     return AVERROR(EINVAL);
  308. }
  309.  
  310. static int vdpau_old_init(AVCodecContext *s)
  311. {
  312.     InputStream *ist = s->opaque;
  313.     int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
  314.     AVVDPAUContext *vdpau_ctx;
  315.     VDPAUContext *ctx;
  316.     VdpStatus err;
  317.     int profile, ret;
  318.  
  319.     if (!ist->hwaccel_ctx) {
  320.         ret = vdpau_alloc(s);
  321.         if (ret < 0)
  322.             return ret;
  323.     }
  324.     ctx       = ist->hwaccel_ctx;
  325.     vdpau_ctx = s->hwaccel_context;
  326.  
  327.     ret = av_vdpau_get_profile(s, &profile);
  328.     if (ret < 0) {
  329.         av_log(NULL, loglevel, "No known VDPAU decoder profile for this stream.\n");
  330.         return AVERROR(EINVAL);
  331.     }
  332.  
  333.     if (ctx->decoder)
  334.         ctx->decoder_destroy(ctx->decoder);
  335.  
  336.     err = ctx->decoder_create(ctx->device, profile,
  337.                               s->coded_width, s->coded_height,
  338.                               16, &ctx->decoder);
  339.     if (err != VDP_STATUS_OK) {
  340.         av_log(NULL, loglevel, "Error creating the VDPAU decoder: %s\n",
  341.                ctx->get_error_string(err));
  342.         return AVERROR_UNKNOWN;
  343.     }
  344.  
  345.     vdpau_ctx->decoder = ctx->decoder;
  346.  
  347.     ist->hwaccel_get_buffer    = vdpau_get_buffer;
  348.     ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  349.  
  350.     return 0;
  351. }
  352.  
  353. int vdpau_init(AVCodecContext *s)
  354. {
  355.     InputStream *ist = s->opaque;
  356.  
  357.     if (vdpau_api_ver == 1)
  358.         return vdpau_old_init(s);
  359.  
  360.     if (!ist->hwaccel_ctx) {
  361.         int ret = vdpau_alloc(s);
  362.         if (ret < 0)
  363.             return ret;
  364.     }
  365.  
  366.     ist->hwaccel_get_buffer    = vdpau_get_buffer;
  367.     ist->hwaccel_retrieve_data = vdpau_retrieve_data;
  368.  
  369.     return 0;
  370. }
  371.