vf_framepack.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /*
  2. * Copyright (c) 2013 Vittorio Giovara
  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. * Generate a frame packed video, by combining two views in a single surface.
  23. */
  24. #include <string.h>
  25. #include "libavutil/imgutils.h"
  26. #include "libavutil/opt.h"
  27. #include "libavutil/pixdesc.h"
  28. #include "libavutil/rational.h"
  29. #include "libavutil/stereo3d.h"
  30. #include "avfilter.h"
  31. #include "formats.h"
  32. #include "internal.h"
  33. #include "video.h"
  34. #define LEFT 0
  35. #define RIGHT 1
  36. typedef struct FramepackContext {
  37. const AVClass *class;
  38. const AVPixFmtDescriptor *pix_desc; ///< agreed pixel format
  39. enum AVStereo3DType format; ///< frame pack type output
  40. AVFrame *input_views[2]; ///< input frames
  41. int64_t double_pts; ///< new pts for frameseq mode
  42. } FramepackContext;
  43. static const enum AVPixelFormat formats_supported[] = {
  44. AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
  45. AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ420P,
  46. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
  47. AV_PIX_FMT_NONE
  48. };
  49. static int query_formats(AVFilterContext *ctx)
  50. {
  51. // this will ensure that formats are the same on all pads
  52. ff_set_common_formats(ctx, ff_make_format_list(formats_supported));
  53. return 0;
  54. }
  55. static av_cold void framepack_uninit(AVFilterContext *ctx)
  56. {
  57. FramepackContext *s = ctx->priv;
  58. // clean any leftover frame
  59. av_frame_free(&s->input_views[LEFT]);
  60. av_frame_free(&s->input_views[RIGHT]);
  61. }
  62. static int config_output(AVFilterLink *outlink)
  63. {
  64. AVFilterContext *ctx = outlink->src;
  65. FramepackContext *s = outlink->src->priv;
  66. int width = ctx->inputs[LEFT]->w;
  67. int height = ctx->inputs[LEFT]->h;
  68. AVRational time_base = ctx->inputs[LEFT]->time_base;
  69. AVRational frame_rate = ctx->inputs[LEFT]->frame_rate;
  70. // check size and fps match on the other input
  71. if (width != ctx->inputs[RIGHT]->w ||
  72. height != ctx->inputs[RIGHT]->h) {
  73. av_log(ctx, AV_LOG_ERROR,
  74. "Left and right sizes differ (%dx%d vs %dx%d).\n",
  75. width, height,
  76. ctx->inputs[RIGHT]->w, ctx->inputs[RIGHT]->h);
  77. return AVERROR_INVALIDDATA;
  78. } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
  79. av_log(ctx, AV_LOG_ERROR,
  80. "Left and right time bases differ (%d/%d vs %d/%d).\n",
  81. time_base.num, time_base.den,
  82. ctx->inputs[RIGHT]->time_base.num,
  83. ctx->inputs[RIGHT]->time_base.den);
  84. return AVERROR_INVALIDDATA;
  85. } else if (av_cmp_q(frame_rate, ctx->inputs[RIGHT]->frame_rate) != 0) {
  86. av_log(ctx, AV_LOG_ERROR,
  87. "Left and right framerates differ (%d/%d vs %d/%d).\n",
  88. frame_rate.num, frame_rate.den,
  89. ctx->inputs[RIGHT]->frame_rate.num,
  90. ctx->inputs[RIGHT]->frame_rate.den);
  91. return AVERROR_INVALIDDATA;
  92. }
  93. s->pix_desc = av_pix_fmt_desc_get(outlink->format);
  94. if (!s->pix_desc)
  95. return AVERROR_BUG;
  96. // modify output properties as needed
  97. switch (s->format) {
  98. case AV_STEREO3D_FRAMESEQUENCE:
  99. time_base.den *= 2;
  100. frame_rate.num *= 2;
  101. s->double_pts = AV_NOPTS_VALUE;
  102. break;
  103. case AV_STEREO3D_COLUMNS:
  104. case AV_STEREO3D_SIDEBYSIDE:
  105. width *= 2;
  106. break;
  107. case AV_STEREO3D_LINES:
  108. case AV_STEREO3D_TOPBOTTOM:
  109. height *= 2;
  110. break;
  111. default:
  112. av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.");
  113. return AVERROR_INVALIDDATA;
  114. }
  115. outlink->w = width;
  116. outlink->h = height;
  117. outlink->time_base = time_base;
  118. outlink->frame_rate= frame_rate;
  119. return 0;
  120. }
  121. static void horizontal_frame_pack(FramepackContext *s,
  122. AVFrame *dst,
  123. int interleaved)
  124. {
  125. int plane, i;
  126. int length = dst->width / 2;
  127. int lines = dst->height;
  128. for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
  129. const uint8_t *leftp = s->input_views[LEFT]->data[plane];
  130. const uint8_t *rightp = s->input_views[RIGHT]->data[plane];
  131. uint8_t *dstp = dst->data[plane];
  132. if (plane == 1 || plane == 2) {
  133. length = -(-(dst->width / 2) >> s->pix_desc->log2_chroma_w);
  134. lines = -(-(dst->height) >> s->pix_desc->log2_chroma_h);
  135. }
  136. if (interleaved) {
  137. for (i = 0; i < lines; i++) {
  138. int j;
  139. int k = 0;
  140. for (j = 0; j < length; j++) {
  141. dstp[k++] = leftp[j];
  142. dstp[k++] = rightp[j];
  143. }
  144. dstp += dst->linesize[plane];
  145. leftp += s->input_views[LEFT]->linesize[plane];
  146. rightp += s->input_views[RIGHT]->linesize[plane];
  147. }
  148. } else {
  149. av_image_copy_plane(dst->data[plane], dst->linesize[plane],
  150. leftp, s->input_views[LEFT]->linesize[plane],
  151. length, lines);
  152. av_image_copy_plane(dst->data[plane] + length, dst->linesize[plane],
  153. rightp, s->input_views[RIGHT]->linesize[plane],
  154. length, lines);
  155. }
  156. }
  157. }
  158. static void vertical_frame_pack(FramepackContext *s,
  159. AVFrame *dst,
  160. int interleaved)
  161. {
  162. int plane, offset;
  163. int length = dst->width;
  164. int lines = dst->height / 2;
  165. for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
  166. if (plane == 1 || plane == 2) {
  167. length = -(-(dst->width) >> s->pix_desc->log2_chroma_w);
  168. lines = -(-(dst->height / 2) >> s->pix_desc->log2_chroma_h);
  169. }
  170. offset = interleaved ? dst->linesize[plane] : dst->linesize[plane] * lines;
  171. av_image_copy_plane(dst->data[plane],
  172. dst->linesize[plane] << interleaved,
  173. s->input_views[LEFT]->data[plane],
  174. s->input_views[LEFT]->linesize[plane],
  175. length, lines);
  176. av_image_copy_plane(dst->data[plane] + offset,
  177. dst->linesize[plane] << interleaved,
  178. s->input_views[RIGHT]->data[plane],
  179. s->input_views[RIGHT]->linesize[plane],
  180. length, lines);
  181. }
  182. }
  183. static av_always_inline void spatial_frame_pack(FramepackContext *s, AVFrame *dst)
  184. {
  185. switch (s->format) {
  186. case AV_STEREO3D_SIDEBYSIDE:
  187. horizontal_frame_pack(s, dst, 0);
  188. break;
  189. case AV_STEREO3D_COLUMNS:
  190. horizontal_frame_pack(s, dst, 1);
  191. break;
  192. case AV_STEREO3D_TOPBOTTOM:
  193. vertical_frame_pack(s, dst, 0);
  194. break;
  195. case AV_STEREO3D_LINES:
  196. vertical_frame_pack(s, dst, 1);
  197. break;
  198. }
  199. }
  200. static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
  201. {
  202. FramepackContext *s = inlink->dst->priv;
  203. s->input_views[LEFT] = frame;
  204. return 0;
  205. }
  206. static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
  207. {
  208. FramepackContext *s = inlink->dst->priv;
  209. s->input_views[RIGHT] = frame;
  210. return 0;
  211. }
  212. static int request_frame(AVFilterLink *outlink)
  213. {
  214. AVFilterContext *ctx = outlink->src;
  215. FramepackContext *s = ctx->priv;
  216. AVStereo3D *stereo;
  217. int ret, i;
  218. /* get a frame on the either input, stop as soon as a video ends */
  219. for (i = 0; i < 2; i++) {
  220. if (!s->input_views[i]) {
  221. ret = ff_request_frame(ctx->inputs[i]);
  222. if (ret < 0)
  223. return ret;
  224. }
  225. }
  226. if (s->format == AV_STEREO3D_FRAMESEQUENCE) {
  227. if (s->double_pts == AV_NOPTS_VALUE)
  228. s->double_pts = s->input_views[LEFT]->pts;
  229. for (i = 0; i < 2; i++) {
  230. // set correct timestamps
  231. s->input_views[i]->pts = s->double_pts++;
  232. // set stereo3d side data
  233. stereo = av_stereo3d_create_side_data(s->input_views[i]);
  234. if (!stereo)
  235. return AVERROR(ENOMEM);
  236. stereo->type = s->format;
  237. // filter the frame and immediately relinquish its pointer
  238. ret = ff_filter_frame(outlink, s->input_views[i]);
  239. s->input_views[i] = NULL;
  240. if (ret < 0)
  241. return ret;
  242. }
  243. return ret;
  244. } else {
  245. AVFrame *dst = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  246. if (!dst)
  247. return AVERROR(ENOMEM);
  248. spatial_frame_pack(s, dst);
  249. // get any property from the original frame
  250. ret = av_frame_copy_props(dst, s->input_views[LEFT]);
  251. if (ret < 0) {
  252. av_frame_free(&dst);
  253. return ret;
  254. }
  255. for (i = 0; i < 2; i++)
  256. av_frame_free(&s->input_views[i]);
  257. // set stereo3d side data
  258. stereo = av_stereo3d_create_side_data(dst);
  259. if (!stereo) {
  260. av_frame_free(&dst);
  261. return AVERROR(ENOMEM);
  262. }
  263. stereo->type = s->format;
  264. return ff_filter_frame(outlink, dst);
  265. }
  266. }
  267. #define OFFSET(x) offsetof(FramepackContext, x)
  268. #define V AV_OPT_FLAG_VIDEO_PARAM
  269. static const AVOption options[] = {
  270. { "format", "Frame pack output format", OFFSET(format), AV_OPT_TYPE_INT,
  271. { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = V, .unit = "format" },
  272. { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST,
  273. { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
  274. { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST,
  275. { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
  276. { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST,
  277. { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
  278. { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST,
  279. { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
  280. { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST,
  281. { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
  282. { NULL },
  283. };
  284. static const AVClass framepack_class = {
  285. .class_name = "framepack",
  286. .item_name = av_default_item_name,
  287. .option = options,
  288. .version = LIBAVUTIL_VERSION_INT,
  289. };
  290. static const AVFilterPad framepack_inputs[] = {
  291. {
  292. .name = "left",
  293. .type = AVMEDIA_TYPE_VIDEO,
  294. .filter_frame = filter_frame_left,
  295. .needs_fifo = 1,
  296. },
  297. {
  298. .name = "right",
  299. .type = AVMEDIA_TYPE_VIDEO,
  300. .filter_frame = filter_frame_right,
  301. .needs_fifo = 1,
  302. },
  303. { NULL }
  304. };
  305. static const AVFilterPad framepack_outputs[] = {
  306. {
  307. .name = "packed",
  308. .type = AVMEDIA_TYPE_VIDEO,
  309. .config_props = config_output,
  310. .request_frame = request_frame,
  311. },
  312. { NULL }
  313. };
  314. AVFilter ff_vf_framepack = {
  315. .name = "framepack",
  316. .description = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
  317. .priv_size = sizeof(FramepackContext),
  318. .priv_class = &framepack_class,
  319. .query_formats = query_formats,
  320. .inputs = framepack_inputs,
  321. .outputs = framepack_outputs,
  322. .uninit = framepack_uninit,
  323. };