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
This commit is contained in:
Michael Niedermayer 2010-03-05 02:20:10 +00:00
parent 4ef82b174a
commit 917d2bb348

368
ffplay.c
View File

@ -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 <SDL.h>
@ -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; ///<the last filter in the video chain
#endif
} VideoState;
static void show_help(void);
@ -234,6 +252,9 @@ static int error_recognition = FF_ER_CAREFUL;
static int error_concealment = 3;
static int decoder_reorder_pts= -1;
static int autoexit;
#if CONFIG_AVFILTER
static char *vfilters = NULL;
#endif
/* current context */
static int is_full_screen;
@ -668,6 +689,13 @@ static void video_image_display(VideoState *is)
vp = &is->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_pts<is->faulty_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;i<VIDEO_PICTURE_QUEUE_SIZE; i++) {
vp = &is->pictq[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<CODEC_TYPE_NB; i++){
avcodec_opts[i]= avcodec_alloc_context2(i);
}
avformat_opts = avformat_alloc_context();
#if !CONFIG_AVFILTER
sws_opts = sws_getContext(16,16,0, 16,16,0, sws_flags, NULL,NULL,NULL);
#endif
show_banner();