diff --git a/ffplay.c b/ffplay.c index 36bbf15bb9d7ffe24cdcfda140488ab4ed30836f..6ef0b25f2862e92b67ea1532922123d5347e5633 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