Browse code

src_movie: implement multiple outputs.

The audio and video code paths were too different,
most of the decoding has been rewritten.

Nicolas George authored on 2012/07/19 08:03:20
Showing 2 changed files
... ...
@@ -960,35 +960,8 @@ aevalsrc="0.1*sin(2*PI*(360-2.5/2)*t) : 0.1*sin(2*PI*(360+2.5/2)*t)"
960 960
 
961 961
 @section amovie
962 962
 
963
-Read an audio stream from a movie container.
964
-
965
-It accepts the syntax: @var{movie_name}[:@var{options}] where
966
-@var{movie_name} is the name of the resource to read (not necessarily
967
-a file but also a device or a stream accessed through some protocol),
968
-and @var{options} is an optional sequence of @var{key}=@var{value}
969
-pairs, separated by ":".
970
-
971
-The description of the accepted options follows.
972
-
973
-@table @option
974
-
975
-@item format_name, f
976
-Specify the format assumed for the movie to read, and can be either
977
-the name of a container or an input device. If not specified the
978
-format is guessed from @var{movie_name} or by probing.
979
-
980
-@item seek_point, sp
981
-Specify the seek point in seconds, the frames will be output
982
-starting from this seek point, the parameter is evaluated with
983
-@code{av_strtod} so the numerical value may be suffixed by an IS
984
-postfix. Default value is "0".
985
-
986
-@item stream_index, si
987
-Specify the index of the audio stream to read. If the value is -1,
988
-the best suited audio stream will be automatically selected. Default
989
-value is "-1".
990
-
991
-@end table
963
+This is the same as @ref{src_movie} source, except it selects an audio
964
+stream by default.
992 965
 
993 966
 @section anullsrc
994 967
 
... ...
@@ -3639,9 +3612,10 @@ to the pad with identifier "in".
3639 3639
 "color=c=red@@0.2:s=qcif:r=10 [color]; [in][color] overlay [out]"
3640 3640
 @end example
3641 3641
 
3642
+@anchor{src_movie}
3642 3643
 @section movie
3643 3644
 
3644
-Read a video stream from a movie container.
3645
+Read audio and/or video stream(s) from a movie container.
3645 3646
 
3646 3647
 It accepts the syntax: @var{movie_name}[:@var{options}] where
