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.

284 lines
8.4KB

  1. /*
  2. * Copyright (C) 2012 Michael Niedermayer <michaelni@gmx.at>
  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/cpu.h"
  21. #include "libavutil/common.h"
  22. #include "libavutil/pixdesc.h"
  23. #include "avfilter.h"
  24. #undef NDEBUG
  25. #include <assert.h>
  26. typedef struct {
  27. float interlace_threshold;
  28. float progressive_threshold;
  29. int stat_tff;
  30. int stat_bff;
  31. int stat_progressive;
  32. int stat_undetermined;
  33. AVFilterBufferRef *cur;
  34. AVFilterBufferRef *next;
  35. AVFilterBufferRef *prev;
  36. AVFilterBufferRef *out;
  37. int (*filter_line)(uint8_t *prev, uint8_t *cur, uint8_t *next, int w);
  38. const AVPixFmtDescriptor *csp;
  39. } IDETContext;
  40. static int filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
  41. {
  42. int x;
  43. int ret=0;
  44. for(x=0; x<w; x++){
  45. ret += FFABS((*a++ + *c++) - 2 * *b++);
  46. }
  47. return ret;
  48. }
  49. static int filter_line_c_16bit(const uint16_t *a, const uint16_t *b, const uint16_t *c, int w)
  50. {
  51. int x;
  52. int ret=0;
  53. for(x=0; x<w; x++){
  54. ret += FFABS((*a++ + *c++) - 2 * *b++);
  55. }
  56. return ret;
  57. }
  58. static void filter(AVFilterContext *ctx)
  59. {
  60. IDETContext *idet = ctx->priv;
  61. int y, i;
  62. int64_t alpha[2]={0};
  63. int64_t delta=0;
  64. for (i = 0; i < idet->csp->nb_components; i++) {
  65. int w = idet->cur->video->w;
  66. int h = idet->cur->video->h;
  67. int refs = idet->cur->linesize[i];
  68. int df = (idet->csp->comp[i].depth_minus1 + 8) / 8;
  69. if (i && i<3) {
  70. w >>= idet->csp->log2_chroma_w;
  71. h >>= idet->csp->log2_chroma_h;
  72. }
  73. for (y = 2; y < h - 2; y++) {
  74. uint8_t *prev = &idet->prev->data[i][y*refs];
  75. uint8_t *cur = &idet->cur ->data[i][y*refs];
  76. uint8_t *next = &idet->next->data[i][y*refs];
  77. alpha[ y &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
  78. alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
  79. delta += idet->filter_line(cur-refs, cur, cur+refs, w);
  80. }
  81. }
  82. #if HAVE_MMX
  83. __asm__ volatile("emms \n\t" : : : "memory");
  84. #endif
  85. if (alpha[0] / (float)alpha[1] > idet->interlace_threshold){
  86. av_log(ctx, AV_LOG_INFO, "Interlaced, top field first\n");
  87. idet->stat_tff++;
  88. idet->cur->video->top_field_first = 1;
  89. idet->cur->video->interlaced = 1;
  90. }else if(alpha[1] / (float)alpha[0] > idet->interlace_threshold){
  91. av_log(ctx, AV_LOG_INFO, "Interlaced, bottom field first\n");
  92. idet->stat_bff++;
  93. idet->cur->video->top_field_first = 0;
  94. idet->cur->video->interlaced = 1;
  95. }else if(alpha[1] / (float)delta > idet->progressive_threshold){
  96. av_log(ctx, AV_LOG_INFO, "Progressive\n");
  97. idet->stat_progressive++;
  98. idet->cur->video->interlaced = 0;
  99. }else{
  100. av_log(ctx, AV_LOG_INFO, "Undetermined\n");
  101. idet->stat_undetermined++;
  102. idet->cur->video->interlaced = idet->prev->video->interlaced;
  103. idet->cur->video->top_field_first = idet->prev->video->top_field_first;
  104. }
  105. }
  106. static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
  107. {
  108. AVFilterContext *ctx = link->dst;
  109. IDETContext *idet = ctx->priv;
  110. if (idet->prev)
  111. avfilter_unref_buffer(idet->prev);
  112. idet->prev = idet->cur;
  113. idet->cur = idet->next;
  114. idet->next = picref;
  115. if (!idet->cur)
  116. return;
  117. if (!idet->prev)
  118. idet->prev = avfilter_ref_buffer(idet->cur, AV_PERM_READ);
  119. avfilter_start_frame(ctx->outputs[0], avfilter_ref_buffer(idet->cur, AV_PERM_READ));
  120. }
  121. static void end_frame(AVFilterLink *link)
  122. {
  123. AVFilterContext *ctx = link->dst;
  124. IDETContext *idet = ctx->priv;
  125. if (!idet->cur)
  126. return;
  127. if (!idet->csp)
  128. idet->csp = &av_pix_fmt_descriptors[link->format];
  129. if (idet->csp->comp[0].depth_minus1 / 8 == 1)
  130. idet->filter_line = (void*)filter_line_c_16bit;
  131. filter(ctx);
  132. avfilter_draw_slice(ctx->outputs[0], 0, link->h, 1);
  133. avfilter_end_frame(ctx->outputs[0]);
  134. }
  135. static int request_frame(AVFilterLink *link)
  136. {
  137. AVFilterContext *ctx = link->src;
  138. IDETContext *idet = ctx->priv;
  139. do {
  140. int ret;
  141. if ((ret = avfilter_request_frame(link->src->inputs[0])))
  142. return ret;
  143. } while (!idet->cur);
  144. return 0;
  145. }
  146. static int poll_frame(AVFilterLink *link)
  147. {
  148. IDETContext *idet = link->src->priv;
  149. int ret, val;
  150. val = avfilter_poll_frame(link->src->inputs[0]);
  151. if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape
  152. if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0)
  153. return ret;
  154. val = avfilter_poll_frame(link->src->inputs[0]);
  155. }
  156. assert(idet->next || !val);
  157. return val;
  158. }
  159. static av_cold void uninit(AVFilterContext *ctx)
  160. {
  161. IDETContext *idet = ctx->priv;
  162. av_log(ctx, AV_LOG_INFO, "TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
  163. idet->stat_tff,
  164. idet->stat_bff,
  165. idet->stat_progressive,
  166. idet->stat_undetermined
  167. );
  168. if (idet->prev) avfilter_unref_buffer(idet->prev);
  169. if (idet->cur ) avfilter_unref_buffer(idet->cur );
  170. if (idet->next) avfilter_unref_buffer(idet->next);
  171. }
  172. static int query_formats(AVFilterContext *ctx)
  173. {
  174. static const enum PixelFormat pix_fmts[] = {
  175. PIX_FMT_YUV420P,
  176. PIX_FMT_YUV422P,
  177. PIX_FMT_YUV444P,
  178. PIX_FMT_YUV410P,
  179. PIX_FMT_YUV411P,
  180. PIX_FMT_GRAY8,
  181. PIX_FMT_YUVJ420P,
  182. PIX_FMT_YUVJ422P,
  183. PIX_FMT_YUVJ444P,
  184. AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
  185. PIX_FMT_YUV440P,
  186. PIX_FMT_YUVJ440P,
  187. AV_NE( PIX_FMT_YUV420P10BE, PIX_FMT_YUV420P10LE ),
  188. AV_NE( PIX_FMT_YUV422P10BE, PIX_FMT_YUV422P10LE ),
  189. AV_NE( PIX_FMT_YUV444P10BE, PIX_FMT_YUV444P10LE ),
  190. AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
  191. AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
  192. AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
  193. PIX_FMT_YUVA420P,
  194. PIX_FMT_NONE
  195. };
  196. avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
  197. return 0;
  198. }
  199. static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
  200. {
  201. IDETContext *idet = ctx->priv;
  202. int cpu_flags = av_get_cpu_flags();
  203. idet->csp = NULL;
  204. idet->interlace_threshold = 1.01;
  205. idet->progressive_threshold = 2.5;
  206. if (args) sscanf(args, "%f:%f", &idet->interlace_threshold, &idet->progressive_threshold);
  207. idet->filter_line = filter_line_c;
  208. return 0;
  209. }
  210. static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
  211. AVFilter avfilter_vf_idet = {
  212. .name = "idet",
  213. .description = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
  214. .priv_size = sizeof(IDETContext),
  215. .init = init,
  216. .uninit = uninit,
  217. .query_formats = query_formats,
  218. .inputs = (const AVFilterPad[]) {{ .name = "default",
  219. .type = AVMEDIA_TYPE_VIDEO,
  220. .start_frame = start_frame,
  221. .draw_slice = null_draw_slice,
  222. .end_frame = end_frame,
  223. .rej_perms = AV_PERM_REUSE2, },
  224. { .name = NULL}},
  225. .outputs = (const AVFilterPad[]) {{ .name = "default",
  226. .type = AVMEDIA_TYPE_VIDEO,
  227. .poll_frame = poll_frame,
  228. .request_frame = request_frame, },
  229. { .name = NULL}},
  230. };