| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2010 Stefano Sabatini | ||
| 3 | * Copyright (c) 2008 Victor Paesa | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * movie video source | ||
| 25 | * | ||
| 26 | * @todo support a PTS correction mechanism | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include "config_components.h" | ||
| 30 | |||
| 31 | #include <stdint.h> | ||
| 32 | |||
| 33 | #include "libavutil/attributes.h" | ||
| 34 | #include "libavutil/avstring.h" | ||
| 35 | #include "libavutil/channel_layout.h" | ||
| 36 | #include "libavutil/mem.h" | ||
| 37 | #include "libavutil/opt.h" | ||
| 38 | #include "libavutil/internal.h" | ||
| 39 | |||
| 40 | #include "libavcodec/avcodec.h" | ||
| 41 | |||
| 42 | #include "libavformat/avformat.h" | ||
| 43 | |||
| 44 | #include "audio.h" | ||
| 45 | #include "avfilter.h" | ||
| 46 | #include "filters.h" | ||
| 47 | #include "formats.h" | ||
| 48 | #include "video.h" | ||
| 49 | |||
| 50 | typedef struct MovieStream { | ||
| 51 | AVFilterLink *link; | ||
| 52 | AVStream *st; | ||
| 53 | AVCodecContext *codec_ctx; | ||
| 54 | int64_t discontinuity_threshold; | ||
| 55 | int64_t last_pts; | ||
| 56 | AVFrame *frame; | ||
| 57 | int eof; | ||
| 58 | } MovieStream; | ||
| 59 | |||
| 60 | typedef struct MovieContext { | ||
| 61 | /* common A/V fields */ | ||
| 62 | const AVClass *class; | ||
| 63 | int64_t seek_point; ///< seekpoint in microseconds | ||
| 64 | double seek_point_d; | ||
| 65 | char *format_name; | ||
| 66 | char *file_name; | ||
| 67 | char *stream_specs; /**< user-provided list of streams, separated by + */ | ||
| 68 | int stream_index; /**< for compatibility */ | ||
| 69 | int loop_count; | ||
| 70 | int64_t discontinuity_threshold; | ||
| 71 | int64_t ts_offset; | ||
| 72 | int dec_threads; | ||
| 73 | |||
| 74 | AVPacket *pkt; | ||
| 75 | AVFormatContext *format_ctx; | ||
| 76 | |||
| 77 | int eof; | ||
| 78 | int max_stream_index; /**< max stream # actually used for output */ | ||
| 79 | MovieStream *st; /**< array of all streams, one per output */ | ||
| 80 | int *out_index; /**< stream number -> output number map, or -1 */ | ||
| 81 | AVDictionary *format_opts; | ||
| 82 | } MovieContext; | ||
| 83 | |||
| 84 | #define OFFSET(x) offsetof(MovieContext, x) | ||
| 85 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM | ||
| 86 | |||
| 87 | static const AVOption movie_options[]= { | ||
| 88 | { "filename", NULL, OFFSET(file_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, | ||
| 89 | { "format_name", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, | ||
| 90 | { "f", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, | ||
| 91 | { "stream_index", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, | ||
| 92 | { "si", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, | ||
| 93 | { "seek_point", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, | ||
| 94 | { "sp", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, | ||
| 95 | { "streams", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, FLAGS }, | ||
| 96 | { "s", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, FLAGS }, | ||
| 97 | { "loop", "set loop count", OFFSET(loop_count), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, FLAGS }, | ||
| 98 | { "discontinuity", "set discontinuity threshold", OFFSET(discontinuity_threshold), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, FLAGS }, | ||
| 99 | { "dec_threads", "set the number of threads for decoding", OFFSET(dec_threads), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, | ||
| 100 | { "format_opts", "set format options for the opened file", OFFSET(format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS}, | ||
| 101 | { NULL }, | ||
| 102 | }; | ||
| 103 | |||
| 104 | static int movie_config_output_props(AVFilterLink *outlink); | ||
| 105 | |||
| 106 | 16 | static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) | |
| 107 | { | ||
| 108 | 16 | int i, ret, already = 0, stream_id = -1; | |
| 109 | char type_char[2], dummy; | ||
| 110 | 16 | AVStream *found = NULL; | |
| 111 | enum AVMediaType type; | ||
| 112 | |||
| 113 | 16 | ret = sscanf(spec, "d%1[av]%d%c", type_char, &stream_id, &dummy); | |
| 114 |
2/4✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | if (ret >= 1 && ret <= 2) { |
| 115 | 16 | type = type_char[0] == 'v' ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO; | |
| 116 | 16 | ret = av_find_best_stream(avf, type, stream_id, -1, NULL, 0); | |
| 117 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (ret < 0) { |
| 118 | ✗ | av_log(log, AV_LOG_ERROR, "No %s stream with index '%d' found\n", | |
| 119 | av_get_media_type_string(type), stream_id); | ||
| 120 | ✗ | return NULL; | |
| 121 | } | ||
| 122 | 16 | return avf->streams[ret]; | |
| 123 | } | ||
| 124 | ✗ | for (i = 0; i < avf->nb_streams; i++) { | |
| 125 | ✗ | ret = avformat_match_stream_specifier(avf, avf->streams[i], spec); | |
| 126 | ✗ | if (ret < 0) { | |
| 127 | ✗ | av_log(log, AV_LOG_ERROR, | |
| 128 | "Invalid stream specifier \"%s\"\n", spec); | ||
| 129 | ✗ | return NULL; | |
| 130 | } | ||
| 131 | ✗ | if (!ret) | |
| 132 | ✗ | continue; | |
| 133 | ✗ | if (avf->streams[i]->discard != AVDISCARD_ALL) { | |
| 134 | ✗ | already++; | |
| 135 | ✗ | continue; | |
| 136 | } | ||
| 137 | ✗ | if (found) { | |
| 138 | ✗ | av_log(log, AV_LOG_WARNING, | |
| 139 | "Ambiguous stream specifier \"%s\", using #%d\n", spec, i); | ||
| 140 | ✗ | break; | |
| 141 | } | ||
| 142 | ✗ | found = avf->streams[i]; | |
| 143 | } | ||
| 144 | ✗ | if (!found) { | |
| 145 | ✗ | av_log(log, AV_LOG_WARNING, "Stream specifier \"%s\" %s\n", spec, | |
| 146 | already ? "matched only already used streams" : | ||
| 147 | "did not match any stream"); | ||
| 148 | ✗ | return NULL; | |
| 149 | } | ||
| 150 | ✗ | if (found->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && | |
| 151 | ✗ | found->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { | |
| 152 | ✗ | av_log(log, AV_LOG_ERROR, "Stream specifier \"%s\" matched a %s stream," | |
| 153 | "currently unsupported by libavfilter\n", spec, | ||
| 154 | ✗ | av_get_media_type_string(found->codecpar->codec_type)); | |
| 155 | ✗ | return NULL; | |
| 156 | } | ||
| 157 | ✗ | return found; | |
| 158 | } | ||
| 159 | |||
| 160 | 3937 | static int get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) | |
| 161 | { | ||
| 162 | int linesize_align[AV_NUM_DATA_POINTERS]; | ||
| 163 | 3937 | MovieStream *st = avctx->opaque; | |
| 164 | 3937 | AVFilterLink *outlink = st->link; | |
| 165 | 3937 | int w, h, ow, oh, copy = 0; | |
| 166 | AVFrame *new; | ||
| 167 | |||
| 168 | 3937 | h = oh = frame->height; | |
| 169 | 3937 | w = ow = frame->width; | |
| 170 | |||
| 171 | 3937 | copy = frame->format != outlink->format; | |
| 172 |
2/3✓ Branch 0 taken 3622 times.
✓ Branch 1 taken 315 times.
✗ Branch 2 not taken.
|
3937 | switch (avctx->codec_type) { |
| 173 | 3622 | case AVMEDIA_TYPE_VIDEO: | |
| 174 |
3/4✓ Branch 0 taken 3622 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 3580 times.
|
3622 | if (w != outlink->w || h != outlink->h) |
| 175 | 42 | copy |= 1; | |
| 176 | 3622 | break; | |
| 177 | 315 | case AVMEDIA_TYPE_AUDIO: | |
| 178 |
2/4✓ Branch 0 taken 315 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 315 times.
|
630 | if (outlink->sample_rate != frame->sample_rate || |
| 179 | 315 | av_channel_layout_compare(&outlink->ch_layout, &frame->ch_layout)) | |
| 180 | ✗ | copy |= 1; | |
| 181 | 315 | break; | |
| 182 | } | ||
| 183 | |||
| 184 |
3/4✓ Branch 0 taken 3895 times.
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3895 times.
|
3937 | if (copy || !(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) |
| 185 | 42 | return avcodec_default_get_buffer2(avctx, frame, flags); | |
| 186 | |||
| 187 |
2/3✓ Branch 0 taken 3580 times.
✓ Branch 1 taken 315 times.
✗ Branch 2 not taken.
|
3895 | switch (avctx->codec_type) { |
| 188 | 3580 | case AVMEDIA_TYPE_VIDEO: | |
| 189 | 3580 | avcodec_align_dimensions2(avctx, &w, &h, linesize_align); | |
| 190 | 3580 | new = ff_default_get_video_buffer(outlink, w, h); | |
| 191 | 3580 | break; | |
| 192 | 315 | case AVMEDIA_TYPE_AUDIO: | |
| 193 | 315 | new = ff_default_get_audio_buffer(outlink, frame->nb_samples); | |
| 194 | 315 | break; | |
| 195 | ✗ | default: | |
| 196 | ✗ | return -1; | |
| 197 | } | ||
| 198 | |||
| 199 | 3895 | av_frame_copy_props(new, frame); | |
| 200 | 3895 | av_frame_unref(frame); | |
| 201 | 3895 | av_frame_move_ref(frame, new); | |
| 202 | 3895 | av_frame_free(&new); | |
| 203 | |||
| 204 | 3895 | frame->width = ow; | |
| 205 | 3895 | frame->height = oh; | |
| 206 | |||
| 207 | 3895 | return 0; | |
| 208 | } | ||
| 209 | |||
| 210 | 16 | static int open_stream(AVFilterContext *ctx, MovieStream *st, int dec_threads) | |
| 211 | { | ||
| 212 | const AVCodec *codec; | ||
| 213 | int ret; | ||
| 214 | |||
| 215 | 16 | codec = avcodec_find_decoder(st->st->codecpar->codec_id); | |
| 216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!codec) { |
| 217 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n"); | |
| 218 | ✗ | return AVERROR(EINVAL); | |
| 219 | } | ||
| 220 | |||
| 221 | 16 | st->codec_ctx = avcodec_alloc_context3(codec); | |
| 222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!st->codec_ctx) |
| 223 | ✗ | return AVERROR(ENOMEM); | |
| 224 | |||
| 225 | 16 | st->codec_ctx->opaque = st; | |
| 226 | 16 | st->codec_ctx->get_buffer2 = get_buffer; | |
| 227 | 16 | ret = avcodec_parameters_to_context(st->codec_ctx, st->st->codecpar); | |
| 228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (ret < 0) |
| 229 | ✗ | return ret; | |
| 230 | 16 | st->codec_ctx->pkt_timebase = st->st->time_base; | |
| 231 | |||
| 232 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!dec_threads) |
| 233 | 16 | dec_threads = ff_filter_get_nb_threads(ctx); | |
| 234 | 16 | st->codec_ctx->thread_count = dec_threads; | |
| 235 | |||
| 236 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if ((ret = avcodec_open2(st->codec_ctx, codec, NULL)) < 0) { |
| 237 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n"); | |
| 238 | ✗ | return ret; | |
| 239 | } | ||
| 240 | |||
| 241 | 16 | return 0; | |
| 242 | } | ||
| 243 | |||
| 244 | ✗ | static int guess_channel_layout(MovieStream *st, int st_index, void *log_ctx) | |
| 245 | { | ||
| 246 | ✗ | AVCodecParameters *dec_par = st->st->codecpar; | |
| 247 | char buf[256]; | ||
| 248 | ✗ | AVChannelLayout chl = { 0 }; | |
| 249 | |||
| 250 | ✗ | av_channel_layout_default(&chl, dec_par->ch_layout.nb_channels); | |
| 251 | |||
| 252 | ✗ | if (!KNOWN(&chl)) { | |
| 253 | ✗ | av_log(log_ctx, AV_LOG_WARNING, | |
| 254 | "Channel layout is not set in stream %d, and could not " | ||
| 255 | "be guessed from the number of channels (%d)\n", | ||
| 256 | st_index, dec_par->ch_layout.nb_channels); | ||
| 257 | ✗ | return av_channel_layout_copy(&dec_par->ch_layout, &chl); | |
| 258 | } | ||
| 259 | |||
| 260 | ✗ | av_channel_layout_describe(&chl, buf, sizeof(buf)); | |
| 261 | ✗ | av_log(log_ctx, AV_LOG_WARNING, | |
| 262 | "Channel layout is not set in output stream %d, " | ||
| 263 | "guessed channel layout is '%s'\n", | ||
| 264 | st_index, buf); | ||
| 265 | ✗ | return av_channel_layout_copy(&dec_par->ch_layout, &chl); | |
| 266 | } | ||
| 267 | |||
| 268 | 16 | static av_cold int movie_common_init(AVFilterContext *ctx) | |
| 269 | { | ||
| 270 | 16 | MovieContext *movie = ctx->priv; | |
| 271 | 16 | const AVInputFormat *iformat = NULL; | |
| 272 | int64_t timestamp; | ||
| 273 | 16 | int nb_streams = 1, ret, i; | |
| 274 | char default_streams[16], *stream_specs, *spec, *cursor; | ||
| 275 | AVStream *st; | ||
| 276 | |||
| 277 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!movie->file_name) { |
| 278 | ✗ | av_log(ctx, AV_LOG_ERROR, "No filename provided!\n"); | |
| 279 | ✗ | return AVERROR(EINVAL); | |
| 280 | } | ||
| 281 | |||
| 282 | 16 | movie->seek_point = movie->seek_point_d * 1000000 + 0.5; | |
| 283 | |||
| 284 | 16 | stream_specs = movie->stream_specs; | |
| 285 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!stream_specs) { |
| 286 | 16 | snprintf(default_streams, sizeof(default_streams), "d%c%d", | |
| 287 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
|
16 | !strcmp(ctx->filter->name, "amovie") ? 'a' : 'v', |
| 288 | movie->stream_index); | ||
| 289 | 16 | stream_specs = default_streams; | |
| 290 | } | ||
| 291 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 16 times.
|
80 | for (cursor = stream_specs; *cursor; cursor++) |
| 292 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
|
64 | if (*cursor == '+') |
| 293 | ✗ | nb_streams++; | |
| 294 | |||
| 295 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
16 | if (movie->loop_count != 1 && nb_streams != 1) { |
| 296 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 297 | "Loop with several streams is currently unsupported\n"); | ||
| 298 | ✗ | return AVERROR_PATCHWELCOME; | |
| 299 | } | ||
| 300 | |||
| 301 | // Try to find the movie format (container) | ||
| 302 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | iformat = movie->format_name ? av_find_input_format(movie->format_name) : NULL; |
| 303 | |||
| 304 | 16 | movie->format_ctx = NULL; | |
| 305 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if ((ret = avformat_open_input(&movie->format_ctx, movie->file_name, iformat, &movie->format_opts)) < 0) { |
| 306 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 307 | "Failed to avformat_open_input '%s'\n", movie->file_name); | ||
| 308 | ✗ | return ret; | |
| 309 | } | ||
| 310 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if ((ret = avformat_find_stream_info(movie->format_ctx, NULL)) < 0) |
| 311 | ✗ | av_log(ctx, AV_LOG_WARNING, "Failed to find stream info\n"); | |
| 312 | |||
| 313 | // if seeking requested, we execute it | ||
| 314 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (movie->seek_point > 0) { |
| 315 | ✗ | timestamp = movie->seek_point; | |
| 316 | // add the stream start time, should it exist | ||
| 317 | ✗ | if (movie->format_ctx->start_time != AV_NOPTS_VALUE) { | |
| 318 | ✗ | if (timestamp > 0 && movie->format_ctx->start_time > INT64_MAX - timestamp) { | |
| 319 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 320 | "%s: seek value overflow with start_time:%"PRId64" seek_point:%"PRId64"\n", | ||
| 321 | ✗ | movie->file_name, movie->format_ctx->start_time, movie->seek_point); | |
| 322 | ✗ | return AVERROR(EINVAL); | |
| 323 | } | ||
| 324 | ✗ | timestamp += movie->format_ctx->start_time; | |
| 325 | } | ||
| 326 | ✗ | if ((ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD)) < 0) { | |
| 327 | ✗ | av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n", | |
| 328 | movie->file_name, timestamp); | ||
| 329 | ✗ | return ret; | |
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 16 times.
|
35 | for (i = 0; i < movie->format_ctx->nb_streams; i++) |
| 334 | 19 | movie->format_ctx->streams[i]->discard = AVDISCARD_ALL; | |
| 335 | |||
| 336 | 16 | movie->pkt = av_packet_alloc(); | |
| 337 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!movie->pkt) |
| 338 | ✗ | return AVERROR(ENOMEM); | |
| 339 | 16 | movie->st = av_calloc(nb_streams, sizeof(*movie->st)); | |
| 340 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!movie->st) |
| 341 | ✗ | return AVERROR(ENOMEM); | |
| 342 | |||
| 343 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (i = 0; i < nb_streams; i++) { |
| 344 | 16 | spec = av_strtok(stream_specs, "+", &cursor); | |
| 345 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!spec) |
| 346 | ✗ | return AVERROR_BUG; | |
| 347 | 16 | stream_specs = NULL; /* for next strtok */ | |
| 348 | 16 | st = find_stream(ctx, movie->format_ctx, spec); | |
| 349 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!st) |
| 350 | ✗ | return AVERROR(EINVAL); | |
| 351 | 16 | st->discard = AVDISCARD_DEFAULT; | |
| 352 | 16 | movie->st[i].st = st; | |
| 353 | 16 | movie->max_stream_index = FFMAX(movie->max_stream_index, st->index); | |
| 354 | 16 | movie->st[i].discontinuity_threshold = | |
| 355 | 16 | av_rescale_q(movie->discontinuity_threshold, AV_TIME_BASE_Q, st->time_base); | |
| 356 | |||
| 357 | 16 | movie->st[i].frame = av_frame_alloc(); | |
| 358 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!movie->st[i].frame) |
| 359 | ✗ | return AVERROR(ENOMEM); | |
| 360 | } | ||
| 361 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (av_strtok(NULL, "+", &cursor)) |
| 362 | ✗ | return AVERROR_BUG; | |
| 363 | |||
| 364 | 16 | movie->out_index = av_calloc(movie->max_stream_index + 1, | |
| 365 | sizeof(*movie->out_index)); | ||
| 366 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!movie->out_index) |
| 367 | ✗ | return AVERROR(ENOMEM); | |
| 368 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (i = 0; i <= movie->max_stream_index; i++) |
| 369 | 16 | movie->out_index[i] = -1; | |
| 370 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (i = 0; i < nb_streams; i++) { |
| 371 | 16 | AVFilterPad pad = { 0 }; | |
| 372 | 16 | movie->out_index[movie->st[i].st->index] = i; | |
| 373 | 16 | pad.type = movie->st[i].st->codecpar->codec_type; | |
| 374 | 16 | pad.name = av_asprintf("out%d", i); | |
| 375 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!pad.name) |
| 376 | ✗ | return AVERROR(ENOMEM); | |
| 377 | 16 | pad.config_props = movie_config_output_props; | |
| 378 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) |
| 379 | ✗ | return ret; | |
| 380 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
|
16 | if ( movie->st[i].st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && |
| 381 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | !KNOWN(&movie->st[i].st->codecpar->ch_layout)) { |
| 382 | ✗ | ret = guess_channel_layout(&movie->st[i], i, ctx); | |
| 383 | ✗ | if (ret < 0) | |
| 384 | ✗ | return ret; | |
| 385 | } | ||
| 386 | 16 | ret = open_stream(ctx, &movie->st[i], movie->dec_threads); | |
| 387 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (ret < 0) |
| 388 | ✗ | return ret; | |
| 389 | } | ||
| 390 | |||
| 391 | 16 | av_log(ctx, AV_LOG_VERBOSE, "seek_point:%"PRIi64" format_name:%s file_name:%s stream_index:%d\n", | |
| 392 | movie->seek_point, movie->format_name, movie->file_name, | ||
| 393 | movie->stream_index); | ||
| 394 | |||
| 395 | 16 | return 0; | |
| 396 | } | ||
| 397 | |||
| 398 | 16 | static av_cold void movie_uninit(AVFilterContext *ctx) | |
| 399 | { | ||
| 400 | 16 | MovieContext *movie = ctx->priv; | |
| 401 | int i; | ||
| 402 | |||
| 403 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (i = 0; i < ctx->nb_outputs; i++) { |
| 404 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (movie->st[i].st) |
| 405 | 16 | avcodec_free_context(&movie->st[i].codec_ctx); | |
| 406 | 16 | av_frame_free(&movie->st[i].frame); | |
| 407 | } | ||
| 408 | 16 | av_packet_free(&movie->pkt); | |
| 409 | 16 | av_freep(&movie->st); | |
| 410 | 16 | av_freep(&movie->out_index); | |
| 411 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (movie->format_ctx) |
| 412 | 16 | avformat_close_input(&movie->format_ctx); | |
| 413 | 16 | } | |
| 414 | |||
| 415 | 16 | static int movie_query_formats(const AVFilterContext *ctx, | |
| 416 | AVFilterFormatsConfig **cfg_in, | ||
| 417 | AVFilterFormatsConfig **cfg_out) | ||
| 418 | { | ||
| 419 | 16 | const MovieContext *movie = ctx->priv; | |
| 420 | 16 | int list[] = { 0, -1 }; | |
| 421 | 16 | AVChannelLayout list64[] = { { 0 }, { 0 } }; | |
| 422 | int i, ret; | ||
| 423 | |||
| 424 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (i = 0; i < ctx->nb_outputs; i++) { |
| 425 | 16 | const MovieStream *st = &movie->st[i]; | |
| 426 | 16 | const AVCodecParameters *c = st->st->codecpar; | |
| 427 | 16 | AVFilterFormatsConfig *cfg = cfg_out[i]; | |
| 428 | |||
| 429 |
2/3✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
16 | switch (c->codec_type) { |
| 430 | 13 | case AVMEDIA_TYPE_VIDEO: | |
| 431 | 13 | list[0] = c->format; | |
| 432 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | if ((ret = ff_formats_ref(ff_make_format_list(list), &cfg->formats)) < 0) |
| 433 | ✗ | return ret; | |
| 434 | 13 | list[0] = c->color_space; | |
| 435 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | if ((ret = ff_formats_ref(ff_make_format_list(list), &cfg->color_spaces)) < 0) |
| 436 | ✗ | return ret; | |
| 437 | 13 | list[0] = c->color_range; | |
| 438 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | if ((ret = ff_formats_ref(ff_make_format_list(list), &cfg->color_ranges)) < 0) |
| 439 | ✗ | return ret; | |
| 440 | 13 | break; | |
| 441 | 3 | case AVMEDIA_TYPE_AUDIO: | |
| 442 | 3 | list[0] = c->format; | |
| 443 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if ((ret = ff_formats_ref(ff_make_format_list(list), &cfg->formats)) < 0) |
| 444 | ✗ | return ret; | |
| 445 | 3 | list[0] = c->sample_rate; | |
| 446 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if ((ret = ff_formats_ref(ff_make_format_list(list), &cfg->samplerates)) < 0) |
| 447 | ✗ | return ret; | |
| 448 | 3 | list64[0] = c->ch_layout; | |
| 449 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if ((ret = ff_channel_layouts_ref(ff_make_channel_layout_list(list64), |
| 450 | &cfg->channel_layouts)) < 0) | ||
| 451 | ✗ | return ret; | |
| 452 | 3 | break; | |
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | 16 | return 0; | |
| 457 | } | ||
| 458 | |||
| 459 | 16 | static int movie_config_output_props(AVFilterLink *outlink) | |
| 460 | { | ||
| 461 | 16 | FilterLink *l = ff_filter_link(outlink); | |
| 462 | 16 | AVFilterContext *ctx = outlink->src; | |
| 463 | 16 | MovieContext *movie = ctx->priv; | |
| 464 | 16 | unsigned out_id = FF_OUTLINK_IDX(outlink); | |
| 465 | 16 | MovieStream *st = &movie->st[out_id]; | |
| 466 | 16 | AVCodecParameters *c = st->st->codecpar; | |
| 467 | |||
| 468 | 16 | outlink->time_base = st->st->time_base; | |
| 469 | |||
| 470 |
2/3✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
16 | switch (c->codec_type) { |
| 471 | 13 | case AVMEDIA_TYPE_VIDEO: | |
| 472 | 13 | outlink->w = c->width; | |
| 473 | 13 | outlink->h = c->height; | |
| 474 | 13 | l->frame_rate = st->st->r_frame_rate; | |
| 475 | 13 | break; | |
| 476 | 3 | case AVMEDIA_TYPE_AUDIO: | |
| 477 | 3 | break; | |
| 478 | } | ||
| 479 | |||
| 480 | 16 | st->link = outlink; | |
| 481 | |||
| 482 | 16 | return 0; | |
| 483 | } | ||
| 484 | |||
| 485 | ✗ | static int rewind_file(AVFilterContext *ctx) | |
| 486 | { | ||
| 487 | ✗ | MovieContext *movie = ctx->priv; | |
| 488 | ✗ | int64_t timestamp = movie->seek_point; | |
| 489 | int ret, i; | ||
| 490 | |||
| 491 | ✗ | if (movie->format_ctx->start_time != AV_NOPTS_VALUE) | |
| 492 | ✗ | timestamp += movie->format_ctx->start_time; | |
| 493 | ✗ | ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD); | |
| 494 | ✗ | if (ret < 0) { | |
| 495 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unable to loop: %s\n", av_err2str(ret)); | |
| 496 | ✗ | movie->loop_count = 1; /* do not try again */ | |
| 497 | ✗ | return ret; | |
| 498 | } | ||
| 499 | |||
| 500 | ✗ | for (i = 0; i < ctx->nb_outputs; i++) { | |
| 501 | ✗ | avcodec_flush_buffers(movie->st[i].codec_ctx); | |
| 502 | } | ||
| 503 | ✗ | return 0; | |
| 504 | } | ||
| 505 | |||
| 506 | 16 | static int flush_decoder(AVFilterContext *ctx, int i) | |
| 507 | { | ||
| 508 | 16 | MovieContext *movie = ctx->priv; | |
| 509 | 16 | AVCodecContext *dec = movie->st[i].codec_ctx; | |
| 510 | |||
| 511 | 16 | return avcodec_send_packet(dec, NULL); | |
| 512 | } | ||
| 513 | |||
| 514 | 4035 | static int decode_packet(AVFilterContext *ctx, int i) | |
| 515 | { | ||
| 516 | 4035 | AVFilterLink *outlink = ctx->outputs[i]; | |
| 517 | 4035 | MovieContext *movie = ctx->priv; | |
| 518 | 4035 | MovieStream *st = &movie->st[i]; | |
| 519 | 4035 | AVCodecContext *dec = movie->st[i].codec_ctx; | |
| 520 | 4035 | AVFrame *frame = movie->st[i].frame; | |
| 521 | 4035 | AVPacket *pkt = movie->pkt; | |
| 522 | 4035 | int ret = 0; | |
| 523 | |||
| 524 | // do not output more than 1 frame per iteration, | ||
| 525 | // so try to receive_frame first | ||
| 526 | 4035 | ret = avcodec_receive_frame(dec, frame); | |
| 527 |
3/4✓ Branch 0 taken 3949 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 3949 times.
✗ Branch 3 not taken.
|
4035 | if (!movie->eof && ret == AVERROR(EAGAIN)) { |
| 528 | 3949 | ret = avcodec_send_packet(dec, pkt); | |
| 529 | 3949 | av_packet_unref(pkt); | |
| 530 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3949 times.
|
3949 | if (ret < 0) |
| 531 | ✗ | return ret; | |
| 532 | 3949 | ret = avcodec_receive_frame(dec, frame); | |
| 533 | } | ||
| 534 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 3937 times.
|
4035 | if (ret < 0) { |
| 535 | // those two return values are special and mean there is no output | ||
| 536 | // frame available, but there were no errors during decoding | ||
| 537 |
3/4✓ Branch 0 taken 82 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
|
98 | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) |
| 538 | 98 | return 0; | |
| 539 | ✗ | return ret; | |
| 540 | } | ||
| 541 | |||
| 542 | // output a single frame | ||
| 543 |
1/2✓ Branch 0 taken 3937 times.
✗ Branch 1 not taken.
|
3937 | if (ret >= 0) { |
| 544 | 3937 | frame->pts = frame->best_effort_timestamp; | |
| 545 |
2/2✓ Branch 0 taken 3933 times.
✓ Branch 1 taken 4 times.
|
3937 | if (frame->pts != AV_NOPTS_VALUE) { |
| 546 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3933 times.
|
3933 | if (movie->ts_offset) |
| 547 | ✗ | frame->pts += av_rescale_q_rnd(movie->ts_offset, AV_TIME_BASE_Q, outlink->time_base, AV_ROUND_UP); | |
| 548 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3933 times.
|
3933 | if (st->discontinuity_threshold) { |
| 549 | ✗ | if (st->last_pts != AV_NOPTS_VALUE) { | |
| 550 | ✗ | int64_t diff = frame->pts - st->last_pts; | |
| 551 | ✗ | if (diff < 0 || diff > st->discontinuity_threshold) { | |
| 552 | ✗ | av_log(ctx, AV_LOG_VERBOSE, "Discontinuity in stream:%d diff:%"PRId64"\n", i, diff); | |
| 553 | ✗ | movie->ts_offset += av_rescale_q_rnd(-diff, outlink->time_base, AV_TIME_BASE_Q, AV_ROUND_UP); | |
| 554 | ✗ | frame->pts -= diff; | |
| 555 | } | ||
| 556 | } | ||
| 557 | } | ||
| 558 | 3933 | st->last_pts = frame->pts; | |
| 559 | } | ||
| 560 | 3937 | ret = ff_filter_frame(outlink, av_frame_clone(frame)); | |
| 561 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3937 times.
|
3937 | if (ret < 0) |
| 562 | ✗ | return ret; | |
| 563 |
1/2✓ Branch 0 taken 3937 times.
✗ Branch 1 not taken.
|
3937 | if (ret == 0) |
| 564 | 3937 | return 1; | |
| 565 | } | ||
| 566 | |||
| 567 | ✗ | return 0; | |
| 568 | } | ||
| 569 | |||
| 570 | 4617 | static int activate(AVFilterContext *ctx) | |
| 571 | { | ||
| 572 | 4617 | MovieContext *movie = ctx->priv; | |
| 573 | 4617 | int wanted = 0, ret = 0; | |
| 574 | |||
| 575 |
2/2✓ Branch 0 taken 4617 times.
✓ Branch 1 taken 4617 times.
|
9234 | for (int i = 0; i < ctx->nb_outputs; i++) { |
| 576 |
2/2✓ Branch 1 taken 4272 times.
✓ Branch 2 taken 345 times.
|
4617 | if (ff_outlink_frame_wanted(ctx->outputs[i])) |
| 577 | 4272 | wanted++; | |
| 578 | } | ||
| 579 | |||
| 580 |
2/2✓ Branch 0 taken 345 times.
✓ Branch 1 taken 4272 times.
|
4617 | if (wanted == 0) |
| 581 | 345 | return FFERROR_NOT_READY; | |
| 582 | |||
| 583 |
2/2✓ Branch 0 taken 4186 times.
✓ Branch 1 taken 86 times.
|
4272 | if (!movie->eof) { |
| 584 |
1/2✓ Branch 0 taken 4186 times.
✗ Branch 1 not taken.
|
4186 | if (!movie->pkt->buf) |
| 585 | 4186 | ret = av_read_frame(movie->format_ctx, movie->pkt); | |
| 586 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4170 times.
|
4186 | if (ret < 0) { |
| 587 | 16 | movie->eof = 1; | |
| 588 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (int i = 0; i < ctx->nb_outputs; i++) |
| 589 | 16 | flush_decoder(ctx, i); | |
| 590 | 16 | ff_filter_set_ready(ctx, 100); | |
| 591 | 16 | return 0; | |
| 592 | } else { | ||
| 593 |
2/2✓ Branch 0 taken 3949 times.
✓ Branch 1 taken 221 times.
|
4170 | int pkt_out_id = movie->pkt->stream_index > movie->max_stream_index ? -1 : |
| 594 | 3949 | movie->out_index[movie->pkt->stream_index]; | |
| 595 | |||
| 596 |
2/2✓ Branch 0 taken 3949 times.
✓ Branch 1 taken 221 times.
|
4170 | if (pkt_out_id >= 0) { |
| 597 | 3949 | ret = decode_packet(ctx, pkt_out_id); | |
| 598 | } else { | ||
| 599 | 221 | av_packet_unref(movie->pkt); | |
| 600 | } | ||
| 601 | 4170 | ff_filter_set_ready(ctx, 100); | |
| 602 | 4170 | return (ret <= 0) ? ret : 0; | |
| 603 | } | ||
| 604 | } else { | ||
| 605 | 86 | int nb_eofs = 0; | |
| 606 | |||
| 607 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
|
172 | for (int i = 0; i < ctx->nb_outputs; i++) { |
| 608 |
1/2✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
|
86 | if (!movie->st[i].eof) { |
| 609 | 86 | ret = decode_packet(ctx, i); | |
| 610 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 70 times.
|
86 | if (ret <= 0) |
| 611 | 16 | movie->st[i].eof = 1; | |
| 612 | } | ||
| 613 | 86 | nb_eofs += movie->st[i].eof == 1; | |
| 614 | } | ||
| 615 |
3/4✓ Branch 0 taken 16 times.
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
86 | if (nb_eofs == ctx->nb_outputs && movie->loop_count != 1) { |
| 616 | ✗ | ret = rewind_file(ctx); | |
| 617 | ✗ | if (ret < 0) | |
| 618 | ✗ | return ret; | |
| 619 | ✗ | movie->loop_count -= movie->loop_count > 1; | |
| 620 | ✗ | av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); | |
| 621 | ✗ | ff_filter_set_ready(ctx, 100); | |
| 622 | ✗ | for (int i = 0; i < ctx->nb_outputs; i++) | |
| 623 | ✗ | movie->st[i].eof = 0; | |
| 624 | ✗ | movie->eof = 0; | |
| 625 | ✗ | return 0; | |
| 626 | } else { | ||
| 627 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
|
172 | for (int i = 0; i < ctx->nb_outputs; i++) { |
| 628 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 70 times.
|
86 | if (movie->st[i].eof) { |
| 629 | 16 | ff_outlink_set_status(ctx->outputs[i], AVERROR_EOF, movie->st[i].last_pts); | |
| 630 | 16 | nb_eofs++; | |
| 631 | } | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 |
2/2✓ Branch 0 taken 70 times.
✓ Branch 1 taken 16 times.
|
86 | if (nb_eofs < ctx->nb_outputs) |
| 636 | 70 | ff_filter_set_ready(ctx, 100); | |
| 637 | 86 | return 0; | |
| 638 | } | ||
| 639 | |||
| 640 | return FFERROR_NOT_READY; | ||
| 641 | } | ||
| 642 | |||
| 643 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
| 644 | char *res, int res_len, int flags) | ||
| 645 | { | ||
| 646 | ✗ | MovieContext *movie = ctx->priv; | |
| 647 | ✗ | int ret = AVERROR(ENOSYS); | |
| 648 | |||
| 649 | ✗ | if (!strcmp(cmd, "seek")) { | |
| 650 | int idx, flags, i; | ||
| 651 | int64_t ts; | ||
| 652 | char tail[2]; | ||
| 653 | |||
| 654 | ✗ | if (sscanf(args, "%i|%"SCNi64"|%i %1s", &idx, &ts, &flags, tail) != 3) | |
| 655 | ✗ | return AVERROR(EINVAL); | |
| 656 | |||
| 657 | ✗ | ret = av_seek_frame(movie->format_ctx, idx, ts, flags); | |
| 658 | ✗ | if (ret < 0) | |
| 659 | ✗ | return ret; | |
| 660 | |||
| 661 | ✗ | for (i = 0; i < ctx->nb_outputs; i++) { | |
| 662 | ✗ | avcodec_flush_buffers(movie->st[i].codec_ctx); | |
| 663 | } | ||
| 664 | ✗ | return ret; | |
| 665 | ✗ | } else if (!strcmp(cmd, "get_duration")) { | |
| 666 | int print_len; | ||
| 667 | char tail[2]; | ||
| 668 | |||
| 669 | ✗ | if (!res || res_len <= 0) | |
| 670 | ✗ | return AVERROR(EINVAL); | |
| 671 | |||
| 672 | ✗ | if (args && sscanf(args, "%1s", tail) == 1) | |
| 673 | ✗ | return AVERROR(EINVAL); | |
| 674 | |||
| 675 | ✗ | print_len = snprintf(res, res_len, "%"PRId64, movie->format_ctx->duration); | |
| 676 | ✗ | if (print_len < 0 || print_len >= res_len) | |
| 677 | ✗ | return AVERROR(EINVAL); | |
| 678 | |||
| 679 | ✗ | return 0; | |
| 680 | } | ||
| 681 | |||
| 682 | ✗ | return ret; | |
| 683 | } | ||
| 684 | |||
| 685 | AVFILTER_DEFINE_CLASS_EXT(movie, "(a)movie", movie_options); | ||
| 686 | |||
| 687 | #if CONFIG_MOVIE_FILTER | ||
| 688 | |||
| 689 | const FFFilter ff_avsrc_movie = { | ||
| 690 | .p.name = "movie", | ||
| 691 | .p.description = NULL_IF_CONFIG_SMALL("Read from a movie source."), | ||
| 692 | .p.priv_class = &movie_class, | ||
| 693 | .p.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, | ||
| 694 | .priv_size = sizeof(MovieContext), | ||
| 695 | .init = movie_common_init, | ||
| 696 | .activate = activate, | ||
| 697 | .uninit = movie_uninit, | ||
| 698 | FILTER_QUERY_FUNC2(movie_query_formats), | ||
| 699 | |||
| 700 | .process_command = process_command | ||
| 701 | }; | ||
| 702 | |||
| 703 | #endif /* CONFIG_MOVIE_FILTER */ | ||
| 704 | |||
| 705 | #if CONFIG_AMOVIE_FILTER | ||
| 706 | |||
| 707 | const FFFilter ff_avsrc_amovie = { | ||
| 708 | .p.name = "amovie", | ||
| 709 | .p.description = NULL_IF_CONFIG_SMALL("Read audio from a movie source."), | ||
| 710 | .p.priv_class = &movie_class, | ||
| 711 | .p.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, | ||
| 712 | .priv_size = sizeof(MovieContext), | ||
| 713 | .init = movie_common_init, | ||
| 714 | .activate = activate, | ||
| 715 | .uninit = movie_uninit, | ||
| 716 | FILTER_QUERY_FUNC2(movie_query_formats), | ||
| 717 | |||
| 718 | .process_command = process_command, | ||
| 719 | }; | ||
| 720 | |||
| 721 | #endif /* CONFIG_AMOVIE_FILTER */ | ||
| 722 |