Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2013 Jeff Moguillansky
  3.  *
  4.  * This file is part of FFmpeg.
  5.  *
  6.  * FFmpeg is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * FFmpeg is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with FFmpeg; if not, write to the Free Software
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19.  */
  20.  
  21. /**
  22.  * @file
  23.  * XVideo output device
  24.  *
  25.  * TODO:
  26.  * - add support to more formats
  27.  * - add support to window id specification
  28.  */
  29.  
  30. #include <X11/Xlib.h>
  31. #include <X11/extensions/Xv.h>
  32. #include <X11/extensions/XShm.h>
  33. #include <X11/extensions/Xvlib.h>
  34. #include <sys/shm.h>
  35.  
  36. #include "libavutil/opt.h"
  37. #include "libavutil/pixdesc.h"
  38. #include "avdevice.h"
  39.  
  40. typedef struct {
  41.     AVClass *class;
  42.     GC gc;
  43.  
  44.     Window window;
  45.     char *window_title;
  46.     int window_width, window_height;
  47.     int window_x, window_y;
  48.  
  49.     Display* display;
  50.     char *display_name;
  51.  
  52.     XvImage* yuv_image;
  53.     int image_width, image_height;
  54.     XShmSegmentInfo yuv_shminfo;
  55.     int xv_port;
  56. } XVContext;
  57.  
  58. static int xv_write_header(AVFormatContext *s)
  59. {
  60.     XVContext *xv = s->priv_data;
  61.     unsigned int num_adaptors;
  62.     XvAdaptorInfo *ai;
  63.     XvImageFormatValues *fv;
  64.     int num_formats = 0, j;
  65.     AVCodecContext *encctx = s->streams[0]->codec;
  66.  
  67.     if (   s->nb_streams > 1
  68.         || encctx->codec_type != AVMEDIA_TYPE_VIDEO
  69.         || encctx->codec_id   != AV_CODEC_ID_RAWVIDEO) {
  70.         av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
  71.         return AVERROR(EINVAL);
  72.     }
  73.  
  74.     xv->display = XOpenDisplay(xv->display_name);
  75.     if (!xv->display) {
  76.         av_log(s, AV_LOG_ERROR, "Could not open the X11 display '%s'\n", xv->display_name);
  77.         return AVERROR(EINVAL);
  78.     }
  79.  
  80.     xv->image_width  = encctx->width;
  81.     xv->image_height = encctx->height;
  82.     if (!xv->window_width && !xv->window_height) {
  83.         xv->window_width  = encctx->width;
  84.         xv->window_height = encctx->height;
  85.     }
  86.     xv->window = XCreateSimpleWindow(xv->display, DefaultRootWindow(xv->display),
  87.                                      xv->window_x, xv->window_y,
  88.                                      xv->window_width, xv->window_height,
  89.                                      0, 0, 0);
  90.     if (!xv->window_title) {
  91.         if (!(xv->window_title = av_strdup(s->filename)))
  92.             return AVERROR(ENOMEM);
  93.     }
  94.     XStoreName(xv->display, xv->window, xv->window_title);
  95.     XMapWindow(xv->display, xv->window);
  96.  
  97.     if (XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &num_adaptors, &ai) != Success)
  98.         return AVERROR_EXTERNAL;
  99.     xv->xv_port = ai[0].base_id;
  100.  
  101.     if (encctx->pix_fmt != AV_PIX_FMT_YUV420P) {
  102.         av_log(s, AV_LOG_ERROR,
  103.                "Unsupported pixel format '%s', only yuv420p is currently supported\n",
  104.                av_get_pix_fmt_name(encctx->pix_fmt));
  105.         return AVERROR_PATCHWELCOME;
  106.     }
  107.  
  108.     fv = XvListImageFormats(xv->display, xv->xv_port, &num_formats);
  109.     if (!fv)
  110.         return AVERROR_EXTERNAL;
  111.     for (j = 0; j < num_formats; j++) {
  112.         if (fv[j].id == MKTAG('I','4','2','0')) {
  113.             break;
  114.         }
  115.     }
  116.     XFree(fv);
  117.  
  118.     if (j >= num_formats) {
  119.         av_log(s, AV_LOG_ERROR,
  120.                "Device does not support pixel format yuv420p, aborting\n");
  121.         return AVERROR(EINVAL);
  122.     }
  123.  
  124.     xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
  125.     xv->image_width  = encctx->width;
  126.     xv->image_height = encctx->height;
  127.     xv->yuv_image = XvShmCreateImage(xv->display, xv->xv_port,
  128.                                      MKTAG('I','4','2','0'), 0,
  129.                                      xv->image_width, xv->image_height, &xv->yuv_shminfo);
  130.     xv->yuv_shminfo.shmid = shmget(IPC_PRIVATE, xv->yuv_image->data_size,
  131.                                    IPC_CREAT | 0777);
  132.     xv->yuv_shminfo.shmaddr = (char *)shmat(xv->yuv_shminfo.shmid, 0, 0);
  133.     xv->yuv_image->data = xv->yuv_shminfo.shmaddr;
  134.     xv->yuv_shminfo.readOnly = False;
  135.  
  136.     XShmAttach(xv->display, &xv->yuv_shminfo);
  137.     XSync(xv->display, False);
  138.     shmctl(xv->yuv_shminfo.shmid, IPC_RMID, 0);
  139.  
  140.     return 0;
  141. }
  142.  
  143. static int xv_write_packet(AVFormatContext *s, AVPacket *pkt)
  144. {
  145.     XVContext *xv = s->priv_data;
  146.     XvImage *img = xv->yuv_image;
  147.     XWindowAttributes window_attrs;
  148.     AVPicture pict;
  149.     AVCodecContext *ctx = s->streams[0]->codec;
  150.     int y, h;
  151.  
  152.     h = img->height / 2;
  153.  
  154.     avpicture_fill(&pict, pkt->data, ctx->pix_fmt, ctx->width, ctx->height);
  155.     for (y = 0; y < img->height; y++) {
  156.         memcpy(&img->data[img->offsets[0] + (y * img->pitches[0])],
  157.                &pict.data[0][y * pict.linesize[0]], img->pitches[0]);
  158.     }
  159.  
  160.     for (y = 0; y < h; ++y) {
  161.         memcpy(&img->data[img->offsets[1] + (y * img->pitches[1])],
  162.                &pict.data[1][y * pict.linesize[1]], img->pitches[1]);
  163.         memcpy(&img->data[img->offsets[2] + (y * img->pitches[2])],
  164.                &pict.data[2][y * pict.linesize[2]], img->pitches[2]);
  165.     }
  166.  
  167.     XGetWindowAttributes(xv->display, xv->window, &window_attrs);
  168.     if (XvShmPutImage(xv->display, xv->xv_port, xv->window, xv->gc,
  169.                       xv->yuv_image, 0, 0, xv->image_width, xv->image_height, 0, 0,
  170.                       window_attrs.width, window_attrs.height, True) != Success) {
  171.         av_log(s, AV_LOG_ERROR, "Could not copy image to XV shared memory buffer\n");
  172.         return AVERROR_EXTERNAL;
  173.     }
  174.     return 0;
  175. }
  176.  
  177. static int xv_write_trailer(AVFormatContext *s)
  178. {
  179.     XVContext *xv = s->priv_data;
  180.  
  181.     XShmDetach(xv->display, &xv->yuv_shminfo);
  182.     shmdt(xv->yuv_image->data);
  183.     XFree(xv->yuv_image);
  184.     XCloseDisplay(xv->display);
  185.     return 0;
  186. }
  187.  
  188. #define OFFSET(x) offsetof(XVContext, x)
  189. static const AVOption options[] = {
  190.     { "display_name", "set display name",       OFFSET(display_name), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  191.     { "window_size",  "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  192.     { "window_title", "set window title",       OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  193.     { "window_x",     "set window x offset",    OFFSET(window_x),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  194.     { "window_y",     "set window y offset",    OFFSET(window_y),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  195.     { NULL }
  196.  
  197. };
  198.  
  199. static const AVClass xv_class = {
  200.     .class_name = "xvideo outdev",
  201.     .item_name  = av_default_item_name,
  202.     .option     = options,
  203.     .version    = LIBAVUTIL_VERSION_INT,
  204. };
  205.  
  206. AVOutputFormat ff_xv_muxer = {
  207.     .name           = "xv",
  208.     .long_name      = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"),
  209.     .priv_data_size = sizeof(XVContext),
  210.     .audio_codec    = AV_CODEC_ID_NONE,
  211.     .video_codec    = AV_CODEC_ID_RAWVIDEO,
  212.     .write_header   = xv_write_header,
  213.     .write_packet   = xv_write_packet,
  214.     .write_trailer  = xv_write_trailer,
  215.     .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
  216.     .priv_class     = &xv_class,
  217. };
  218.