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.

255 lines
8.3KB

  1. /*
  2. * Copyright (c) 2013 Paul B Mahol
  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. #include "libavutil/avstring.h"
  21. #include "libavutil/imgutils.h"
  22. #include "libavutil/opt.h"
  23. #include "libavutil/pixdesc.h"
  24. #include "avfilter.h"
  25. #include "internal.h"
  26. #define PLANE_R 0x01
  27. #define PLANE_G 0x02
  28. #define PLANE_B 0x04
  29. #define PLANE_A 0x08
  30. #define PLANE_Y 0x10
  31. #define PLANE_U 0x20
  32. #define PLANE_V 0x40
  33. typedef struct {
  34. const AVClass *class;
  35. int requested_planes;
  36. int map[4];
  37. int linesize[4];
  38. } ExtractPlanesContext;
  39. #define OFFSET(x) offsetof(ExtractPlanesContext, x)
  40. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  41. static const AVOption extractplanes_options[] = {
  42. { "planes", "set planes", OFFSET(requested_planes), AV_OPT_TYPE_FLAGS, {.i64=1}, 1, 0xff, FLAGS, "flags"},
  43. { "y", "set luma plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, "flags"},
  44. { "u", "set u plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, "flags"},
  45. { "v", "set v plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, "flags"},
  46. { "g", "set green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, "flags"},
  47. { "r", "set red plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, "flags"},
  48. { "b", "set blue plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, "flags"},
  49. { "a", "set alpha plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_A}, 0, 0, FLAGS, "flags"},
  50. { NULL }
  51. };
  52. AVFILTER_DEFINE_CLASS(extractplanes);
  53. static int query_formats(AVFilterContext *ctx)
  54. {
  55. static const enum AVPixelFormat in_pixfmts[] = {
  56. AV_PIX_FMT_YUV410P,
  57. AV_PIX_FMT_YUV411P,
  58. AV_PIX_FMT_YUV440P,
  59. AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
  60. AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P,
  61. AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
  62. AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
  63. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P,
  64. AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUVA420P16LE,
  65. AV_PIX_FMT_YUV420P16BE, AV_PIX_FMT_YUVA420P16BE,
  66. AV_PIX_FMT_YUV422P16LE, AV_PIX_FMT_YUVA422P16LE,
  67. AV_PIX_FMT_YUV422P16BE, AV_PIX_FMT_YUVA422P16BE,
  68. AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUVA444P16LE,
  69. AV_PIX_FMT_YUV444P16BE, AV_PIX_FMT_YUVA444P16BE,
  70. AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A,
  71. AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE,
  72. AV_PIX_FMT_GBRP,
  73. AV_PIX_FMT_GBRP16LE, AV_PIX_FMT_GBRP16BE,
  74. AV_PIX_FMT_NONE,
  75. };
  76. static const enum AVPixelFormat out8_pixfmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
  77. static const enum AVPixelFormat out16le_pixfmts[] = { AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_NONE };
  78. static const enum AVPixelFormat out16be_pixfmts[] = { AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_NONE };
  79. const enum AVPixelFormat *out_pixfmts;
  80. const AVPixFmtDescriptor *desc;
  81. AVFilterFormats *avff;
  82. int i, depth = 0, be = 0;
  83. if (!ctx->inputs[0]->in_formats ||
  84. !ctx->inputs[0]->in_formats->format_count) {
  85. return AVERROR(EAGAIN);
  86. }
  87. if (!ctx->inputs[0]->out_formats)
  88. ff_formats_ref(ff_make_format_list(in_pixfmts), &ctx->inputs[0]->out_formats);
  89. avff = ctx->inputs[0]->in_formats;
  90. desc = av_pix_fmt_desc_get(avff->formats[0]);
  91. depth = desc->comp[0].depth_minus1;
  92. be = desc->flags & PIX_FMT_BE;
  93. for (i = 1; i < avff->format_count; i++) {
  94. desc = av_pix_fmt_desc_get(avff->formats[i]);
  95. if (depth != desc->comp[0].depth_minus1 ||
  96. be != (desc->flags & PIX_FMT_BE)) {
  97. return AVERROR(EAGAIN);
  98. }
  99. }
  100. if (depth == 7)
  101. out_pixfmts = out8_pixfmts;
  102. else if (be)
  103. out_pixfmts = out16be_pixfmts;
  104. else
  105. out_pixfmts = out16le_pixfmts;
  106. for (i = 0; i < ctx->nb_outputs; i++)
  107. ff_formats_ref(ff_make_format_list(out_pixfmts), &ctx->outputs[i]->in_formats);
  108. return 0;
  109. }
  110. static int config_input(AVFilterLink *inlink)
  111. {
  112. AVFilterContext *ctx = inlink->dst;
  113. ExtractPlanesContext *e = ctx->priv;
  114. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  115. int plane_avail, ret;
  116. plane_avail = ((desc->flags & PIX_FMT_RGB) ? PLANE_R|PLANE_G|PLANE_B :
  117. PLANE_Y |
  118. ((desc->nb_components > 2) ? PLANE_U|PLANE_V : 0)) |
  119. ((desc->flags & PIX_FMT_ALPHA) ? PLANE_A : 0);
  120. if (e->requested_planes & ~plane_avail) {
  121. av_log(ctx, AV_LOG_ERROR, "Requested planes not available.\n");
  122. return AVERROR(EINVAL);
  123. }
  124. if ((ret = av_image_fill_linesizes(e->linesize, inlink->format, inlink->w)) < 0)
  125. return ret;
  126. return 0;
  127. }
  128. static int config_output(AVFilterLink *outlink)
  129. {
  130. AVFilterContext *ctx = outlink->src;
  131. AVFilterLink *inlink = ctx->inputs[0];
  132. ExtractPlanesContext *e = ctx->priv;
  133. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  134. const int output = outlink->srcpad - ctx->output_pads;
  135. if (e->map[output] == 1 || e->map[output] == 2) {
  136. outlink->h = inlink->h >> desc->log2_chroma_h;
  137. outlink->w = inlink->w >> desc->log2_chroma_w;
  138. }
  139. return 0;
  140. }
  141. static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
  142. {
  143. AVFilterContext *ctx = inlink->dst;
  144. ExtractPlanesContext *e = ctx->priv;
  145. int i, eof = 0, ret = 0;
  146. for (i = 0; i < ctx->nb_outputs; i++) {
  147. AVFilterLink *outlink = ctx->outputs[i];
  148. const int idx = e->map[i];
  149. AVFrame *out;
  150. if (outlink->closed || !frame->data[idx])
  151. continue;
  152. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  153. if (!out) {
  154. ret = AVERROR(ENOMEM);
  155. break;
  156. }
  157. av_frame_copy_props(out, frame);
  158. av_image_copy_plane(out->data[0], out->linesize[0],
  159. frame->data[idx], frame->linesize[idx],
  160. e->linesize[idx], outlink->h);
  161. ret = ff_filter_frame(outlink, out);
  162. if (ret == AVERROR_EOF)
  163. eof++;
  164. else if (ret < 0)
  165. break;
  166. }
  167. av_frame_free(&frame);
  168. if (eof == ctx->nb_outputs)
  169. ret = AVERROR_EOF;
  170. else if (ret == AVERROR_EOF)
  171. ret = 0;
  172. return ret;
  173. }
  174. static int init(AVFilterContext *ctx)
  175. {
  176. ExtractPlanesContext *e = ctx->priv;
  177. int planes = (e->requested_planes & 0xf) | (e->requested_planes >> 4);
  178. int i;
  179. for (i = 0; i < 4; i++) {
  180. char *name;
  181. AVFilterPad pad = { 0 };
  182. if (!(planes & (1 << i)))
  183. continue;
  184. name = av_asprintf("out%d", ctx->nb_outputs);
  185. if (!name)
  186. return AVERROR(ENOMEM);
  187. e->map[ctx->nb_outputs] = i;
  188. pad.name = name;
  189. pad.type = AVMEDIA_TYPE_VIDEO;
  190. pad.config_props = config_output;
  191. ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
  192. }
  193. return 0;
  194. }
  195. static void uninit(AVFilterContext *ctx)
  196. {
  197. int i;
  198. for (i = 0; i < ctx->nb_outputs; i++)
  199. av_freep(&ctx->output_pads[i].name);
  200. }
  201. static const AVFilterPad extractplanes_inputs[] = {
  202. {
  203. .name = "default",
  204. .type = AVMEDIA_TYPE_VIDEO,
  205. .filter_frame = filter_frame,
  206. .config_props = config_input,
  207. },
  208. { NULL }
  209. };
  210. AVFilter avfilter_vf_extractplanes = {
  211. .name = "extractplanes",
  212. .description = NULL_IF_CONFIG_SMALL("Extract planes as grayscale frames."),
  213. .priv_size = sizeof(ExtractPlanesContext),
  214. .priv_class = &extractplanes_class,
  215. .init = init,
  216. .uninit = uninit,
  217. .query_formats = query_formats,
  218. .inputs = extractplanes_inputs,
  219. .outputs = NULL,
  220. .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
  221. };