--- /dev/null
+#include "../../../StdAfx.h"\r
+\r
+#include "scalable_yadif.h"\r
+\r
+\r
+/*
+ * Copyright (C) 2006-2010 Michael Niedermayer <michaelni@gmx.at>
+ * 2010 James Darnley <james.darnley@gmail.com>
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+\r
+#pragma warning(push, 0)
+
+
+#undef NDEBUG
+#include <assert.h>
+
+typedef struct {
+ /**
+ * 0: send 1 frame for each frame
+ * 1: send 1 frame for each field
+ * 2: like 0 but skips spatial interlacing check
+ * 3: like 1 but skips spatial interlacing check
+ */
+ int mode;
+
+ /**
+ * 0: bottom field first
+ * 1: top field first
+ * -1: auto-detection
+ */
+ int parity;
+
+ int frame_pending;
+
+ /**
+ * 0: deinterlace all frames
+ * 1: only deinterlace frames marked as interlaced
+ */
+ int auto_enable;
+
+ AVFilterBufferRef *cur;
+ AVFilterBufferRef *next;
+ AVFilterBufferRef *prev;
+ AVFilterBufferRef *out;
+ void (*filter_line)(uint8_t *dst,
+ uint8_t *prev, uint8_t *cur, uint8_t *next,
+ int w, int prefs, int mrefs, int parity, int mode);
+
+ const AVPixFmtDescriptor *csp;
+} YADIFContext;
+
+#define CHECK(j)\
+ { int score = FFABS(cur[mrefs-1+(j)] - cur[prefs-1-(j)])\
+ + FFABS(cur[mrefs +(j)] - cur[prefs -(j)])\
+ + FFABS(cur[mrefs+1+(j)] - cur[prefs+1-(j)]);\
+ if (score < spatial_score) {\
+ spatial_score= score;\
+ spatial_pred= (cur[mrefs +(j)] + cur[prefs -(j)])>>1;\
+
+#define FILTER \
+ for (x = 0; x < w; x++) { \
+ int c = cur[mrefs]; \
+ int d = (prev2[0] + next2[0])>>1; \
+ int e = cur[prefs]; \
+ int temporal_diff0 = FFABS(prev2[0] - next2[0]); \
+ int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e) )>>1; \
+ int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e) )>>1; \
+ int diff = FFMAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); \
+ int spatial_pred = (c+e)>>1; \
+ int spatial_score = FFABS(cur[mrefs-1] - cur[prefs-1]) + FFABS(c-e) \
+ + FFABS(cur[mrefs+1] - cur[prefs+1]) - 1; \
+ \
+ CHECK(-1) CHECK(-2) }} }} \
+ CHECK( 1) CHECK( 2) }} }} \
+ \
+ if (mode < 2) { \
+ int b = (prev2[2*mrefs] + next2[2*mrefs])>>1; \
+ int f = (prev2[2*prefs] + next2[2*prefs])>>1; \
+ int max = FFMAX3(d-e, d-c, FFMIN(b-c, f-e)); \
+ int min = FFMIN3(d-e, d-c, FFMAX(b-c, f-e)); \
+ \
+ diff = FFMAX3(diff, min, -max); \
+ } \
+ \
+ if (spatial_pred > d + diff) \
+ spatial_pred = d + diff; \
+ else if (spatial_pred < d - diff) \
+ spatial_pred = d - diff; \
+ \
+ dst[0] = spatial_pred; \
+ \
+ dst++; \
+ cur++; \
+ prev++; \
+ next++; \
+ prev2++; \
+ next2++; \
+ }
+
+void filter_line_c(uint8_t *dst,
+ uint8_t *prev, uint8_t *cur, uint8_t *next,
+ int w, int prefs, int mrefs, int parity, int mode)
+{
+ int x;
+ uint8_t *prev2 = parity ? prev : cur ;
+ uint8_t *next2 = parity ? cur : next;
+
+ FILTER
+}
+
+void filter_line_c_16bit(uint16_t *dst,
+ uint16_t *prev, uint16_t *cur, uint16_t *next,
+ int w, int prefs, int mrefs, int parity, int mode)
+{
+ int x;
+ uint16_t *prev2 = parity ? prev : cur ;
+ uint16_t *next2 = parity ? cur : next;
+ mrefs /= 2;
+ prefs /= 2;
+
+ FILTER
+}
+
+void filter(AVFilterContext *ctx, AVFilterBufferRef *dstpic,
+ int parity, int tff)
+{
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+ int y, i;
+
+ for (i = 0; i < yadif->csp->nb_components; i++) {
+ int w = dstpic->video->w;
+ int h = dstpic->video->h;
+ int refs = yadif->cur->linesize[i];
+ int df = (yadif->csp->comp[i].depth_minus1+1) / 8;
+
+ if (i) {
+ /* Why is this not part of the per-plane description thing? */
+ w >>= yadif->csp->log2_chroma_w;
+ h >>= yadif->csp->log2_chroma_h;
+ }
+
+ for (y = 0; y < h; y++) {
+ if ((y ^ parity) & 1) {
+ uint8_t *prev = &yadif->prev->data[i][y*refs];
+ uint8_t *cur = &yadif->cur ->data[i][y*refs];
+ uint8_t *next = &yadif->next->data[i][y*refs];
+ uint8_t *dst = &dstpic->data[i][y*dstpic->linesize[i]];
+ int mode = y==1 || y+2==h ? 2 : yadif->mode;
+ yadif->filter_line(dst, prev, cur, next, w, y+1<h ? refs : -refs, y ? -refs : refs, parity ^ tff, mode);
+ } else {
+ memcpy(&dstpic->data[i][y*dstpic->linesize[i]],
+ &yadif->cur->data[i][y*refs], w*df);
+ }
+ }
+ }
+ //__asm__ volatile("emms \n\t" : : : "memory");
+}
+
+AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms, int w, int h)
+{
+ AVFilterBufferRef *picref;
+ int width = FFALIGN(w, 32);
+ int height= FFALIGN(h+2, 32);
+ int i;
+
+ picref = avfilter_default_get_video_buffer(link, perms, width, height);
+
+ picref->video->w = w;
+ picref->video->h = h;
+
+ for (i = 0; i < 3; i++)
+ picref->data[i] += picref->linesize[i];
+
+ return picref;
+}
+
+void return_frame(AVFilterContext *ctx, int is_second)
+{
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+ AVFilterLink *link= ctx->outputs[0];
+ int tff;
+
+ if (yadif->parity == -1) {
+ tff = yadif->cur->video->interlaced ?
+ yadif->cur->video->top_field_first : 1;
+ } else {
+ tff = yadif->parity^1;
+ }
+
+ if (is_second)
+ yadif->out = avfilter_get_video_buffer(link, AV_PERM_WRITE | AV_PERM_PRESERVE |
+ AV_PERM_REUSE, link->w, link->h);
+
+ if (!yadif->csp)
+ yadif->csp = &av_pix_fmt_descriptors[link->format];
+ if (yadif->csp->comp[0].depth_minus1 == 15)
+ yadif->filter_line = (decltype(yadif->filter_line))filter_line_c_16bit;
+
+ filter(ctx, yadif->out, tff ^ !is_second, tff);
+
+ if (is_second) {
+ if (yadif->next->pts != AV_NOPTS_VALUE &&
+ yadif->cur->pts != AV_NOPTS_VALUE) {
+ yadif->out->pts =
+ (yadif->next->pts&yadif->cur->pts) +
+ ((yadif->next->pts^yadif->cur->pts)>>1);
+ } else {
+ yadif->out->pts = AV_NOPTS_VALUE;
+ }
+ avfilter_start_frame(ctx->outputs[0], yadif->out);
+ }
+ avfilter_draw_slice(ctx->outputs[0], 0, link->h, 1);
+ avfilter_end_frame(ctx->outputs[0]);
+
+ yadif->frame_pending = (yadif->mode&1) && !is_second;
+}
+
+void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
+{
+ AVFilterContext *ctx = link->dst;
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+
+ if (yadif->frame_pending)
+ return_frame(ctx, 1);
+
+ if (yadif->prev)
+ avfilter_unref_buffer(yadif->prev);
+ yadif->prev = yadif->cur;
+ yadif->cur = yadif->next;
+ yadif->next = picref;
+
+ if (!yadif->cur)
+ return;
+
+ if (yadif->auto_enable && !yadif->cur->video->interlaced) {
+ yadif->out = avfilter_ref_buffer(yadif->cur, AV_PERM_READ);
+ avfilter_unref_buffer(yadif->prev);
+ yadif->prev = NULL;
+ avfilter_start_frame(ctx->outputs[0], yadif->out);
+ return;
+ }
+
+ if (!yadif->prev)
+ yadif->prev = avfilter_ref_buffer(yadif->cur, AV_PERM_READ);
+
+ yadif->out = avfilter_get_video_buffer(ctx->outputs[0], AV_PERM_WRITE | AV_PERM_PRESERVE |
+ AV_PERM_REUSE, link->w, link->h);
+
+ avfilter_copy_buffer_ref_props(yadif->out, yadif->cur);
+ yadif->out->video->interlaced = 0;
+ avfilter_start_frame(ctx->outputs[0], yadif->out);
+}
+
+void end_frame(AVFilterLink *link)
+{
+ AVFilterContext *ctx = link->dst;
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+
+ if (!yadif->out)
+ return;
+
+ if (yadif->auto_enable && !yadif->cur->video->interlaced) {
+ avfilter_draw_slice(ctx->outputs[0], 0, link->h, 1);
+ avfilter_end_frame(ctx->outputs[0]);
+ return;
+ }
+
+ return_frame(ctx, 0);
+}
+
+int request_frame(AVFilterLink *link)
+{
+ AVFilterContext *ctx = link->src;
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+
+ if (yadif->frame_pending) {
+ return_frame(ctx, 1);
+ return 0;
+ }
+
+ do {
+ int ret;
+ ret = avfilter_request_frame(link->src->inputs[0]);
+ if (ret)
+ return ret;
+ } while (!yadif->cur);
+
+ return 0;
+}
+
+int poll_frame(AVFilterLink *link)
+{
+ YADIFContext *yadif = (YADIFContext*)link->src->priv;
+ int ret, val;
+
+ if (yadif->frame_pending)
+ return 1;
+
+ val = avfilter_poll_frame(link->src->inputs[0]);
+
+ if (val==1 && !yadif->next) { //FIXME change API to not requre this red tape
+ if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0)
+ return ret;
+ val = avfilter_poll_frame(link->src->inputs[0]);
+ }
+ assert(yadif->next || !val);
+
+ if (yadif->auto_enable && yadif->next && !yadif->next->video->interlaced)
+ return val;
+
+ return val * ((yadif->mode&1)+1);
+}
+
+void uninit(AVFilterContext *ctx)
+{
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+
+ if (yadif->prev) avfilter_unref_buffer(yadif->prev);
+ if (yadif->cur ) avfilter_unref_buffer(yadif->cur );
+ if (yadif->next) avfilter_unref_buffer(yadif->next);
+}
+
+int query_formats(AVFilterContext *ctx)
+{
+ static const enum PixelFormat pix_fmts[] = {
+ PIX_FMT_YUV420P,
+ PIX_FMT_YUV422P,
+ PIX_FMT_YUV444P,
+ PIX_FMT_YUV410P,
+ PIX_FMT_YUV411P,
+ PIX_FMT_GRAY8,
+ PIX_FMT_YUVJ420P,
+ PIX_FMT_YUVJ422P,
+ PIX_FMT_YUVJ444P,
+ AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
+ PIX_FMT_YUV440P,
+ PIX_FMT_YUVJ440P,
+ AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
+ AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
+ AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
+ PIX_FMT_NONE
+ };
+
+ avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list((const int*)pix_fmts));
+
+ return 0;
+}
+
+int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+ YADIFContext *yadif = (YADIFContext*)ctx->priv;
+ av_unused int cpu_flags = av_get_cpu_flags();
+
+ yadif->mode = 0;
+ yadif->parity = -1;
+ yadif->auto_enable = 0;
+ yadif->csp = NULL;
+
+ if (args) sscanf(args, "%d:%d:%d", &yadif->mode, &yadif->parity, &yadif->auto_enable);
+
+ \r
+ auto module = LoadLibrary(L"avfilter-2.dll");\r
+ static std::shared_ptr<void> lib(module, FreeLibrary);\r
+ yadif->filter_line = (decltype(yadif->filter_line))(GetProcAddress(module, "ff_yadif_filter_line_ssse3"));
+
+ //yadif->filter_line = filter_line_c;
+ //if (HAVE_SSSE3 && cpu_flags & AV_CPU_FLAG_SSSE3)
+ // yadif->filter_line = ff_yadif_filter_line_ssse3;
+ //else if (HAVE_SSE && cpu_flags & AV_CPU_FLAG_SSE2)
+ // yadif->filter_line = ff_yadif_filter_line_sse2;
+ //else if (HAVE_MMX && cpu_flags & AV_CPU_FLAG_MMX)
+ // yadif->filter_line = ff_yadif_filter_line_mmx;
+
+ av_log(ctx, AV_LOG_INFO, "mode:%d parity:%d auto_enable:%d\n", yadif->mode, yadif->parity, yadif->auto_enable);
+
+ return 0;
+}
+
+void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
+
+void register_scalable_yadif()
+{
+ static AVFilter avfilter_vf_syadif;
+ avfilter_vf_syadif.name = "syadif";
+ avfilter_vf_syadif .description = "Deinterlace the input image";
+
+ avfilter_vf_syadif.priv_size = sizeof(YADIFContext);
+ avfilter_vf_syadif.init = init;
+ avfilter_vf_syadif.uninit = uninit;
+ avfilter_vf_syadif.query_formats = query_formats;
+
+ static AVFilterPad inputs[2];
+
+ inputs[0].name = "default";
+ inputs[0].type = AVMEDIA_TYPE_VIDEO,
+ inputs[0].start_frame = start_frame;
+ inputs[0].get_video_buffer = get_video_buffer;
+ inputs[0].draw_slice = null_draw_slice;
+ inputs[0].end_frame = end_frame;
+ inputs[1].name = nullptr;
+
+ avfilter_vf_syadif.inputs = inputs;
+
+ static AVFilterPad outputs[2];
+
+ outputs[0].name = "default";
+ outputs[0].type = AVMEDIA_TYPE_VIDEO;
+ outputs[0].poll_frame = poll_frame;
+ outputs[0].request_frame = request_frame;
+ outputs[1].name = nullptr;
+
+ avfilter_vf_syadif.outputs = outputs;
+
+ avfilter_register(&avfilter_vf_syadif);
+};
{ \r
std::queue<safe_ptr<write_frame>> video_frames_;\r
std::vector<int16_t> audio_samples_;\r
- std::queue<safe_ptr<basic_frame>> frame_buffer_;\r
+ std::deque<safe_ptr<basic_frame>> frame_buffer_;\r
display_mode::type display_mode_;\r
const double in_fps_;\r
const video_format_desc format_desc_;\r
\r
if(display_mode_ == display_mode::invalid)\r
display_mode_ = auto_mode_ ? get_display_mode(video_frame->get_type(), in_fps_, format_desc_.mode, format_desc_.fps) : display_mode::simple;\r
-\r
- if(display_mode_ != display_mode::deinterlace && display_mode_ != display_mode::deinterlace_bob) \r
- video_frame->commit();\r
-\r
+ \r
++video_frame_count_;\r
\r
// Fix field-order if needed\r
\r
video_frames_.push(make_safe(video_frame));\r
\r
- process();\r
+ process(frame_buffer_);\r
+\r
+ video_frame->commit();\r
}\r
\r
void push(const std::shared_ptr<std::vector<int16_t>>& audio_samples)\r
audio_sample_count_ += audio_samples->size();\r
\r
boost::range::push_back(audio_samples_, *audio_samples);\r
- process();\r
+ std::deque<safe_ptr<basic_frame>> frames;\r
+ process(frames);\r
+ boost::range::push_back(frame_buffer_, frames);\r
}\r
\r
safe_ptr<basic_frame> pop()\r
{ \r
auto frame = frame_buffer_.front();\r
- frame_buffer_.pop();\r
+ frame_buffer_.pop_front();\r
return frame;\r
}\r
\r
return samples;\r
}\r
\r
- void process()\r
+ void process(std::deque<safe_ptr<basic_frame>>& dest)\r
{\r
if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
switch(display_mode_)\r
{\r
- case display_mode::simple:\r
- return simple();\r
- case display_mode::duplicate:\r
- return duplicate();\r
- case display_mode::half:\r
- return half();\r
- case display_mode::interlace:\r
- return interlace();\r
- case display_mode::deinterlace_bob:\r
- return deinterlace_bob();\r
- case display_mode::deinterlace:\r
- return deinterlace();\r
- default:\r
- BOOST_THROW_EXCEPTION(invalid_operation());\r
+ case display_mode::simple: return simple(dest);\r
+ case display_mode::duplicate: return duplicate(dest);\r
+ case display_mode::half: return half(dest);\r
+ case display_mode::interlace: return interlace(dest);\r
+ case display_mode::deinterlace_bob: return deinterlace_bob(dest);\r
+ case display_mode::deinterlace: return deinterlace(dest);\r
+ default: BOOST_THROW_EXCEPTION(invalid_operation());\r
}\r
}\r
\r
- void simple()\r
+ void simple(std::deque<safe_ptr<basic_frame>>& dest)\r
{\r
if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
+ \r
+ if(video_frames_.front()->get_type() != core::video_mode::progressive && format_desc_.mode != core::video_mode::progressive &&\r
+ video_frames_.front()->get_pixel_format_desc().planes.at(0).height != format_desc_.height )\r
+ { // The frame will most likely be scaled, we need to deinterlace->reinterlace\r
+ if(!filter_)\r
+ filter_.reset(new filter(L"YADIF=1:-1"));\r
+ \r
+ auto frame = pop_video();\r
\r
- auto frame1 = pop_video();\r
- frame1->audio_data() = pop_audio();\r
+ auto av_frames = filter_->execute(as_av_frame(frame));\r
+\r
+ if(av_frames.size() < 2)\r
+ return;\r
+\r
+ auto frame1 = make_write_frame(frame->tag(), av_frames[0], frame_factory_);\r
+ frame1->commit();\r
+ auto frame2 = make_write_frame(frame->tag(), av_frames[1], frame_factory_);\r
+ frame2->commit();\r
\r
- frame_buffer_.push(frame1);\r
+ frame1->audio_data() = pop_audio();\r
+ dest.push_back(core::basic_frame::interlace(frame1, frame2, format_desc_.mode)); \r
+ }\r
+ else\r
+ {\r
+ auto frame1 = pop_video();\r
+ frame1->commit();\r
+ frame1->audio_data() = pop_audio();\r
+\r
+ dest.push_back(frame1);\r
+ }\r
}\r
\r
- void duplicate()\r
+ void duplicate(std::deque<safe_ptr<basic_frame>>& dest)\r
{ \r
if(video_frames_.empty() || audio_samples_.size()/2 < format_desc_.audio_samples_per_frame)\r
return;\r
\r
auto frame = pop_video();\r
+ frame->commit();\r
\r
auto frame1 = make_safe<core::write_frame>(*frame); // make a copy\r
frame1->audio_data() = pop_audio();\r
auto frame2 = frame;\r
frame2->audio_data() = pop_audio();\r
\r
- frame_buffer_.push(frame1);\r
- frame_buffer_.push(frame2);\r
+ dest.push_back(frame1);\r
+ dest.push_back(frame2);\r
}\r
\r
- void half()\r
+ void half(std::deque<safe_ptr<basic_frame>>& dest)\r
{ \r
if(video_frames_.size() < 2 || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
auto frame1 = pop_video();\r
+ frame1->commit();\r
frame1->audio_data() = pop_audio();\r
\r
video_frames_.pop(); // Throw away\r
\r
- frame_buffer_.push(frame1);\r
+ dest.push_back(frame1);\r
}\r
\r
- void interlace()\r
+ void interlace(std::deque<safe_ptr<basic_frame>>& dest)\r
{ \r
if(video_frames_.size() < 2 || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
auto frame1 = pop_video();\r
+ frame1->commit();\r
\r
frame1->audio_data() = pop_audio();\r
\r
auto frame2 = pop_video();\r
\r
- frame_buffer_.push(core::basic_frame::interlace(frame1, frame2, format_desc_.mode)); \r
+ dest.push_back(core::basic_frame::interlace(frame1, frame2, format_desc_.mode)); \r
}\r
- \r
- void deinterlace_bob()\r
+ \r
+ void deinterlace_bob(std::deque<safe_ptr<basic_frame>>& dest)\r
{\r
if(video_frames_.empty() || audio_samples_.size()/2 < format_desc_.audio_samples_per_frame)\r
return;\r
frame2->commit();\r
frame2->audio_data() = pop_audio();\r
\r
- frame_buffer_.push(frame1);\r
- frame_buffer_.push(frame2);\r
+ dest.push_back(frame1);\r
+ dest.push_back(frame2);\r
}\r
\r
- void deinterlace()\r
+ void deinterlace(std::deque<safe_ptr<basic_frame>>& dest)\r
{\r
if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
frame1->commit();\r
frame1->audio_data() = pop_audio();\r
\r
- frame_buffer_.push(frame1);\r
+ dest.push_back(frame1);\r
}\r
};\r
\r