+
+ }
+ return 0;
+}
+
+#if CONFIG_AVFILTER
+typedef struct {
+ VideoState *is;
+ AVFrame *frame;
+ int use_dr1;
+} FilterPriv;
+
+static int input_get_buffer(AVCodecContext *codec, AVFrame *pic)
+{
+ AVFilterContext *ctx = codec->opaque;
+ AVFilterBufferRef *ref;
+ int perms = AV_PERM_WRITE;
+ int i, w, h, stride[4];
+ unsigned edge;
+
+ if(pic->buffer_hints & FF_BUFFER_HINTS_VALID) {
+ if(pic->buffer_hints & FF_BUFFER_HINTS_READABLE) perms |= AV_PERM_READ;
+ if(pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) perms |= AV_PERM_PRESERVE;
+ if(pic->buffer_hints & FF_BUFFER_HINTS_REUSABLE) perms |= AV_PERM_REUSE2;
+ }
+ if(pic->reference) perms |= AV_PERM_READ | AV_PERM_PRESERVE;
+
+ w = codec->width;
+ h = codec->height;
+ avcodec_align_dimensions2(codec, &w, &h, stride);
+ edge = codec->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width();
+ w += edge << 1;
+ h += edge << 1;
+
+ if(!(ref = avfilter_get_video_buffer(ctx->outputs[0], perms, w, h)))
+ return -1;
+
+ ref->video->w = codec->width;
+ ref->video->h = codec->height;
+ for(i = 0; i < 4; i ++) {
+ unsigned hshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_w : 0;
+ unsigned vshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_h : 0;
+
+ if (ref->data[i]) {
+ ref->data[i] += (edge >> hshift) + ((edge * ref->linesize[i]) >> vshift);
+ }
+ pic->data[i] = ref->data[i];
+ pic->linesize[i] = ref->linesize[i];
+ }
+ pic->opaque = ref;
+ pic->age = INT_MAX;
+ pic->type = FF_BUFFER_TYPE_USER;
+ pic->reordered_opaque = codec->reordered_opaque;
+ return 0;
+}
+
+static void input_release_buffer(AVCodecContext *codec, AVFrame *pic)
+{
+ memset(pic->data, 0, sizeof(pic->data));
+ avfilter_unref_buffer(pic->opaque);
+}
+
+static int input_reget_buffer(AVCodecContext *codec, AVFrame *pic)
+{
+ AVFilterBufferRef *ref = pic->opaque;
+
+ if (pic->data[0] == NULL) {
+ pic->buffer_hints |= FF_BUFFER_HINTS_READABLE;
+ return codec->get_buffer(codec, pic);
+ }
+
+ if ((codec->width != ref->video->w) || (codec->height != ref->video->h) ||
+ (codec->pix_fmt != ref->format)) {
+ av_log(codec, AV_LOG_ERROR, "Picture properties changed.\n");
+ return -1;
+ }
+
+ pic->reordered_opaque = codec->reordered_opaque;
+ return 0;
+}
+
+static int input_init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+ FilterPriv *priv = ctx->priv;
+ AVCodecContext *codec;
+ if(!opaque) return -1;
+
+ priv->is = opaque;
+ codec = priv->is->video_st->codec;
+ codec->opaque = ctx;
+ if(codec->codec->capabilities & CODEC_CAP_DR1) {
+ priv->use_dr1 = 1;
+ codec->get_buffer = input_get_buffer;
+ codec->release_buffer = input_release_buffer;
+ codec->reget_buffer = input_reget_buffer;
+ }
+
+ priv->frame = avcodec_alloc_frame();
+
+ return 0;
+}
+
+static void input_uninit(AVFilterContext *ctx)
+{
+ FilterPriv *priv = ctx->priv;
+ av_free(priv->frame);
+}
+
+static int input_request_frame(AVFilterLink *link)
+{
+ FilterPriv *priv = link->src->priv;
+ AVFilterBufferRef *picref;
+ int64_t pts = 0;
+ AVPacket pkt;
+ int ret;
+
+ while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt)))
+ av_free_packet(&pkt);
+ if (ret < 0)
+ return -1;
+
+ if(priv->use_dr1) {
+ picref = avfilter_ref_buffer(priv->frame->opaque, ~0);
+ } else {
+ picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h);
+ av_image_copy(picref->data, picref->linesize,
+ priv->frame->data, priv->frame->linesize,
+ picref->format, link->w, link->h);
+ }
+ av_free_packet(&pkt);
+
+ picref->pts = pts;
+ picref->pos = pkt.pos;
+ picref->video->pixel_aspect = priv->is->video_st->codec->sample_aspect_ratio;
+ avfilter_start_frame(link, picref);
+ avfilter_draw_slice(link, 0, link->h, 1);
+ avfilter_end_frame(link);
+
+ return 0;
+}
+
+static int input_query_formats(AVFilterContext *ctx)
+{
+ FilterPriv *priv = ctx->priv;
+ enum PixelFormat pix_fmts[] = {
+ priv->is->video_st->codec->pix_fmt, PIX_FMT_NONE
+ };
+
+ avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
+ return 0;
+}
+
+static int input_config_props(AVFilterLink *link)
+{
+ FilterPriv *priv = link->src->priv;
+ AVCodecContext *c = priv->is->video_st->codec;
+
+ link->w = c->width;
+ link->h = c->height;
+ link->time_base = priv->is->video_st->time_base;
+
+ return 0;
+}
+
+static AVFilter input_filter =
+{
+ .name = "ffplay_input",
+
+ .priv_size = sizeof(FilterPriv),
+
+ .init = input_init,
+ .uninit = input_uninit,
+
+ .query_formats = input_query_formats,
+
+ .inputs = (AVFilterPad[]) {{ .name = NULL }},
+ .outputs = (AVFilterPad[]) {{ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .request_frame = input_request_frame,
+ .config_props = input_config_props, },
+ { .name = NULL }},
+};
+
+#endif /* CONFIG_AVFILTER */
+
+static int video_thread(void *arg)
+{
+ VideoState *is = arg;
+ AVFrame *frame= avcodec_alloc_frame();
+ int64_t pts_int;
+ double pts;
+ int ret;
+
+#if CONFIG_AVFILTER
+ int64_t pos;
+ char sws_flags_str[128];
+ FFSinkContext ffsink_ctx = { .pix_fmt = PIX_FMT_YUV420P };
+ AVFilterContext *filt_src = NULL, *filt_out = NULL;
+ AVFilterGraph *graph = av_mallocz(sizeof(AVFilterGraph));
+ snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags);
+ graph->scale_sws_opts = av_strdup(sws_flags_str);
+
+ if (avfilter_open(&filt_src, &input_filter, "src") < 0) goto the_end;
+ if (avfilter_open(&filt_out, &ffsink , "out") < 0) goto the_end;
+
+ if(avfilter_init_filter(filt_src, NULL, is)) goto the_end;
+ if(avfilter_init_filter(filt_out, NULL, &ffsink_ctx)) goto the_end;
+
+
+ if(vfilters) {
+ AVFilterInOut *outputs = av_malloc(sizeof(AVFilterInOut));
+ AVFilterInOut *inputs = av_malloc(sizeof(AVFilterInOut));
+
+ outputs->name = av_strdup("in");
+ outputs->filter = filt_src;
+ outputs->pad_idx = 0;
+ outputs->next = NULL;
+
+ inputs->name = av_strdup("out");
+ inputs->filter = filt_out;
+ inputs->pad_idx = 0;
+ inputs->next = NULL;
+
+ if (avfilter_graph_parse(graph, vfilters, inputs, outputs, NULL) < 0)
+ goto the_end;
+ av_freep(&vfilters);
+ } else {
+ if(avfilter_link(filt_src, 0, filt_out, 0) < 0) goto the_end;
+ }
+ avfilter_graph_add_filter(graph, filt_src);
+ avfilter_graph_add_filter(graph, filt_out);
+
+ if (avfilter_graph_config(graph, NULL) < 0)
+ goto the_end;
+
+ is->out_video_filter = filt_out;
+#endif
+
+ for(;;) {
+#if !CONFIG_AVFILTER
+ AVPacket pkt;
+#else
+ AVFilterBufferRef *picref;
+ AVRational tb;
+#endif
+ while (is->paused && !is->videoq.abort_request)
+ SDL_Delay(10);
+#if CONFIG_AVFILTER
+ ret = get_filtered_video_frame(filt_out, frame, &picref, &tb);
+ if (picref) {
+ pts_int = picref->pts;
+ pos = picref->pos;
+ frame->opaque = picref;
+ }
+
+ if (av_cmp_q(tb, is->video_st->time_base)) {
+ int64_t pts1 = pts_int;
+ pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base);
+ av_log(NULL, AV_LOG_DEBUG, "video_thread(): "
+ "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n",
+ tb.num, tb.den, pts1,
+ is->video_st->time_base.num, is->video_st->time_base.den, pts_int);
+ }
+#else
+ ret = get_video_frame(is, frame, &pts_int, &pkt);
+#endif
+
+ if (ret < 0) goto the_end;
+
+ if (!ret)
+ continue;
+
+ pts = pts_int*av_q2d(is->video_st->time_base);
+
+#if CONFIG_AVFILTER
+ ret = output_picture2(is, frame, pts, pos);
+#else
+ ret = output_picture2(is, frame, pts, pkt.pos);
+ av_free_packet(&pkt);
+#endif
+ if (ret < 0)
+ goto the_end;
+