3647 3648
 @var{movie_name} is the name of the resource to read (not necessarily
... ...
@@ -3664,13 +3638,22 @@ starting from this seek point, the parameter is evaluated with
3664 3664
 @code{av_strtod} so the numerical value may be suffixed by an IS
3665 3665
 postfix. Default value is "0".
3666 3666
 
3667
+@item streams, s
3668
+Specifies the streams to read. Several streams can be specified, separated
3669
+by "+". The source will then have as many outputs, in the same order. The
3670
+syntax is explained in the @ref{Stream specifiers} chapter. Two special
3671
+names, "dv" and "da" specify respectively the default (best suited) video
3672
+and audio stream. Default is "dv", or "da" if the filter is called as
3673
+"amovie".
3674
+
3667 3675
 @item stream_index, si
3668 3676
 Specifies the index of the video stream to read. If the value is -1,
3669 3677
 the best suited video stream will be automatically selected. Default
3670
-value is "-1".
3678
+value is "-1". Deprecated. If the filter is called "amovie", it will select
3679
+audio instead of video.
3671 3680
 
3672 3681
 @item loop
3673
-Specifies how many times to read the video stream in sequence.
3682
+Specifies how many times to read the stream in sequence.
3674 3683
 If the value is less than 1, the stream will be read again and again.
3675 3684
 Default value is "1".
3676 3685
 
... ...
@@ -3699,6 +3682,10 @@ movie=in.avi:seek_point=3.2, scale=180:-1, setpts=PTS-STARTPTS [movie];
3699 3699
 movie=/dev/video0:f=video4linux2, scale=180:-1, setpts=PTS-STARTPTS [movie];
3700 3700
 [in] setpts=PTS-STARTPTS, [movie] overlay=16:16 [out]
3701 3701
 
3702
+# read the first video stream and the audio stream with id 0x81 from
3703
+# dvd.vob; the video is connected to the pad named "video" and the audio is
3704
+# connected to the pad named "audio":
3705
+movie=dvd.vob:s=v:0+#0x81 [video] [audio]
3702 3706
 @end example
3703 3707
 
3704 3708
 @section mptestsrc
... ...
@@ -25,15 +25,16 @@
25 25
  *
26 26
  * @todo use direct rendering (no allocation of a new frame)
27 27
  * @todo support a PTS correction mechanism
28
- * @todo support more than one output stream
29 28
  */
30 29
 
31 30
 /* #define DEBUG */
32 31
 
33 32
 #include <float.h>
34 33
 #include "libavutil/avstring.h"
34
+#include "libavutil/avassert.h"
35 35
 #include "libavutil/opt.h"
36 36
 #include "libavutil/imgutils.h"
37
+#include "libavutil/timestamp.h"
37 38
 #include "libavformat/avformat.h"
38 39
 #include "audio.h"
39 40
 #include "avcodec.h"
... ...
@@ -42,11 +43,10 @@
42 42
 #include "internal.h"
43 43
 #include "video.h"
44 44
 
45
-typedef enum {
46
-    STATE_DECODING,
47
-    STATE_FLUSHING,
48
-    STATE_DONE,
49
-} MovieState;
45
+typedef struct {
46
+    AVStream *st;
47
+    int done;
48
+} MovieStream;
50 49
 
51 50
 typedef struct {
52 51
     /* common A/V fields */
... ...
@@ -55,22 +55,18 @@ typedef struct {
55 55
     double seek_point_d;
56 56
     char *format_name;
57 57
     char *file_name;
58
-    int stream_index;
58
+    char *stream_specs; /**< user-provided list of streams, separated by + */
59
+    int stream_index; /**< for compatibility */
59 60
     int loop_count;
60 61
 
61 62
     AVFormatContext *format_ctx;
62
-    AVCodecContext *codec_ctx;
63
-    MovieState state;
63
+    int eof;
64
+    AVPacket pkt, pkt0;
64 65
     AVFrame *frame;   ///< video frame to store the decoded images in
65 66
 
66
-    /* video-only fields */
67
-    int w, h;
68
-    AVFilterBufferRef *picref;
69
-
70
-    /* audio-only fields */
71
-    int bps;            ///< bytes per sample
72
-    AVPacket pkt, pkt0;
73
-    AVFilterBufferRef *samplesref;
67
+    int max_stream_index; /**< max stream # actually used for output */
68
+    MovieStream *st; /**< array of all streams, one per output */
69
+    int *out_index; /**< stream number -> output number map, or -1 */
74 70
 } MovieContext;
75 71
 
76 72
 #define OFFSET(x) offsetof(MovieContext, x)
... ...
@@ -78,8 +74,10 @@ typedef struct {
78 78
 static const AVOption movie_options[]= {
79 79
 {"format_name",  "set format name",         OFFSET(format_name),  AV_OPT_TYPE_STRING, {.str =  0},  CHAR_MIN, CHAR_MAX },
80 80
 {"f",            "set format name",         OFFSET(format_name),  AV_OPT_TYPE_STRING, {.str =  0},  CHAR_MIN, CHAR_MAX },
81
-{"stream_index", "set stream index",        OFFSET(stream_index), AV_OPT_TYPE_INT,    {.dbl = -1},  -1,       INT_MAX  },
81
+{"streams",      "set streams",             OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str =  0},  CHAR_MAX, CHAR_MAX },
82
+{"s",            "set streams",             OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str =  0},  CHAR_MAX, CHAR_MAX },
82 83
 {"si",           "set stream index",        OFFSET(stream_index), AV_OPT_TYPE_INT,    {.dbl = -1},  -1,       INT_MAX  },
84
+{"stream_index", "set stream index",        OFFSET(stream_index), AV_OPT_TYPE_INT,    {.dbl = -1},  -1,       INT_MAX  },
83 85
 {"seek_point",   "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, {.dbl =  0},  0,        (INT64_MAX-1) / 1000000 },
84 86
 {"sp",           "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, {.dbl =  0},  0,        (INT64_MAX-1) / 1000000 },
85 87
 {"loop",         "set loop count",          OFFSET(loop_count),   AV_OPT_TYPE_INT,    {.dbl =  1},  0,        INT_MAX  },
... ...
@@ -88,14 +86,91 @@ static const AVOption movie_options[]= {
88 88
 
89 89
 AVFILTER_DEFINE_CLASS(movie);
90 90
 
91
-static av_cold int movie_common_init(AVFilterContext *ctx, const char *args,
92
-                                     enum AVMediaType type)
91
+static int movie_config_output_props(AVFilterLink *outlink);
92
+static int movie_request_frame(AVFilterLink *outlink);
93
+
94
+static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec)
95
+{
96
+    int i, ret, already = 0, stream_id = -1;
97
+    char type_char, dummy;
98
+    AVStream *found = NULL;
99
+    enum AVMediaType type;
100
+
101
+    ret = sscanf(spec, "d%[av]%d%c", &type_char, &stream_id, &dummy);
102
+    if (ret >= 1 && ret <= 2) {
103
+        type = type_char == 'v' ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
104
+        ret = av_find_best_stream(avf, type, stream_id, -1, NULL, 0);
105
+        if (ret < 0) {
106
+            av_log(log, AV_LOG_ERROR, "No %s stream with index '%d' found\n",
107
+                   av_get_media_type_string(type), stream_id);
108
+            return NULL;
109
+        }
110
+        return avf->streams[ret];
111
+    }
112
+    for (i = 0; i < avf->nb_streams; i++) {
113
+        ret = avformat_match_stream_specifier(avf, avf->streams[i], spec);
114
+        if (ret < 0) {
115
+            av_log(log, AV_LOG_ERROR,
116
+                   "Invalid stream specifier \"%s\"\n", spec);
117
+            return NULL;
118
+        }
119
+        if (!ret)
120
+            continue;
121
+        if (avf->streams[i]->discard != AVDISCARD_ALL) {
122
+            already++;
123
+            continue;
124
+        }
125
+        if (found) {
126
+            av_log(log, AV_LOG_WARNING,
127
+                   "Ambiguous stream specifier \"%s\", using #%d\n", spec, i);
128
+            break;
129
+        }
130
+        found = avf->streams[i];
131
+    }
132
+    if (!found) {
133
+        av_log(log, AV_LOG_WARNING, "Stream specifier \"%s\" %s\n", spec,
134
+               already ? "matched only already used streams" :
135
+                         "did not match any stream");
136
+        return NULL;
137
+    }
138
+    if (found->codec->codec_type != AVMEDIA_TYPE_VIDEO &&
139
+        found->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
140
+        av_log(log, AV_LOG_ERROR, "Stream specifier \"%s\" matched a %s stream,"
141
+               "currently unsupported by libavfilter\n", spec,
142
+               av_get_media_type_string(found->codec->codec_type));
143
+        return NULL;
144
+    }
145
+    return found;
146
+}
147
+
148
+static int open_stream(void *log, MovieStream *st)
149
+{
150
+    AVCodec *codec;
151
+    int ret;
152
+
153
+    codec = avcodec_find_decoder(st->st->codec->codec_id);
154
+    if (!codec) {
155
+        av_log(log, AV_LOG_ERROR, "Failed to find any codec\n");
156
+        return AVERROR(EINVAL);
157
+    }
158
+
159
+    if ((ret = avcodec_open2(st->st->codec, codec, NULL)) < 0) {
160
+        av_log(log, AV_LOG_ERROR, "Failed to open codec\n");
161
+        return ret;
162
+    }
163
+
164
+    return 0;
165
+}
166
+
167
+static av_cold int movie_init(AVFilterContext *ctx, const char *args)
93 168
 {
94 169
     MovieContext *movie = ctx->priv;
95 170
     AVInputFormat *iformat = NULL;
96
-    AVCodec *codec;
97 171
     int64_t timestamp;
98
-    int ret;
172
+    int nb_streams, ret, i;
173
+    char default_streams[16], *stream_specs, *spec, *cursor;
174
+    char name[16];
175
+    AVStream *st;
99 176
 
100 177
     movie->class = &movie_class;
101 178
     av_opt_set_defaults(movie);
... ...
@@ -114,6 +189,23 @@ static av_cold int movie_common_init(AVFilterContext *ctx, const char *args,
114 114
 
115 115
     movie->seek_point = movie->seek_point_d * 1000000 + 0.5;
116 116
 
117
+    stream_specs = movie->stream_specs;
118
+    if (!stream_specs) {
119
+        snprintf(default_streams, sizeof(default_streams), "d%c%d",
120
+                 !strcmp(ctx->filter->name, "amovie") ? 'a' : 'v',
121
+                 movie->stream_index);
122
+        stream_specs = default_streams;
123
+    }
124
+    for (cursor = stream_specs, nb_streams = 1; *cursor; cursor++)
125
+        if (*cursor == '+')
126
+            nb_streams++;
127
+
128
+    if (movie->loop_count != 1 && nb_streams != 1) {
129
+        av_log(ctx, AV_LOG_ERROR,
130
+               "Loop with several streams is currently unsupported\n");
131
+        return AVERROR_PATCHWELCOME;
132
+    }
133
+
117 134
     av_register_all();
118 135
 
119 136
     // Try to find the movie format (container)
... ...
@@ -148,358 +240,358 @@ static av_cold int movie_common_init(AVFilterContext *ctx, const char *args,
148 148
         }
149 149
     }
150 150
 
151
-    /* select the media stream */
152
-    if ((ret = av_find_best_stream(movie->format_ctx, type,
153
-                                   movie->stream_index, -1, NULL, 0)) < 0) {
154
-        av_log(ctx, AV_LOG_ERROR, "No %s stream with index '%d' found\n",
155
-               av_get_media_type_string(type), movie->stream_index);
156
-        return ret;
151
+    for (i = 0; i < movie->format_ctx->nb_streams; i++)
152
+        movie->format_ctx->streams[i]->discard = AVDISCARD_ALL;
153
+
154
+    movie->st = av_calloc(nb_streams, sizeof(*movie->st));
155
+    if (!movie->st)
156
+        return AVERROR(ENOMEM);
157
+
158
+    for (i = 0; i < nb_streams; i++) {
159
+        spec = av_strtok(stream_specs, "+", &cursor);
160
+        if (!spec)
161
+            return AVERROR_BUG;
162
+        stream_specs = NULL; /* for next strtok */
163
+        st = find_stream(ctx, movie->format_ctx, spec);
164
+        if (!st)
165
+            return AVERROR(EINVAL);
166
+        st->discard = AVDISCARD_DEFAULT;
167
+        movie->st[i].st = st;
168
+        movie->max_stream_index = FFMAX(movie->max_stream_index, st->index);
157 169
     }
158
-    movie->stream_index = ret;
159
-    movie->codec_ctx = movie->format_ctx->streams[movie->stream_index]->codec;
160
-
161
-    /*
162
-     * So now we've got a pointer to the so-called codec context for our video
163
-     * stream, but we still have to find the actual codec and open it.
164
-     */
165
-    codec = avcodec_find_decoder(movie->codec_ctx->codec_id);
166
-    if (!codec) {
167
-        av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n");
168
-        return AVERROR(EINVAL);
170
+    if (av_strtok(NULL, "+", &cursor))
171
+        return AVERROR_BUG;
172
+
173
+    movie->out_index = av_calloc(movie->max_stream_index + 1,
174
+                                 sizeof(*movie->out_index));
175
+    if (!movie->out_index)
176
+        return AVERROR(ENOMEM);
177
+    for (i = 0; i <= movie->max_stream_index; i++)
178
+        movie->out_index[i] = -1;
179
+    for (i = 0; i < nb_streams; i++)
180
+        movie->out_index[movie->st[i].st->index] = i;
181
+
182
+    for (i = 0; i < nb_streams; i++) {
183
+        AVFilterPad pad = { 0 };
184
+        snprintf(name, sizeof(name), "out%d", i);
185
+        pad.type          = movie->st[i].st->codec->codec_type;
186
+        pad.name          = av_strdup(name);
187
+        pad.config_props  = movie_config_output_props;
188
+        pad.request_frame = movie_request_frame;
189
+        ff_insert_outpad(ctx, i, &pad);
190
+        ret = open_stream(ctx, &movie->st[i]);
191
+        if (ret < 0)
192
+            return ret;
169 193
     }
170 194
 
171
-    if ((ret = avcodec_open2(movie->codec_ctx, codec, NULL)) < 0) {
172
-        av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n");
173
-        return ret;
195
+    if (!(movie->frame = avcodec_alloc_frame()) ) {
196
+        av_log(log, AV_LOG_ERROR, "Failed to alloc frame\n");
197
+        return AVERROR(ENOMEM);
174 198
     }
175 199
 
176 200
     av_log(ctx, AV_LOG_VERBOSE, "seek_point:%"PRIi64" format_name:%s file_name:%s stream_index:%d\n",
177 201
            movie->seek_point, movie->format_name, movie->file_name,
178 202
            movie->stream_index);
179 203
 
180
-    if (!(movie->frame = avcodec_alloc_frame()) ) {
181
-        av_log(ctx, AV_LOG_ERROR, "Failed to alloc frame\n");
182
-        return AVERROR(ENOMEM);
183
-    }
184
-
185 204
     return 0;
186 205
 }
187 206
 
188
-static av_cold void movie_common_uninit(AVFilterContext *ctx)
207
+static av_cold void movie_uninit(AVFilterContext *ctx)
189 208
 {
190 209
     MovieContext *movie = ctx->priv;
210
+    int i;
191 211
 
192
-    av_free(movie->file_name);
193
-    av_free(movie->format_name);
194
-    if (movie->codec_ctx)
195
-        avcodec_close(movie->codec_ctx);
212
+    for (i = 0; i < ctx->nb_outputs; i++) {
213
+        av_freep(&ctx->output_pads[i].name);
214
+        if (movie->st[i].st)
215
+            avcodec_close(movie->st[i].st->codec);
216
+    }
217
+    av_opt_free(movie);
218
+    av_freep(&movie->file_name);
219
+    av_freep(&movie->st);
220
+    av_freep(&movie->out_index);
221
+    av_freep(&movie->frame);
196 222
     if (movie->format_ctx)
197 223
         avformat_close_input(&movie->format_ctx);
198
-
199
-    avfilter_unref_buffer(movie->picref);
200
-    av_freep(&movie->frame);
201
-
202
-    avfilter_unref_buffer(movie->samplesref);
203 224
 }
204 225
 
205
-#if CONFIG_MOVIE_FILTER
206
-
207
-static av_cold int movie_init(AVFilterContext *ctx, const char *args)
226
+static int movie_query_formats(AVFilterContext *ctx)
208 227
 {
209 228
     MovieContext *movie = ctx->priv;
210
-    int ret;
229
+    int list[] = { 0, -1 };
230
+    int64_t list64[] = { 0, -1 };
231
+    int i;
232
+
233
+    for (i = 0; i < ctx->nb_outputs; i++) {
234
+        MovieStream *st = &movie->st[i];
235
+        AVCodecContext *c = st->st->codec;
236
+        AVFilterLink *outlink = ctx->outputs[i];
237
+
238
+        switch (c->codec_type) {
239
+        case AVMEDIA_TYPE_VIDEO:
240
+            list[0] = c->pix_fmt;
241
+            ff_formats_ref(ff_make_format_list(list), &outlink->in_formats);
242
+            break;
243
+        case AVMEDIA_TYPE_AUDIO:
244
+            list[0] = c->sample_fmt;
245
+            ff_formats_ref(ff_make_format_list(list), &outlink->in_formats);
246
+            list[0] = c->sample_rate;
247
+            ff_formats_ref(ff_make_format_list(list), &outlink->in_samplerates);
248
+            list64[0] = c->channel_layout ? c->channel_layout :
249
+                        av_get_default_channel_layout(c->channels);
250
+            ff_channel_layouts_ref(avfilter_make_format64_list(list64),
251
+                                   &outlink->in_channel_layouts);
252
+            break;
253
+        }
254
+    }
211 255
 
212
-    if ((ret = movie_common_init(ctx, args, AVMEDIA_TYPE_VIDEO)) < 0)
213
-        return ret;
256
+    return 0;
257
+}
214 258
 
215
-    movie->w = movie->codec_ctx->width;
216
-    movie->h = movie->codec_ctx->height;
259
+static int movie_config_output_props(AVFilterLink *outlink)
260
+{
261
+    AVFilterContext *ctx = outlink->src;
262
+    MovieContext *movie  = ctx->priv;
263
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
264
+    MovieStream *st = &movie->st[out_id];
265
+    AVCodecContext *c = st->st->codec;
266
+
267
+    outlink->time_base = st->st->time_base;
268
+
269
+    switch (c->codec_type) {
270
+    case AVMEDIA_TYPE_VIDEO:
271
+        outlink->w          = c->width;
272
+        outlink->h          = c->height;
273
+        outlink->frame_rate = st->st->r_frame_rate;
274
+        break;
275
+    case AVMEDIA_TYPE_AUDIO:
276
+        break;
277
+    }
217 278
 
218 279
     return 0;
219 280
 }
220 281
 
221
-static int movie_query_formats(AVFilterContext *ctx)
282
+static AVFilterBufferRef *frame_to_buf(enum AVMediaType type, AVFrame *frame,
283
+                                       AVFilterLink *outlink)
222 284
 {
223
-    MovieContext *movie = ctx->priv;
224
-    enum PixelFormat pix_fmts[] = { movie->codec_ctx->pix_fmt, PIX_FMT_NONE };
285
+    AVFilterBufferRef *buf, *copy;
286
+
287
+    buf = avfilter_get_buffer_ref_from_frame(type, frame,
288
+                                             AV_PERM_WRITE |
289
+                                             AV_PERM_PRESERVE |
290
+                                             AV_PERM_REUSE2);
291
+    if (!buf)
292
+        return NULL;
293
+    buf->pts = av_frame_get_best_effort_timestamp(frame);
294
+    copy = ff_copy_buffer_ref(outlink, buf);
295
+    if (!copy)
296
+        return NULL;
297
+    buf->buf->data[0] = NULL; /* it belongs to the frame */
298
+    avfilter_unref_buffer(buf);
299
+    return copy;
300
+}
225 301
 
226
-    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
227
-    return 0;
302
+static char *describe_bufref_to_str(char *dst, size_t dst_size,
303
+                                    AVFilterBufferRef *buf,
304
+                                    AVFilterLink *link)
305
+{
306
+    switch (buf->type) {
307
+    case AVMEDIA_TYPE_VIDEO:
308
+        snprintf(dst, dst_size,
309
+                 "video pts:%s time:%s pos:%"PRId64" size:%dx%d aspect:%d/%d",
310
+                 av_ts2str(buf->pts), av_ts2timestr(buf->pts, &link->time_base),
311
+                 buf->pos, buf->video->w, buf->video->h,
312
+                 buf->video->sample_aspect_ratio.num,
313
+                 buf->video->sample_aspect_ratio.den);
314
+                 break;
315
+    case AVMEDIA_TYPE_AUDIO:
316
+        snprintf(dst, dst_size,
317
+                 "audio pts:%s time:%s pos:%"PRId64" samples:%d",
318
+                 av_ts2str(buf->pts), av_ts2timestr(buf->pts, &link->time_base),
319
+                 buf->pos, buf->audio->nb_samples);
320
+                 break;
321
+    default:
322
+        snprintf(dst, dst_size, "%s BUG", av_get_media_type_string(buf->type));
323
+        break;
324
+    }
325
+    return dst;
228 326
 }
229 327
 
230
-static int movie_config_output_props(AVFilterLink *outlink)
328
+#define describe_bufref(buf, link) \
329
+    describe_bufref_to_str((char[1024]){0}, 1024, buf, link)
330
+
331
+static int rewind_file(AVFilterContext *ctx)
231 332
 {
232
-    MovieContext *movie = outlink->src->priv;
333
+    MovieContext *movie = ctx->priv;
334
+    int64_t timestamp = movie->seek_point;
335
+    int ret, i;
233 336
 
234
-    outlink->w = movie->w;
235
-    outlink->h = movie->h;
236
-    outlink->time_base = movie->format_ctx->streams[movie->stream_index]->time_base;
337
+    if (movie->format_ctx->start_time != AV_NOPTS_VALUE)
338
+        timestamp += movie->format_ctx->start_time;
339
+    ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD);
340
+    if (ret < 0) {
341
+        av_log(ctx, AV_LOG_ERROR, "Unable to loop: %s\n", av_err2str(ret));
342
+        movie->loop_count = 1; /* do not try again */
343
+        return ret;
344
+    }
237 345
 
346
+    for (i = 0; i < ctx->nb_outputs; i++) {
347
+        avcodec_flush_buffers(movie->st[i].st->codec);
348
+        movie->st[i].done = 0;
349
+    }
350
+    movie->eof = 0;
238 351
     return 0;
239 352
 }
240 353
 
241
-static int movie_get_frame(AVFilterLink *outlink)
354
+/**
355
+ * Try to push a frame to the requested output.
356
+ *
357
+ * @return  1 if a frame was pushed on the requested output,
358
+ *          0 if another attempt is possible,
359
+ *          <0 AVERROR code
360
+ */
361
+static int movie_push_frame(AVFilterContext *ctx, unsigned out_id)
242 362
 {
243
-    MovieContext *movie = outlink->src->priv;
244
-    AVPacket pkt;
245
-    int ret = 0, frame_decoded;
246
-    AVStream *st = movie->format_ctx->streams[movie->stream_index];
247
-
248
-    if (movie->state == STATE_DONE)
249
-        return 0;
250
-
251
-    while (1) {
252
-        if (movie->state == STATE_DECODING) {
253
-            ret = av_read_frame(movie->format_ctx, &pkt);
254
-            if (ret == AVERROR_EOF) {
255
-                int64_t timestamp;
363
+    MovieContext *movie = ctx->priv;
364
+    AVPacket *pkt = &movie->pkt;
365
+    MovieStream *st;
366
+    int ret, got_frame = 0, pkt_out_id;
367
+    AVFilterLink *outlink;
368
+    AVFilterBufferRef *buf;
369
+
370
+    if (!pkt->size) {
371
+        if (movie->eof) {
372
+            if (movie->st[out_id].done) {
256 373
                 if (movie->loop_count != 1) {
257
-                    timestamp = movie->seek_point;
258
-                    if (movie->format_ctx->start_time != AV_NOPTS_VALUE)
259
-                        timestamp += movie->format_ctx->start_time;
260
-                    if (av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD) < 0) {
261
-                        movie->state = STATE_FLUSHING;
262
-                    } else if (movie->loop_count>1)
263
-                        movie->loop_count--;
264
-                    continue;
265
-                } else {
266
-                    movie->state = STATE_FLUSHING;
374
+                    ret = rewind_file(ctx);
375
+                    if (ret < 0)
376
+                        return ret;
377
+                    movie->loop_count -= movie->loop_count > 1;
378
+                    av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n");
379
+                    return 0; /* retry */
267 380
                 }
268
-            } else if (ret < 0)
269
-                break;
270
-        }
271
-
272
-        // Is this a packet from the video stream?
273
-        if (pkt.stream_index == movie->stream_index || movie->state == STATE_FLUSHING) {
274
-            avcodec_decode_video2(movie->codec_ctx, movie->frame, &frame_decoded, &pkt);
275
-
276
-            if (frame_decoded) {
277
-                /* FIXME: avoid the memcpy */
278
-                movie->picref = ff_get_video_buffer(outlink, AV_PERM_WRITE | AV_PERM_PRESERVE |
279
-                                                    AV_PERM_REUSE2, outlink->w, outlink->h);
280
-                av_image_copy(movie->picref->data, movie->picref->linesize,
281
-                              (void*)movie->frame->data,  movie->frame->linesize,
282
-                              movie->picref->format, outlink->w, outlink->h);
283
-                avfilter_copy_frame_props(movie->picref, movie->frame);
284
-
285
-                /* FIXME: use a PTS correction mechanism as that in
286
-                 * ffplay.c when some API will be available for that */
287
-                /* use pkt_dts if pkt_pts is not available */
288
-                movie->picref->pts = movie->frame->pkt_pts == AV_NOPTS_VALUE ?
289
-                    movie->frame->pkt_dts : movie->frame->pkt_pts;
290
-
291
-                if (!movie->frame->sample_aspect_ratio.num)
292
-                    movie->picref->video->sample_aspect_ratio = st->sample_aspect_ratio;
293
-                av_dlog(outlink->src,
294
-                        "movie_get_frame(): file:'%s' pts:%"PRId64" time:%lf pos:%"PRId64" aspect:%d/%d\n",
295
-                        movie->file_name, movie->picref->pts,
296
-                        (double)movie->picref->pts * av_q2d(st->time_base),
297
-                        movie->picref->pos,
298
-                        movie->picref->video->sample_aspect_ratio.num,
299
-                        movie->picref->video->sample_aspect_ratio.den);
300
-                // We got it. Free the packet since we are returning
301
-                av_free_packet(&pkt);
302
-
303
-                return 0;
304
-            } else if (movie->state == STATE_FLUSHING) {
305
-                movie->state = STATE_DONE;
306
-                av_free_packet(&pkt);
307 381
                 return AVERROR_EOF;
308 382
             }
383
+            /* packet is already ready for flushing */
384
+        } else {
385
+            ret = av_read_frame(movie->format_ctx, &movie->pkt0);
386
+            if (ret < 0) {
387
+                av_init_packet(&movie->pkt0); /* ready for flushing */
388
+                *pkt = movie->pkt0;
389
+                if (ret == AVERROR_EOF) {
390
+                    movie->eof = 1;
391
+                    return 0; /* start flushing */
392
+                }
393
+                return ret;
394
+            }
395
+            *pkt = movie->pkt0;
309 396
         }
310
-        // Free the packet that was allocated by av_read_frame
311
-        av_free_packet(&pkt);
312 397
     }
313 398
 
314
-    return ret;
399
+    pkt_out_id = pkt->stream_index > movie->max_stream_index ? -1 :
400
+                 movie->out_index[pkt->stream_index];
401
+    if (pkt_out_id < 0) {
402
+        av_free_packet(&movie->pkt0);
403
+        pkt->size = 0; /* ready for next run */
404
+        pkt->data = NULL;
405
+        return 0;
406
+    }
407
+    st = &movie->st[pkt_out_id];
408
+    outlink = ctx->outputs[pkt_out_id];
409
+
410
+    switch (st->st->codec->codec_type) {
411
+    case AVMEDIA_TYPE_VIDEO:
412
+        ret = avcodec_decode_video2(st->st->codec, movie->frame, &got_frame, pkt);
413
+        break;
414
+    case AVMEDIA_TYPE_AUDIO:
415
+        ret = avcodec_decode_audio4(st->st->codec, movie->frame, &got_frame, pkt);
416
+        break;
417
+    default:
418
+        ret = AVERROR(ENOSYS);
419
+        break;
420
+    }
421
+    if (ret < 0) {
422
+        av_log(ctx, AV_LOG_WARNING, "Decode error: %s\n", av_err2str(ret));
423
+        return 0;
424
+    }
425
+    if (!ret)
426
+        ret = pkt->size;
427
+
428
+    pkt->data += ret;
429
+    pkt->size -= ret;
430
+    if (pkt->size <= 0) {
431
+        av_free_packet(&movie->pkt0);
432
+        pkt->size = 0; /* ready for next run */
433
+        pkt->data = NULL;
434
+    }
435
+    if (!got_frame) {
436
+        if (!ret)
437
+            st->done = 1;
438
+        return 0;
439
+    }
440
+
441
+    buf = frame_to_buf(st->st->codec->codec_type, movie->frame, outlink);
442
+    if (!buf)
443
+        return AVERROR(ENOMEM);
444
+    av_dlog(ctx, "movie_push_frame(): file:'%s' %s\n", movie->file_name,
445
+            describe_bufref(buf, outlink));
446
+    switch (st->st->codec->codec_type) {
447
+    case AVMEDIA_TYPE_VIDEO:
448
+        if (!movie->frame->sample_aspect_ratio.num)
449
+            buf->video->sample_aspect_ratio = st->st->sample_aspect_ratio;
450
+        ff_start_frame(outlink, buf);
451
+        ff_draw_slice(outlink, 0, outlink->h, 1);
452
+        ff_end_frame(outlink);
453
+        break;
454
+    case AVMEDIA_TYPE_AUDIO:
455
+        ff_filter_samples(outlink, buf);
456
+        break;
457
+    }
458
+
459
+    return pkt_out_id == out_id;
315 460
 }
316 461
 
317 462
 static int movie_request_frame(AVFilterLink *outlink)
318 463
 {
319
-    AVFilterBufferRef *outpicref;
320
-    MovieContext *movie = outlink->src->priv;
464
+    AVFilterContext *ctx = outlink->src;
465
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
321 466
     int ret;
322 467
 
323
-    if (movie->state == STATE_DONE)
324
-        return AVERROR_EOF;
325
-    if ((ret = movie_get_frame(outlink)) < 0)
326
-        return ret;
327
-
328
-    outpicref = avfilter_ref_buffer(movie->picref, ~0);
329
-    if (!outpicref) {
330
-        ret = AVERROR(ENOMEM);
331
-        goto fail;
468
+    while (1) {
469
+        ret = movie_push_frame(ctx, out_id);
470
+        if (ret)
471
+            return FFMIN(ret, 0);
332 472
     }
333
-
334
-    ret = ff_start_frame(outlink, outpicref);
335
-    if (ret < 0)
336
-        goto fail;
337
-
338
-    ret = ff_draw_slice(outlink, 0, outlink->h, 1);
339
-    if (ret < 0)
340
-        goto fail;
341
-
342
-    ret = ff_end_frame(outlink);
343
-fail:
344
-    avfilter_unref_bufferp(&movie->picref);
345
-
346
-    return ret;
347 473
 }
348 474
 
475
+#if CONFIG_MOVIE_FILTER
476
+
349 477
 AVFilter avfilter_vsrc_movie = {
350 478
     .name          = "movie",
351 479
     .description   = NULL_IF_CONFIG_SMALL("Read from a movie source."),
352 480
     .priv_size     = sizeof(MovieContext),
353 481
     .init          = movie_init,
354
-    .uninit        = movie_common_uninit,
482
+    .uninit        = movie_uninit,
355 483
     .query_formats = movie_query_formats,
356 484
 
357 485
     .inputs    = (const AVFilterPad[]) {{ .name = NULL }},
358
-    .outputs   = (const AVFilterPad[]) {{ .name            = "default",
359
-                                          .type            = AVMEDIA_TYPE_VIDEO,
360
-                                          .request_frame   = movie_request_frame,
361
-                                          .config_props    = movie_config_output_props, },
362
-                                        { .name = NULL}},
486
+    .outputs   = (const AVFilterPad[]) {{ .name = NULL }},
363 487
 };
364 488
 
365 489
 #endif  /* CONFIG_MOVIE_FILTER */
366 490
 
367 491
 #if CONFIG_AMOVIE_FILTER
368 492
 
369
-static av_cold int amovie_init(AVFilterContext *ctx, const char *args)
370
-{
371
-    MovieContext *movie = ctx->priv;
372
-    int ret;
373
-
374
-    if ((ret = movie_common_init(ctx, args, AVMEDIA_TYPE_AUDIO)) < 0)
375
-        return ret;
376
-
377
-    movie->bps = av_get_bytes_per_sample(movie->codec_ctx->sample_fmt);
378
-    return 0;
379
-}
380
-
381
-static int amovie_query_formats(AVFilterContext *ctx)
382
-{
383
-    MovieContext *movie = ctx->priv;
384
-    AVCodecContext *c = movie->codec_ctx;
385
-
386
-    enum AVSampleFormat sample_fmts[] = { c->sample_fmt, -1 };
387
-    int sample_rates[] = { c->sample_rate, -1 };
388
-    int64_t chlayouts[] = { c->channel_layout ? c->channel_layout :
389
-                            av_get_default_channel_layout(c->channels), -1 };
390
-
391
-    ff_set_common_formats        (ctx, ff_make_format_list(sample_fmts));
392
-    ff_set_common_samplerates    (ctx, ff_make_format_list(sample_rates));
393
-    ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
394
-
395
-    return 0;
396
-}
397
-
398
-static int amovie_config_output_props(AVFilterLink *outlink)
399
-{
400
-    MovieContext *movie = outlink->src->priv;
401
-    AVCodecContext *c = movie->codec_ctx;
402
-
403
-    outlink->sample_rate = c->sample_rate;
404
-    outlink->time_base = movie->format_ctx->streams[movie->stream_index]->time_base;
405
-
406
-    return 0;
407
-}
408
-
409
-static int amovie_get_samples(AVFilterLink *outlink)
410
-{
411
-    MovieContext *movie = outlink->src->priv;
412
-    AVPacket pkt;
413
-    int ret, got_frame = 0;
414
-
415
-    if (!movie->pkt.size && movie->state == STATE_DONE)
416
-        return AVERROR_EOF;
417
-
418
-    /* check for another frame, in case the previous one was completely consumed */
419
-    if (!movie->pkt.size) {
420
-        while ((ret = av_read_frame(movie->format_ctx, &pkt)) >= 0) {
421
-            // Is this a packet from the selected stream?
422
-            if (pkt.stream_index != movie->stream_index) {
423
-                av_free_packet(&pkt);
424
-                continue;
425
-            } else {
426
-                movie->pkt0 = movie->pkt = pkt;
427
-                break;
428
-            }
429
-        }
430
-
431
-        if (ret == AVERROR_EOF) {
432
-            movie->state = STATE_DONE;
433
-            return ret;
434
-        }
435
-    }
436
-
437
-    /* decode and update the movie pkt */
438
-    avcodec_get_frame_defaults(movie->frame);
439
-    ret = avcodec_decode_audio4(movie->codec_ctx, movie->frame, &got_frame, &movie->pkt);
440
-    if (ret < 0) {
441
-        movie->pkt.size = 0;
442
-        return ret;
443
-    }
444
-    movie->pkt.data += ret;
445
-    movie->pkt.size -= ret;
446
-
447
-    /* wrap the decoded data in a samplesref */
448
-    if (got_frame) {
449
-        int nb_samples = movie->frame->nb_samples;
450
-        int data_size =
451
-            av_samples_get_buffer_size(NULL, movie->codec_ctx->channels,
452
-                                       nb_samples, movie->codec_ctx->sample_fmt, 1);
453
-        if (data_size < 0)
454
-            return data_size;
455
-        movie->samplesref =
456
-            ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples);
457
-        memcpy(movie->samplesref->data[0], movie->frame->data[0], data_size);
458
-        movie->samplesref->pts = movie->pkt.pts;
459
-        movie->samplesref->pos = movie->pkt.pos;
460
-        movie->samplesref->audio->sample_rate = movie->codec_ctx->sample_rate;
461
-    }
462
-
463
-    // We got it. Free the packet since we are returning
464
-    if (movie->pkt.size <= 0)
465
-        av_free_packet(&movie->pkt0);
466
-
467
-    return 0;
468
-}
469
-
470
-static int amovie_request_frame(AVFilterLink *outlink)
471
-{
472
-    MovieContext *movie = outlink->src->priv;
473
-    int ret;
474
-
475
-    if (movie->state == STATE_DONE)
476
-        return AVERROR_EOF;
477
-    do {
478
-        if ((ret = amovie_get_samples(outlink)) < 0)
479
-            return ret;
480
-    } while (!movie->samplesref);
481
-
482
-    ff_filter_samples(outlink, avfilter_ref_buffer(movie->samplesref, ~0));
483
-    avfilter_unref_buffer(movie->samplesref);
484
-    movie->samplesref = NULL;
485
-
486
-    return 0;
487
-}
488
-
489 493
 AVFilter avfilter_asrc_amovie = {
490 494
     .name          = "amovie",
491 495
     .description   = NULL_IF_CONFIG_SMALL("Read audio from a movie source."),
492 496
     .priv_size     = sizeof(MovieContext),
493
-    .init          = amovie_init,
494
-    .uninit        = movie_common_uninit,
495
-    .query_formats = amovie_query_formats,
497
+    .init          = movie_init,
498
+    .uninit        = movie_uninit,
499
+    .query_formats = movie_query_formats,
496 500
 
497 501
     .inputs    = (const AVFilterPad[]) {{ .name = NULL }},
498
-    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
499
-                                    .type            = AVMEDIA_TYPE_AUDIO,
500
-                                    .request_frame   = amovie_request_frame,
501
-                                    .config_props    = amovie_config_output_props, },
502
-                                  { .name = NULL}},
502
+    .outputs   = (const AVFilterPad[]) {{ .name = NULL }},
503 503
 };
504 504
 
505 505
 #endif /* CONFIG_AMOVIE_FILTER */