xv.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. * @file
  22. * XVideo output device
  23. *
  24. * TODO:
  25. * - add support to more formats
  26. * - add support to window id specification
  27. */
  28. #include <X11/Xlib.h>
  29. #include <X11/extensions/Xv.h>
  30. #include <X11/extensions/Xvlib.h>
  31. #include <X11/extensions/XShm.h>
  32. #include <sys/shm.h>
  33. #include "libavutil/opt.h"
  34. #include "libavutil/pixdesc.h"
  35. #include "avdevice.h"
  36. typedef struct {
  37. AVClass *class;
  38. GC gc;
  39. Window window;
  40. char *window_title;
  41. int window_width, window_height;
  42. int window_x, window_y;
  43. Display* display;
  44. char *display_name;
  45. XvImage* yuv_image;
  46. int image_width, image_height;
  47. XShmSegmentInfo yuv_shminfo;
  48. int xv_port;
  49. } XVContext;
  50. static int xv_write_header(AVFormatContext *s)
  51. {
  52. XVContext *xv = s->priv_data;
  53. unsigned int num_adaptors;
  54. XvAdaptorInfo *ai;
  55. XvImageFormatValues *fv;
  56. int num_formats = 0, j;
  57. AVCodecContext *encctx = s->streams[0]->codec;
  58. if ( s->nb_streams > 1
  59. || encctx->codec_type != AVMEDIA_TYPE_VIDEO
  60. || encctx->codec_id != AV_CODEC_ID_RAWVIDEO) {
  61. av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
  62. return AVERROR(EINVAL);
  63. }
  64. xv->display = XOpenDisplay(xv->display_name);
  65. if (!xv->display) {
  66. av_log(s, AV_LOG_ERROR, "Could not open the X11 display '%s'\n", xv->display_name);
  67. return AVERROR(EINVAL);
  68. }
  69. xv->image_width = encctx->width;
  70. xv->image_height = encctx->height;
  71. if (!xv->window_width && !xv->window_height) {
  72. xv->window_width = encctx->width;
  73. xv->window_height = encctx->height;
  74. }
  75. xv->window = XCreateSimpleWindow(xv->display, DefaultRootWindow(xv->display),
  76. xv->window_x, xv->window_y,
  77. xv->window_width, xv->window_height,
  78. 0, 0, 0);
  79. if (!xv->window_title) {
  80. if (!(xv->window_title = av_strdup(s->filename)))
  81. return AVERROR(ENOMEM);
  82. }
  83. XStoreName(xv->display, xv->window, xv->window_title);
  84. XMapWindow(xv->display, xv->window);
  85. if (XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &num_adaptors, &ai) != Success)
  86. return AVERROR_EXTERNAL;
  87. xv->xv_port = ai[0].base_id;
  88. if (encctx->pix_fmt != AV_PIX_FMT_YUV420P) {
  89. av_log(s, AV_LOG_ERROR,
  90. "Unsupported pixel format '%s', only yuv420p is currently supported\n",
  91. av_get_pix_fmt_name(encctx->pix_fmt));
  92. return AVERROR_PATCHWELCOME;
  93. }
  94. fv = XvListImageFormats(xv->display, xv->xv_port, &num_formats);
  95. if (!fv)
  96. return AVERROR_EXTERNAL;
  97. for (j = 0; j < num_formats; j++) {
  98. if (fv[j].id == MKTAG('I','4','2','0')) {
  99. break;
  100. }
  101. }
  102. XFree(fv);
  103. if (j >= num_formats) {
  104. av_log(s, AV_LOG_ERROR,
  105. "Device does not support pixel format yuv420p, aborting\n");
  106. return AVERROR(EINVAL);
  107. }
  108. xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
  109. xv->image_width = encctx->width;
  110. xv->image_height = encctx->height;
  111. xv->yuv_image = XvShmCreateImage(xv->display, xv->xv_port,
  112. MKTAG('I','4','2','0'), 0,
  113. xv->image_width, xv->image_height, &xv->yuv_shminfo);
  114. xv->yuv_shminfo.shmid = shmget(IPC_PRIVATE, xv->yuv_image->data_size,
  115. IPC_CREAT | 0777);
  116. xv->yuv_shminfo.shmaddr = (char *)shmat(xv->yuv_shminfo.shmid, 0, 0);
  117. xv->yuv_image->data = xv->yuv_shminfo.shmaddr;
  118. xv->yuv_shminfo.readOnly = False;
  119. XShmAttach(xv->display, &xv->yuv_shminfo);
  120. XSync(xv->display, False);
  121. shmctl(xv->yuv_shminfo.shmid, IPC_RMID, 0);
  122. return 0;
  123. }
  124. static int xv_write_packet(AVFormatContext *s, AVPacket *pkt)
  125. {
  126. XVContext *xv = s->priv_data;
  127. XvImage *img = xv->yuv_image;
  128. XWindowAttributes window_attrs;
  129. AVPicture pict;
  130. AVCodecContext *ctx = s->streams[0]->codec;
  131. int y, h;
  132. h = img->height / 2;
  133. avpicture_fill(&pict, pkt->data, ctx->pix_fmt, ctx->width, ctx->height);
  134. for (y = 0; y < img->height; y++) {
  135. memcpy(&img->data[img->offsets[0] + (y * img->pitches[0])],
  136. &pict.data[0][y * pict.linesize[0]], img->pitches[0]);
  137. }
  138. for (y = 0; y < h; ++y) {
  139. memcpy(&img->data[img->offsets[1] + (y * img->pitches[1])],
  140. &pict.data[1][y * pict.linesize[1]], img->pitches[1]);
  141. memcpy(&img->data[img->offsets[2] + (y * img->pitches[2])],
  142. &pict.data[2][y * pict.linesize[2]], img->pitches[2]);
  143. }
  144. XGetWindowAttributes(xv->display, xv->window, &window_attrs);
  145. if (XvShmPutImage(xv->display, xv->xv_port, xv->window, xv->gc,
  146. xv->yuv_image, 0, 0, xv->image_width, xv->image_height, 0, 0,
  147. window_attrs.width, window_attrs.height, True) != Success) {
  148. av_log(s, AV_LOG_ERROR, "Could not copy image to XV shared memory buffer\n");
  149. return AVERROR_EXTERNAL;
  150. }
  151. return 0;
  152. }
  153. static int xv_write_trailer(AVFormatContext *s)
  154. {
  155. XVContext *xv = s->priv_data;
  156. XShmDetach(xv->display, &xv->yuv_shminfo);
  157. shmdt(xv->yuv_image->data);
  158. XFree(xv->yuv_image);
  159. XCloseDisplay(xv->display);
  160. return 0;
  161. }
  162. #define OFFSET(x) offsetof(XVContext, x)
  163. static const AVOption options[] = {
  164. { "display_name", "set display name", OFFSET(display_name), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  165. { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  166. { "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
  167. { "window_x", "set window x offset", OFFSET(window_x), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  168. { "window_y", "set window y offset", OFFSET(window_y), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  169. { NULL }
  170. };
  171. static const AVClass xv_class = {
  172. .class_name = "xvideo outdev",
  173. .item_name = av_default_item_name,
  174. .option = options,
  175. .version = LIBAVUTIL_VERSION_INT,
  176. };
  177. AVOutputFormat ff_xv_muxer = {
  178. .name = "xv",
  179. .long_name = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"),
  180. .priv_data_size = sizeof(XVContext),
  181. .audio_codec = AV_CODEC_ID_NONE,
  182. .video_codec = AV_CODEC_ID_RAWVIDEO,
  183. .write_header = xv_write_header,
  184. .write_packet = xv_write_packet,
  185. .write_trailer = xv_write_trailer,
  186. .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
  187. .priv_class = &xv_class,
  188. };