[FFmpeg-devel] [WIP] movie video source

Michael Niedermayer michaelni
Thu Dec 30 01:03:38 CET 2010


On Tue, Dec 28, 2010 at 02:42:33PM +0100, Stefano Sabatini wrote:
> On date Sunday 2010-11-21 17:49:57 +0100, Michael Niedermayer encoded:
> > On Sun, Oct 31, 2010 at 08:56:34PM +0100, Stefano Sabatini wrote:
> > [...]
> > > +    // Try to find the movie format (container)
> > > +    if (*movie->format_name)
> > > +        iformat = av_find_input_format(movie->format_name);
> > > +    else
> > 
> > {} nitpick
> > 
> > 
> > > +        iformat = NULL;
> > > +
> > > +    movie->format_ctx = NULL;
> > > +    if ((ret = av_open_input_file(&movie->format_ctx, movie->file_name, iformat, 0, NULL)) < 0) {
> > > +        av_log(ctx, AV_LOG_ERROR,
> > > +               "Failed to av_open_input_file '%s'\n", movie->file_name);
> > > +        return ret;
> > > +    }
> > > +    if ((ret = av_find_stream_info(movie->format_ctx)) < 0) {
> > > +        av_log(ctx, AV_LOG_ERROR, "Failed to find stream info\n");
> > > +        return ret;
> > > +    }
> > > +
> > > +    // if seeking requested, we execute it
> > > +    if (movie->seek_point > 0) {
> > > +        timestamp = movie->seek_point;
> > > +        // add the stream start time, should it exist
> > > +        if (movie->format_ctx->start_time != AV_NOPTS_VALUE)
> > > +            timestamp += movie->format_ctx->start_time;
> > > +        if (av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD) < 0) {
> > > +            av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n",
> > > +                   movie->file_name, timestamp);
> > > +        }
> > > +    }
> > > +
> > 
> > > +    /* find the first available video stream */
> > > +    if (movie->video_stream < 0) {
> > > +        for (i = 0; i < movie->format_ctx->nb_streams; i++)
> > > +            if (movie->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
> > > +                movie->video_stream = i;
> > > +                break;
> > > +            }
> > > +        if (movie->video_stream < 0) {
> > > +            av_log(ctx, AV_LOG_ERROR, "No video stream found\n");
> > > +            return AVERROR(EINVAL);
> > > +        }
> > 
> > should use the same st_best_packet_count system as ffplay to select the default
> > stream. Or this effectively introduces a bug that said system fixed
> 
> Updated, now it uses av_find_best_stream().
>  
> > 
> > [....]
> > > +static av_cold void uninit(AVFilterContext *ctx)
> > > +{
> > > +    MovieContext *movie = ctx->priv;
> > > +
> > > +    if (movie->codec_ctx)
> > > +        avcodec_close(movie->codec_ctx);
> > > +    if (movie->format_ctx)
> > > +        av_close_input_file(movie->format_ctx);
> > > +    av_freep(&movie->frame);
> > > +    if (movie->picref)
> > > +        avfilter_unref_buffer(movie->picref);
> > 
> > some of these functions should get null pointer checks so that the callers
> > dont have to
> > 
> > [...]
> > > +    if (!movie->picref)
> > > +        movie->picref = avfilter_get_video_buffer(outlink,
> > > +                                                  AV_PERM_WRITE | AV_PERM_PRESERVE | AV_PERM_REUSE2,
> > > +                                                  outlink->w, outlink->h);
> > > +
> > > +    while (av_read_frame(movie->format_ctx, &packet) >= 0) {
> > > +        // Is this a packet from the video stream?
> > > +        if (packet.stream_index == movie->video_stream) {
> > > +            // Decode video frame
> > > +            avcodec_decode_video2(movie->codec_ctx, movie->frame, &frame_finished, &packet);
> > > +
> > > +            // Did we get a video frame?
> > > +            if (frame_finished) {
> > > +                av_image_copy(movie->picref->data, movie->picref->linesize,
> > > +                              movie->frame ->data, movie->frame ->linesize,
> > > +                              movie->picref->format, outlink->w, outlink->h);
> > > +
> > > +                movie->picref->pts = packet.pts;
> > > +                movie->picref->pos = packet.pos;
> > > +                movie->picref->video->pixel_aspect = movie->codec_ctx->sample_aspect_ratio;
> > > +                movie->picref->video->interlaced = movie->frame->interlaced_frame;
> > > +                movie->picref->video->top_field_first = movie->frame->top_field_first;
> > > +
> > > +#ifdef DEBUG
> > > +                av_log(outlink->src, AV_LOG_DEBUG,
> > > +                       "movie_get_frame(): file:'%s' pkt.pts:%"PRId64" t:%lf pts:%"PRId64"\n",
> > > +                       movie->file_name, packet.pts,
> > > +                       (double)packet.pts * av_q2d(movie->format_ctx->streams[movie->video_stream]->time_base),
> > > +                       movie->picref->pts);
> > > +#endif
> > > +
> > > +                // We got it. Free the packet since we are returning
> > > +                av_free_packet(&packet);
> > > +
> > > +                return 0;
> > > +            }
> > > +        }
> > > +        // Free the packet that was allocated by av_read_frame
> > > +        av_free_packet(&packet);
> > > +    }
> > 
> > There are 2 ways to implement this
> > 1. as you do get a frame from the next filter and draw into this (this is a bit
> > hard)
> > 2. make a AVFilterBufferRef out of the returned picture from the decoder
> >    you just recently wrote a function doing this, so it should be trivial
> > 
> > but taking a frame from the next filter and then forcing a copy as done is
> > really the wrong way and also more complex than 2. above
> 
> Fixed by using avfilter_get_video_buffer_ref_from_arrays().
> -- 
> FFmpeg = Foolish & Fundamental Magnificient Pure Efficient Gangster

>  doc/filters.texi         |   35 ++++++
>  libavfilter/Makefile     |    2 
>  libavfilter/allfilters.c |    1 
>  libavfilter/vsrc_movie.c |  266 +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 304 insertions(+)
> be9ff2b8f5bf4151572633913e6f89862cb07552  0001-Add-movie-video-source.patch
> From c93063076fa9dd81c3d312cf8a662f0364cb93b0 Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefano.sabatini-lala at poste.it>
> Date: Sun, 31 Oct 2010 17:37:10 +0100
> Subject: [PATCH] Add movie video source.
> 
> ---
>  doc/filters.texi         |   35 ++++++
>  libavfilter/Makefile     |    2 +
>  libavfilter/allfilters.c |    1 +
>  libavfilter/vsrc_movie.c |  266 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 304 insertions(+), 0 deletions(-)
>  create mode 100644 libavfilter/vsrc_movie.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 0039044..8f17a4e 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -1086,6 +1086,41 @@ to the pad with identifier "in".
>  "color=red@@0.2:qcif:10 [color]; [in][color] overlay [out]"
>  @end example
>  
> + at section movie
> +
> +Read a video stream from a movie container.
> +
> +It accepts the syntax:
> + at example
> + at var{seekpoint}:@var{format}:@var{filename}[:@var{stream_index}]
> + at end example
> +

> + at var{seekpoint} specifies the seek point in microseconds, the frames
> +will be output starting from this seek point.

should be seconds and SI postfixes should be supported, we have code for that


> +
> + at var{format} specifies the assumed format for the input file, and can
> +be either the name of a container of an input device.

thats not a english sentance


> +
> + at var{filename} specifies the name of the input file.
> +
> + at var{stream_index} is optional and specifies the index of the video
> +stream to read. If the value is negative, the best suited video stream
> +will be automatically selected. Default value is "-1".
> +
> +It is possible to overlay a second movie on top of another source as
> +shown in this graph:
> + at example
> +input -----------> deltapts0 --> overlay --> output
> +                                    ^
> +                                    |
> +movie --> scale--> deltapts1 -------+
> + at end example
> +
> +Follows an example:
> + at example
> +"movie=3200000:avi:in.avi, scale=180:-1, setpts=PTS-STARTPTS [movie]; [in] setpts=PTS-START_PTS, [movie] overlay=16:16 [out]"
> + at end example
> +
>  @section nullsrc
>  
>  Null video source, never return images. It is mainly useful as a
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index fdb181e..ce1caa5 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak
>  
>  NAME = avfilter
>  FFLIBS = avcore avutil
> +FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec
>  FFLIBS-$(CONFIG_SCALE_FILTER) += swscale
>  
>  HEADERS = avfilter.h avfiltergraph.h
> @@ -50,6 +51,7 @@ OBJS-$(CONFIG_YADIF_FILTER)                  += vf_yadif.o
>  OBJS-$(CONFIG_BUFFER_FILTER)                 += vsrc_buffer.o
>  OBJS-$(CONFIG_COLOR_FILTER)                  += vf_pad.o
>  OBJS-$(CONFIG_FREI0R_SRC_FILTER)             += vf_frei0r.o
> +OBJS-$(CONFIG_MOVIE_FILTER)                  += vsrc_movie.o
>  OBJS-$(CONFIG_NULLSRC_FILTER)                += vsrc_nullsrc.o
>  
>  OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 1dffb80..cb29c2f 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -71,6 +71,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (BUFFER,      buffer,      vsrc);
>      REGISTER_FILTER (COLOR,       color,       vsrc);
>      REGISTER_FILTER (FREI0R,      frei0r_src,  vsrc);
> +    REGISTER_FILTER (MOVIE,       movie,       vsrc);
>      REGISTER_FILTER (NULLSRC,     nullsrc,     vsrc);
>  
>      REGISTER_FILTER (NULLSINK,    nullsink,    vsink);
> diff --git a/libavfilter/vsrc_movie.c b/libavfilter/vsrc_movie.c
> new file mode 100644
> index 0000000..ffc0480
> --- /dev/null
> +++ b/libavfilter/vsrc_movie.c
> @@ -0,0 +1,266 @@
> +/*
> + * Copyright (c) 2010 Stefano Sabatini
> + * Copyright (c) 2008 Victor Paesa
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser 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
> + */
> +
> +/**
> + * @file
> + * movie file video source filter
> + *
> + * @todo use direct rendering (no allocation of a new frame)

+ support more than one output stream


> + */
> +

> +#define DEBUG

?


> +
> +#include "libavcore/imgutils.h"
> +#include "libavformat/avformat.h"
> +#include "avfilter.h"
> +
> +typedef struct {
> +    int64_t seek_point;   ///< seekpoint in microseconds
> +    char format_name[16];
> +    char file_name[255];
> +
> +    AVFormatContext *format_ctx;
> +    int stream_idx;
> +    AVCodecContext *codec_ctx;



> +    int is_done;
> +    AVFrame *frame;   ///< video frame to store the decoded images in
> +
> +    int w, h;
> +    AVFilterBufferRef *picref;
> +} MovieContext;
> +
> +static int movie_init(AVFilterContext *ctx)
> +{
> +    MovieContext *movie = ctx->priv;
> +    AVInputFormat *iformat = NULL;
> +    AVCodec *codec;
> +    int ret;
> +    int64_t timestamp;
> +
> +    av_register_all();
> +
> +    // Try to find the movie format (container)
> +    iformat = *movie->format_name ? av_find_input_format(movie->format_name) : NULL;
> +
> +    movie->format_ctx = NULL;
> +    if ((ret = av_open_input_file(&movie->format_ctx, movie->file_name, iformat, 0, NULL)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Failed to av_open_input_file '%s'\n", movie->file_name);
> +        return ret;
> +    }
> +    if ((ret = av_find_stream_info(movie->format_ctx)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to find stream info\n");
> +        return ret;
> +    }
> +
> +    // if seeking requested, we execute it
> +    if (movie->seek_point > 0) {
> +        timestamp = movie->seek_point;
> +        // add the stream start time, should it exist
> +        if (movie->format_ctx->start_time != AV_NOPTS_VALUE)
> +            timestamp += movie->format_ctx->start_time;
> +        if (av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD) < 0) {
> +            av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n",
> +                   movie->file_name, timestamp);
> +        }
> +    }
> +
> +    /* select the video stream */
> +    if ((ret = av_find_best_stream(movie->format_ctx, AVMEDIA_TYPE_VIDEO,
> +                                   movie->stream_idx, -1, NULL, 0)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "No video stream with index '%d' found\n",
> +               movie->stream_idx);
> +        return ret;
> +    }
> +    movie->stream_idx = ret;
> +    movie->codec_ctx = movie->format_ctx->streams[movie->stream_idx]->codec;
> +
> +    /*
> +     * So now we've got a pointer to the so-called codec context for our video
> +     * stream, but we still have to find the actual codec and open it.
> +     */
> +    codec = avcodec_find_decoder(movie->codec_ctx->codec_id);
> +    if (!codec) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if ((ret = avcodec_open(movie->codec_ctx, codec)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n");
> +        return ret;
> +    }
> +
> +    if (!(movie->frame = avcodec_alloc_frame()) ) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to alloc frame\n");
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    movie->w = movie->codec_ctx->width;
> +    movie->h = movie->codec_ctx->height;
> +
> +    av_log(ctx, AV_LOG_INFO, "seek:%lld format:%s file:%s stream_idx:%d\n",
> +           movie->seek_point, movie->format_name, movie->file_name,
> +           movie->stream_idx);
> +
> +    return 0;
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    MovieContext *movie = ctx->priv;
> +
> +    movie->stream_idx = -1;
> +
> +    if (!args || sscanf(args, "%"PRId64":%15[^:]:%255[^:]:%d",
> +                        &movie->seek_point, movie->format_name,
> +                        movie->file_name, &movie->stream_idx) < 3) {
> +        av_log(ctx, AV_LOG_ERROR, "Expected at least 3 arguments in arguments list: '%s'\n", args);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (movie->seek_point < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid negative value for seekpoint:%"PRId64"\n",
> +               movie->seek_point);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    return movie_init(ctx);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    MovieContext *movie = ctx->priv;
> +
> +    if (movie->codec_ctx)
> +        avcodec_close(movie->codec_ctx);
> +    if (movie->format_ctx)
> +        av_close_input_file(movie->format_ctx);
> +    if (movie->picref) {
> +        av_free(movie->picref->video);
> +        av_freep(&movie->picref);
> +    }
> +    av_freep(&movie->frame);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    MovieContext *movie = ctx->priv;
> +    enum PixelFormat pix_fmts[] = { movie->codec_ctx->pix_fmt, PIX_FMT_NONE };
> +
> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +static int config_output_props(AVFilterLink *outlink)
> +{
> +    MovieContext *movie = outlink->src->priv;
> +
> +    outlink->w = movie->w;
> +    outlink->h = movie->h;
> +    outlink->time_base = movie->format_ctx->streams[movie->stream_idx]->time_base;
> +
> +    return 0;
> +}
> +
> +static int movie_get_frame(AVFilterLink *outlink)
> +{
> +    MovieContext *movie = outlink->src->priv;
> +    AVPacket packet;
> +    int frame_finished;
> +
> +    if (movie->is_done == 1)
> +        return 0;
> +
> +    while (av_read_frame(movie->format_ctx, &packet) >= 0) {

missing EAGAIN handling


    
> +        // Is this a packet from the video stream?
> +        if (packet.stream_index == movie->stream_idx) {
> +            // Decode video frame
> +            avcodec_decode_video2(movie->codec_ctx, movie->frame, &frame_finished, &packet);
> +
> +            // Did we get a video frame?
> +            if (frame_finished) {
> +                movie->picref =
> +                    avfilter_get_video_buffer_ref_from_arrays(movie->frame->data, movie->frame->linesize,
> +                                                              AV_PERM_READ,
> +                                                              outlink->format, outlink->w, outlink->h);

> +                movie->picref->pts = packet.pts;

this is wrong


> +                movie->picref->pos = packet.pos;

so is this

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

I hate to see young programmers poisoned by the kind of thinking
Ulrich Drepper puts forward since it is simply too narrow -- Roman Shaposhnik
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20101230/4d42280d/attachment.pgp>



More information about the ffmpeg-devel mailing list