From 917d2bb348077079df64056a5dd6f4285aedcacb Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 5 Mar 2010 02:20:10 +0000 Subject: Libavfilter for ffplay support. This still needs some minor work here and there but should be already functional. Note that the code pathes that are under "not avfilter" ifdefs as well as the ifdefs will be droped as soon as all major issues have been det with, aka could be real soon or not. Originally committed as revision 22216 to svn://svn.ffmpeg.org/ffmpeg/trunk --- ffplay.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 338 insertions(+), 30 deletions(-) (limited to 'ffplay.c') diff --git a/ffplay.c b/ffplay.c index 36bbf15bb9..6ef0b25f28 100644 --- a/ffplay.c +++ b/ffplay.c @@ -32,6 +32,12 @@ #include "libavcodec/opt.h" #include "libavcodec/dsputil.h" +#if CONFIG_AVFILTER +# include "libavfilter/avfilter.h" +# include "libavfilter/avfiltergraph.h" +# include "libavfilter/graphparser.h" +#endif + #include "cmdutils.h" #include @@ -72,7 +78,9 @@ const int program_birth_year = 2003; /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */ #define SAMPLE_ARRAY_SIZE (2*65536) +#if !CONFIG_AVFILTER static int sws_flags = SWS_BICUBIC; +#endif typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; @@ -93,6 +101,11 @@ typedef struct VideoPicture { int width, height; /* source height & width */ int allocated; SDL_TimerID timer_id; + enum PixelFormat pix_fmt; + +#if CONFIG_AVFILTER + AVFilterPicRef *picref; +#endif } VideoPicture; typedef struct SubPicture { @@ -180,7 +193,9 @@ typedef struct VideoState { int pictq_size, pictq_rindex, pictq_windex; SDL_mutex *pictq_mutex; SDL_cond *pictq_cond; +#if !CONFIG_AVFILTER struct SwsContext *img_convert_ctx; +#endif // QETimer *video_timer; char filename[1024]; @@ -191,6 +206,9 @@ typedef struct VideoState { int64_t last_dts_for_fault_detection; int64_t last_pts_for_fault_detection; +#if CONFIG_AVFILTER + AVFilterContext *out_video_filter; ///pictq[is->pictq_rindex]; if (vp->bmp) { +#if CONFIG_AVFILTER + if (vp->picref->pixel_aspect.num == 0) + aspect_ratio = 0; + else + aspect_ratio = av_q2d(vp->picref->pixel_aspect); +#else + /* XXX: use variable in the frame */ if (is->video_st->sample_aspect_ratio.num) aspect_ratio = av_q2d(is->video_st->sample_aspect_ratio); @@ -675,9 +703,10 @@ static void video_image_display(VideoState *is) aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio); else aspect_ratio = 0; +#endif if (aspect_ratio <= 0.0) aspect_ratio = 1.0; - aspect_ratio *= (float)is->video_st->codec->width / is->video_st->codec->height; + aspect_ratio *= (float)vp->width / (float)vp->height; /* if an active format is indicated, then it overrides the mpeg format */ #if 0 @@ -927,9 +956,15 @@ static int video_open(VideoState *is){ } else if(!is_full_screen && screen_width){ w = screen_width; h = screen_height; +#if CONFIG_AVFILTER + }else if (is->out_video_filter && is->out_video_filter->inputs[0]){ + w = is->out_video_filter->inputs[0]->w; + h = is->out_video_filter->inputs[0]->h; +#else }else if (is->video_st && is->video_st->codec->width){ w = is->video_st->codec->width; h = is->video_st->codec->height; +#endif } else { w = 640; h = 480; @@ -1241,12 +1276,23 @@ static void alloc_picture(void *opaque) if (vp->bmp) SDL_FreeYUVOverlay(vp->bmp); - vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width, - is->video_st->codec->height, +#if CONFIG_AVFILTER + if (vp->picref) + avfilter_unref_pic(vp->picref); + vp->picref = NULL; + + vp->width = is->out_video_filter->inputs[0]->w; + vp->height = is->out_video_filter->inputs[0]->h; + vp->pix_fmt = is->out_video_filter->inputs[0]->format; +#else + vp->width = is->video_st->codec->width; + vp->height = is->video_st->codec->height; + vp->pix_fmt = is->video_st->codec->pix_fmt; +#endif + + vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height, SDL_YV12_OVERLAY, screen); - vp->width = is->video_st->codec->width; - vp->height = is->video_st->codec->height; SDL_LockMutex(is->pictq_mutex); vp->allocated = 1; @@ -1262,7 +1308,9 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t { VideoPicture *vp; int dst_pix_fmt; - +#if CONFIG_AVFILTER + AVPicture pict_src; +#endif /* wait until we have space to put a new picture */ SDL_LockMutex(is->pictq_mutex); while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && @@ -1278,8 +1326,13 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t /* alloc or resize hardware picture buffer */ if (!vp->bmp || +#if CONFIG_AVFILTER + vp->width != is->out_video_filter->inputs[0]->w || + vp->height != is->out_video_filter->inputs[0]->h) { +#else vp->width != is->video_st->codec->width || vp->height != is->video_st->codec->height) { +#endif SDL_Event event; vp->allocated = 0; @@ -1304,6 +1357,11 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t /* if the frame is not skipped, then display it */ if (vp->bmp) { AVPicture pict; +#if CONFIG_AVFILTER + if(vp->picref) + avfilter_unref_pic(vp->picref); + vp->picref = src_frame->opaque; +#endif /* get a pointer on the bitmap */ SDL_LockYUVOverlay (vp->bmp); @@ -1317,18 +1375,31 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t pict.linesize[0] = vp->bmp->pitches[0]; pict.linesize[1] = vp->bmp->pitches[2]; pict.linesize[2] = vp->bmp->pitches[1]; + +#if CONFIG_AVFILTER + pict_src.data[0] = src_frame->data[0]; + pict_src.data[1] = src_frame->data[1]; + pict_src.data[2] = src_frame->data[2]; + + pict_src.linesize[0] = src_frame->linesize[0]; + pict_src.linesize[1] = src_frame->linesize[1]; + pict_src.linesize[2] = src_frame->linesize[2]; + + //FIXME use direct rendering + av_picture_copy(&pict, &pict_src, + vp->pix_fmt, vp->width, vp->height); +#else sws_flags = av_get_int(sws_opts, "sws_flags", NULL); is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx, - is->video_st->codec->width, is->video_st->codec->height, - is->video_st->codec->pix_fmt, - is->video_st->codec->width, is->video_st->codec->height, + vp->width, vp->height, vp->pix_fmt, vp->width, vp->height, dst_pix_fmt, sws_flags, NULL, NULL, NULL); if (is->img_convert_ctx == NULL) { fprintf(stderr, "Cannot initialize the conversion context\n"); exit(1); } sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize, - 0, is->video_st->codec->height, pict.data, pict.linesize); + 0, vp->height, pict.data, pict.linesize); +#endif /* update the bitmap content */ SDL_UnlockYUVOverlay(vp->bmp); @@ -1386,20 +1457,12 @@ static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1, int6 return queue_picture(is, src_frame, pts, pos); } -static int video_thread(void *arg) +static int get_video_frame(VideoState *is, AVFrame *frame, uint64_t *pts, AVPacket *pkt) { - VideoState *is = arg; - AVPacket pkt1, *pkt = &pkt1; int len1, got_picture, i; - AVFrame *frame= avcodec_alloc_frame(); - double pts; - for(;;) { - while (is->paused && !is->videoq.abort_request) { - SDL_Delay(10); - } if (packet_queue_get(&is->videoq, pkt, 1) < 0) - break; + return -1; if(pkt->data == flush_pkt.data){ avcodec_flush_buffers(is->video_st->codec); @@ -1425,7 +1488,7 @@ static int video_thread(void *arg) is->frame_last_delay = 0; is->frame_timer = (double)av_gettime() / 1000000.0; - continue; + return 0; } /* NOTE: ipts is the PTS of the _first_ picture beginning in @@ -1450,25 +1513,251 @@ static int video_thread(void *arg) || (decoder_reorder_pts && is->faulty_ptsfaulty_dts) || pkt->dts == AV_NOPTS_VALUE) && frame->reordered_opaque != AV_NOPTS_VALUE) - pts= frame->reordered_opaque; + *pts= frame->reordered_opaque; else if(pkt->dts != AV_NOPTS_VALUE) - pts= pkt->dts; + *pts= pkt->dts; else - pts= 0; - pts *= av_q2d(is->video_st->time_base); + *pts= 0; + + /* put pts into units of 1/AV_TIME_BASE */ + *pts = av_rescale_q(pts,is->video_st->time_base, AV_TIME_BASE_Q); // if (len1 < 0) // break; - if (got_picture) { - if (output_picture2(is, frame, pts, pkt->pos) < 0) - goto the_end; - } - av_free_packet(pkt); + if (got_picture) + return 1; + return 0; +} + +#if CONFIG_AVFILTER +typedef struct { + VideoState *is; + AVFrame *frame; +} FilterPriv; + +static int input_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + FilterPriv *priv = ctx->priv; + if(!opaque) return -1; + + priv->is = opaque; + 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; + AVFilterPicRef *picref; + uint64_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; + + /* FIXME: until I figure out how to hook everything up to the codec + * right, we're just copying the entire frame. */ + picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h); + av_picture_copy((AVPicture *)&picref->data, (AVPicture *)priv->frame, + picref->pic->format, link->w, link->h); + av_free_packet(&pkt); + + picref->pts = pts; + picref->pixel_aspect = priv->is->video_st->codec->sample_aspect_ratio; + avfilter_start_frame(link, avfilter_ref_pic(picref, ~0)); + avfilter_draw_slice(link, 0, link->h, 1); + avfilter_end_frame(link); + avfilter_unref_pic(picref); + + 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; + + 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 = CODEC_TYPE_VIDEO, + .request_frame = input_request_frame, + .config_props = input_config_props, }, + { .name = NULL }}, +}; + +static void output_end_frame(AVFilterLink *link) +{ +} + +static int output_query_formats(AVFilterContext *ctx) +{ + enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE }; + + avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + return 0; +} + +static int get_filtered_video_frame(AVFilterContext *ctx, AVFrame *frame, + uint64_t *pts) +{ + AVFilterPicRef *pic; + + if(avfilter_request_frame(ctx->inputs[0])) + return -1; + if(!(pic = ctx->inputs[0]->cur_pic)) + return -1; + ctx->inputs[0]->cur_pic = NULL; + + frame->opaque = pic; + *pts = pic->pts; + + memcpy(frame->data, pic->data, sizeof(frame->data)); + memcpy(frame->linesize, pic->linesize, sizeof(frame->linesize)); + + return 1; +} + +static AVFilter output_filter = +{ + .name = "ffplay_output", + + .query_formats = output_query_formats, + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = CODEC_TYPE_VIDEO, + .end_frame = output_end_frame, + .min_perms = AV_PERM_READ, }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = NULL }}, +}; +#endif /* CONFIG_AVFILTER */ + +static int video_thread(void *arg) +{ + VideoState *is = arg; + AVFrame *frame= avcodec_alloc_frame(); + uint64_t pts_int; + double pts; + int ret; + +#if CONFIG_AVFILTER + AVFilterContext *filt_src = NULL, *filt_out = NULL; + AVFilterGraph *graph = av_mallocz(sizeof(AVFilterGraph)); + graph->scale_sws_opts = av_strdup("sws_flags=bilinear"); + + if(!(filt_src = avfilter_open(&input_filter, "src"))) goto the_end; + if(!(filt_out = avfilter_open(&output_filter, "out"))) goto the_end; + + if(avfilter_init_filter(filt_src, NULL, is)) goto the_end; + if(avfilter_init_filter(filt_out, NULL, frame)) 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_check_validity(graph, NULL)) goto the_end; + if(avfilter_graph_config_formats(graph, NULL)) goto the_end; + if(avfilter_graph_config_links(graph, NULL)) goto the_end; + + is->out_video_filter = filt_out; +#endif + + for(;;) { +#if !CONFIG_AVFILTER + AVPacket pkt; +#endif + while (is->paused && !is->videoq.abort_request) + SDL_Delay(10); +#if CONFIG_AVFILTER + ret = get_filtered_video_frame(filt_out, frame, &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; + pts /= AV_TIME_BASE; + +#if CONFIG_AVFILTER + ret = output_picture2(is, frame, pts, -1); /* fixme: unknown pos */ +#else + ret = output_picture2(is, frame, pts, pkt->pos); + av_free_packet(&pkt); +#endif + if (ret < 0) + goto the_end; + if (step) if (cur_stream) stream_pause(cur_stream); } the_end: +#if CONFIG_AVFILTER + avfilter_graph_destroy(graph); + av_freep(&graph); +#endif av_free(frame); return 0; } @@ -2265,6 +2554,12 @@ static void stream_close(VideoState *is) /* free all pictures */ for(i=0;ipictq[i]; +#if CONFIG_AVFILTER + if (vp->picref) { + avfilter_unref_pic(vp->picref); + vp->picref = NULL; + } +#endif if (vp->bmp) { SDL_FreeYUVOverlay(vp->bmp); vp->bmp = NULL; @@ -2274,8 +2569,10 @@ static void stream_close(VideoState *is) SDL_DestroyCond(is->pictq_cond); SDL_DestroyMutex(is->subpq_mutex); SDL_DestroyCond(is->subpq_cond); +#if !CONFIG_AVFILTER if (is->img_convert_ctx) sws_freeContext(is->img_convert_ctx); +#endif av_free(is); } @@ -2367,6 +2664,9 @@ static void do_exit(void) av_free(avcodec_opts[i]); av_free(avformat_opts); av_free(sws_opts); +#if CONFIG_AVFILTER + avfilter_uninit(); +#endif if (show_status) printf("\n"); SDL_Quit(); @@ -2637,6 +2937,9 @@ static const OptionDef options[] = { { "sync", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_sync}, "set audio-video sync. type (type=audio/video/ext)", "type" }, { "threads", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" }, { "autoexit", OPT_BOOL | OPT_EXPERT, {(void*)&autoexit}, "exit at the end", "" }, +#if CONFIG_AVFILTER + { "vfilters", OPT_STRING | HAS_ARG, {(void*)&vfilters}, "video filters", "filter list" }, +#endif { "default", OPT_FUNC2 | HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" }, { NULL, }, }; @@ -2689,13 +2992,18 @@ int main(int argc, char **argv) /* register all codecs, demux and protocols */ avcodec_register_all(); avdevice_register_all(); +#if CONFIG_AVFILTER + avfilter_register_all(); +#endif av_register_all(); for(i=0; i