yading@10: /* yading@10: * Copyright (c) 2010 Stefano Sabatini yading@10: * Copyright (c) 2008 Victor Paesa yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * movie video source yading@10: * yading@10: * @todo use direct rendering (no allocation of a new frame) yading@10: * @todo support a PTS correction mechanism yading@10: */ yading@10: yading@10: /* #define DEBUG */ yading@10: yading@10: #include yading@10: #include "libavutil/avstring.h" yading@10: #include "libavutil/avassert.h" yading@10: #include "libavutil/opt.h" yading@10: #include "libavutil/imgutils.h" yading@10: #include "libavutil/timestamp.h" yading@10: #include "libavformat/avformat.h" yading@10: #include "audio.h" yading@10: #include "avcodec.h" yading@10: #include "avfilter.h" yading@10: #include "formats.h" yading@10: #include "internal.h" yading@10: #include "video.h" yading@10: yading@10: typedef struct { yading@10: AVStream *st; yading@10: int done; yading@10: } MovieStream; yading@10: yading@10: typedef struct { yading@10: /* common A/V fields */ yading@10: const AVClass *class; yading@10: int64_t seek_point; ///< seekpoint in microseconds yading@10: double seek_point_d; yading@10: char *format_name; yading@10: char *file_name; yading@10: char *stream_specs; /**< user-provided list of streams, separated by + */ yading@10: int stream_index; /**< for compatibility */ yading@10: int loop_count; yading@10: yading@10: AVFormatContext *format_ctx; yading@10: int eof; yading@10: AVPacket pkt, pkt0; yading@10: AVFrame *frame; ///< video frame to store the decoded images in yading@10: yading@10: int max_stream_index; /**< max stream # actually used for output */ yading@10: MovieStream *st; /**< array of all streams, one per output */ yading@10: int *out_index; /**< stream number -> output number map, or -1 */ yading@10: } MovieContext; yading@10: yading@10: #define OFFSET(x) offsetof(MovieContext, x) yading@10: #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM yading@10: yading@10: static const AVOption movie_options[]= { yading@10: { "filename", NULL, OFFSET(file_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, yading@10: { "format_name", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, yading@10: { "f", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, .flags = FLAGS }, yading@10: { "stream_index", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, yading@10: { "si", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, yading@10: { "seek_point", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, yading@10: { "sp", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, yading@10: { "streams", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MAX, CHAR_MAX, FLAGS }, yading@10: { "s", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MAX, CHAR_MAX, FLAGS }, yading@10: { "loop", "set loop count", OFFSET(loop_count), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, FLAGS }, yading@10: { NULL }, yading@10: }; yading@10: yading@10: static int movie_config_output_props(AVFilterLink *outlink); yading@10: static int movie_request_frame(AVFilterLink *outlink); yading@10: yading@10: static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) yading@10: { yading@10: int i, ret, already = 0, stream_id = -1; yading@10: char type_char, dummy; yading@10: AVStream *found = NULL; yading@10: enum AVMediaType type; yading@10: yading@10: ret = sscanf(spec, "d%[av]%d%c", &type_char, &stream_id, &dummy); yading@10: if (ret >= 1 && ret <= 2) { yading@10: type = type_char == 'v' ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO; yading@10: ret = av_find_best_stream(avf, type, stream_id, -1, NULL, 0); yading@10: if (ret < 0) { yading@10: av_log(log, AV_LOG_ERROR, "No %s stream with index '%d' found\n", yading@10: av_get_media_type_string(type), stream_id); yading@10: return NULL; yading@10: } yading@10: return avf->streams[ret]; yading@10: } yading@10: for (i = 0; i < avf->nb_streams; i++) { yading@10: ret = avformat_match_stream_specifier(avf, avf->streams[i], spec); yading@10: if (ret < 0) { yading@10: av_log(log, AV_LOG_ERROR, yading@10: "Invalid stream specifier \"%s\"\n", spec); yading@10: return NULL; yading@10: } yading@10: if (!ret) yading@10: continue; yading@10: if (avf->streams[i]->discard != AVDISCARD_ALL) { yading@10: already++; yading@10: continue; yading@10: } yading@10: if (found) { yading@10: av_log(log, AV_LOG_WARNING, yading@10: "Ambiguous stream specifier \"%s\", using #%d\n", spec, i); yading@10: break; yading@10: } yading@10: found = avf->streams[i]; yading@10: } yading@10: if (!found) { yading@10: av_log(log, AV_LOG_WARNING, "Stream specifier \"%s\" %s\n", spec, yading@10: already ? "matched only already used streams" : yading@10: "did not match any stream"); yading@10: return NULL; yading@10: } yading@10: if (found->codec->codec_type != AVMEDIA_TYPE_VIDEO && yading@10: found->codec->codec_type != AVMEDIA_TYPE_AUDIO) { yading@10: av_log(log, AV_LOG_ERROR, "Stream specifier \"%s\" matched a %s stream," yading@10: "currently unsupported by libavfilter\n", spec, yading@10: av_get_media_type_string(found->codec->codec_type)); yading@10: return NULL; yading@10: } yading@10: return found; yading@10: } yading@10: yading@10: static int open_stream(void *log, MovieStream *st) yading@10: { yading@10: AVCodec *codec; yading@10: int ret; yading@10: yading@10: codec = avcodec_find_decoder(st->st->codec->codec_id); yading@10: if (!codec) { yading@10: av_log(log, AV_LOG_ERROR, "Failed to find any codec\n"); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: st->st->codec->refcounted_frames = 1; yading@10: yading@10: if ((ret = avcodec_open2(st->st->codec, codec, NULL)) < 0) { yading@10: av_log(log, AV_LOG_ERROR, "Failed to open codec\n"); yading@10: return ret; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int guess_channel_layout(MovieStream *st, int st_index, void *log_ctx) yading@10: { yading@10: AVCodecContext *dec_ctx = st->st->codec; yading@10: char buf[256]; yading@10: int64_t chl = av_get_default_channel_layout(dec_ctx->channels); yading@10: yading@10: if (!chl) { yading@10: av_log(log_ctx, AV_LOG_ERROR, yading@10: "Channel layout is not set in stream %d, and could not " yading@10: "be guessed from the number of channels (%d)\n", yading@10: st_index, dec_ctx->channels); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: av_get_channel_layout_string(buf, sizeof(buf), dec_ctx->channels, chl); yading@10: av_log(log_ctx, AV_LOG_WARNING, yading@10: "Channel layout is not set in output stream %d, " yading@10: "guessed channel layout is '%s'\n", yading@10: st_index, buf); yading@10: dec_ctx->channel_layout = chl; yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int movie_common_init(AVFilterContext *ctx) yading@10: { yading@10: MovieContext *movie = ctx->priv; yading@10: AVInputFormat *iformat = NULL; yading@10: int64_t timestamp; yading@10: int nb_streams, ret, i; yading@10: char default_streams[16], *stream_specs, *spec, *cursor; yading@10: char name[16]; yading@10: AVStream *st; yading@10: yading@10: if (!*movie->file_name) { yading@10: av_log(ctx, AV_LOG_ERROR, "No filename provided!\n"); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: movie->seek_point = movie->seek_point_d * 1000000 + 0.5; yading@10: yading@10: stream_specs = movie->stream_specs; yading@10: if (!stream_specs) { yading@10: snprintf(default_streams, sizeof(default_streams), "d%c%d", yading@10: !strcmp(ctx->filter->name, "amovie") ? 'a' : 'v', yading@10: movie->stream_index); yading@10: stream_specs = default_streams; yading@10: } yading@10: for (cursor = stream_specs, nb_streams = 1; *cursor; cursor++) yading@10: if (*cursor == '+') yading@10: nb_streams++; yading@10: yading@10: if (movie->loop_count != 1 && nb_streams != 1) { yading@10: av_log(ctx, AV_LOG_ERROR, yading@10: "Loop with several streams is currently unsupported\n"); yading@10: return AVERROR_PATCHWELCOME; yading@10: } yading@10: yading@10: av_register_all(); yading@10: yading@10: // Try to find the movie format (container) yading@10: iformat = movie->format_name ? av_find_input_format(movie->format_name) : NULL; yading@10: yading@10: movie->format_ctx = NULL; yading@10: if ((ret = avformat_open_input(&movie->format_ctx, movie->file_name, iformat, NULL)) < 0) { yading@10: av_log(ctx, AV_LOG_ERROR, yading@10: "Failed to avformat_open_input '%s'\n", movie->file_name); yading@10: return ret; yading@10: } yading@10: if ((ret = avformat_find_stream_info(movie->format_ctx, NULL)) < 0) yading@10: av_log(ctx, AV_LOG_WARNING, "Failed to find stream info\n"); yading@10: yading@10: // if seeking requested, we execute it yading@10: if (movie->seek_point > 0) { yading@10: timestamp = movie->seek_point; yading@10: // add the stream start time, should it exist yading@10: if (movie->format_ctx->start_time != AV_NOPTS_VALUE) { yading@10: if (timestamp > INT64_MAX - movie->format_ctx->start_time) { yading@10: av_log(ctx, AV_LOG_ERROR, yading@10: "%s: seek value overflow with start_time:%"PRId64" seek_point:%"PRId64"\n", yading@10: movie->file_name, movie->format_ctx->start_time, movie->seek_point); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: timestamp += movie->format_ctx->start_time; yading@10: } yading@10: if ((ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD)) < 0) { yading@10: av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n", yading@10: movie->file_name, timestamp); yading@10: return ret; yading@10: } yading@10: } yading@10: yading@10: for (i = 0; i < movie->format_ctx->nb_streams; i++) yading@10: movie->format_ctx->streams[i]->discard = AVDISCARD_ALL; yading@10: yading@10: movie->st = av_calloc(nb_streams, sizeof(*movie->st)); yading@10: if (!movie->st) yading@10: return AVERROR(ENOMEM); yading@10: yading@10: for (i = 0; i < nb_streams; i++) { yading@10: spec = av_strtok(stream_specs, "+", &cursor); yading@10: if (!spec) yading@10: return AVERROR_BUG; yading@10: stream_specs = NULL; /* for next strtok */ yading@10: st = find_stream(ctx, movie->format_ctx, spec); yading@10: if (!st) yading@10: return AVERROR(EINVAL); yading@10: st->discard = AVDISCARD_DEFAULT; yading@10: movie->st[i].st = st; yading@10: movie->max_stream_index = FFMAX(movie->max_stream_index, st->index); yading@10: } yading@10: if (av_strtok(NULL, "+", &cursor)) yading@10: return AVERROR_BUG; yading@10: yading@10: movie->out_index = av_calloc(movie->max_stream_index + 1, yading@10: sizeof(*movie->out_index)); yading@10: if (!movie->out_index) yading@10: return AVERROR(ENOMEM); yading@10: for (i = 0; i <= movie->max_stream_index; i++) yading@10: movie->out_index[i] = -1; yading@10: for (i = 0; i < nb_streams; i++) yading@10: movie->out_index[movie->st[i].st->index] = i; yading@10: yading@10: for (i = 0; i < nb_streams; i++) { yading@10: AVFilterPad pad = { 0 }; yading@10: snprintf(name, sizeof(name), "out%d", i); yading@10: pad.type = movie->st[i].st->codec->codec_type; yading@10: pad.name = av_strdup(name); yading@10: pad.config_props = movie_config_output_props; yading@10: pad.request_frame = movie_request_frame; yading@10: ff_insert_outpad(ctx, i, &pad); yading@10: ret = open_stream(ctx, &movie->st[i]); yading@10: if (ret < 0) yading@10: return ret; yading@10: if ( movie->st[i].st->codec->codec->type == AVMEDIA_TYPE_AUDIO && yading@10: !movie->st[i].st->codec->channel_layout) { yading@10: ret = guess_channel_layout(&movie->st[i], i, ctx); yading@10: if (ret < 0) yading@10: return ret; yading@10: } yading@10: } yading@10: yading@10: av_log(ctx, AV_LOG_VERBOSE, "seek_point:%"PRIi64" format_name:%s file_name:%s stream_index:%d\n", yading@10: movie->seek_point, movie->format_name, movie->file_name, yading@10: movie->stream_index); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold void movie_uninit(AVFilterContext *ctx) yading@10: { yading@10: MovieContext *movie = ctx->priv; yading@10: int i; yading@10: yading@10: for (i = 0; i < ctx->nb_outputs; i++) { yading@10: av_freep(&ctx->output_pads[i].name); yading@10: if (movie->st[i].st) yading@10: avcodec_close(movie->st[i].st->codec); yading@10: } yading@10: av_freep(&movie->file_name); yading@10: av_freep(&movie->st); yading@10: av_freep(&movie->out_index); yading@10: av_frame_free(&movie->frame); yading@10: if (movie->format_ctx) yading@10: avformat_close_input(&movie->format_ctx); yading@10: } yading@10: yading@10: static int movie_query_formats(AVFilterContext *ctx) yading@10: { yading@10: MovieContext *movie = ctx->priv; yading@10: int list[] = { 0, -1 }; yading@10: int64_t list64[] = { 0, -1 }; yading@10: int i; yading@10: yading@10: for (i = 0; i < ctx->nb_outputs; i++) { yading@10: MovieStream *st = &movie->st[i]; yading@10: AVCodecContext *c = st->st->codec; yading@10: AVFilterLink *outlink = ctx->outputs[i]; yading@10: yading@10: switch (c->codec_type) { yading@10: case AVMEDIA_TYPE_VIDEO: yading@10: list[0] = c->pix_fmt; yading@10: ff_formats_ref(ff_make_format_list(list), &outlink->in_formats); yading@10: break; yading@10: case AVMEDIA_TYPE_AUDIO: yading@10: list[0] = c->sample_fmt; yading@10: ff_formats_ref(ff_make_format_list(list), &outlink->in_formats); yading@10: list[0] = c->sample_rate; yading@10: ff_formats_ref(ff_make_format_list(list), &outlink->in_samplerates); yading@10: list64[0] = c->channel_layout; yading@10: ff_channel_layouts_ref(avfilter_make_format64_list(list64), yading@10: &outlink->in_channel_layouts); yading@10: break; yading@10: } yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int movie_config_output_props(AVFilterLink *outlink) yading@10: { yading@10: AVFilterContext *ctx = outlink->src; yading@10: MovieContext *movie = ctx->priv; yading@10: unsigned out_id = FF_OUTLINK_IDX(outlink); yading@10: MovieStream *st = &movie->st[out_id]; yading@10: AVCodecContext *c = st->st->codec; yading@10: yading@10: outlink->time_base = st->st->time_base; yading@10: yading@10: switch (c->codec_type) { yading@10: case AVMEDIA_TYPE_VIDEO: yading@10: outlink->w = c->width; yading@10: outlink->h = c->height; yading@10: outlink->frame_rate = st->st->r_frame_rate; yading@10: break; yading@10: case AVMEDIA_TYPE_AUDIO: yading@10: break; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static char *describe_frame_to_str(char *dst, size_t dst_size, yading@10: AVFrame *frame, yading@10: AVFilterLink *link) yading@10: { yading@10: switch (frame->type) { yading@10: case AVMEDIA_TYPE_VIDEO: yading@10: snprintf(dst, dst_size, yading@10: "video pts:%s time:%s size:%dx%d aspect:%d/%d", yading@10: av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), yading@10: frame->width, frame->height, yading@10: frame->sample_aspect_ratio.num, yading@10: frame->sample_aspect_ratio.den); yading@10: break; yading@10: case AVMEDIA_TYPE_AUDIO: yading@10: snprintf(dst, dst_size, yading@10: "audio pts:%s time:%s samples:%d", yading@10: av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), yading@10: frame->nb_samples); yading@10: break; yading@10: default: yading@10: snprintf(dst, dst_size, "%s BUG", av_get_media_type_string(frame->type)); yading@10: break; yading@10: } yading@10: return dst; yading@10: } yading@10: yading@10: #define describe_frameref(f, link) \ yading@10: describe_frame_to_str((char[1024]){0}, 1024, f, link) yading@10: yading@10: static int rewind_file(AVFilterContext *ctx) yading@10: { yading@10: MovieContext *movie = ctx->priv; yading@10: int64_t timestamp = movie->seek_point; yading@10: int ret, i; yading@10: yading@10: if (movie->format_ctx->start_time != AV_NOPTS_VALUE) yading@10: timestamp += movie->format_ctx->start_time; yading@10: ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD); yading@10: if (ret < 0) { yading@10: av_log(ctx, AV_LOG_ERROR, "Unable to loop: %s\n", av_err2str(ret)); yading@10: movie->loop_count = 1; /* do not try again */ yading@10: return ret; yading@10: } yading@10: yading@10: for (i = 0; i < ctx->nb_outputs; i++) { yading@10: avcodec_flush_buffers(movie->st[i].st->codec); yading@10: movie->st[i].done = 0; yading@10: } yading@10: movie->eof = 0; yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * Try to push a frame to the requested output. yading@10: * yading@10: * @param ctx filter context yading@10: * @param out_id number of output where a frame is wanted; yading@10: * if the frame is read from file, used to set the return value; yading@10: * if the codec is being flushed, flush the corresponding stream yading@10: * @return 1 if a frame was pushed on the requested output, yading@10: * 0 if another attempt is possible, yading@10: * <0 AVERROR code yading@10: */ yading@10: static int movie_push_frame(AVFilterContext *ctx, unsigned out_id) yading@10: { yading@10: MovieContext *movie = ctx->priv; yading@10: AVPacket *pkt = &movie->pkt; yading@10: MovieStream *st; yading@10: int ret, got_frame = 0, pkt_out_id; yading@10: AVFilterLink *outlink; yading@10: yading@10: if (!pkt->size) { yading@10: if (movie->eof) { yading@10: if (movie->st[out_id].done) { yading@10: if (movie->loop_count != 1) { yading@10: ret = rewind_file(ctx); yading@10: if (ret < 0) yading@10: return ret; yading@10: movie->loop_count -= movie->loop_count > 1; yading@10: av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); yading@10: return 0; /* retry */ yading@10: } yading@10: return AVERROR_EOF; yading@10: } yading@10: pkt->stream_index = movie->st[out_id].st->index; yading@10: /* packet is already ready for flushing */ yading@10: } else { yading@10: ret = av_read_frame(movie->format_ctx, &movie->pkt0); yading@10: if (ret < 0) { yading@10: av_init_packet(&movie->pkt0); /* ready for flushing */ yading@10: *pkt = movie->pkt0; yading@10: if (ret == AVERROR_EOF) { yading@10: movie->eof = 1; yading@10: return 0; /* start flushing */ yading@10: } yading@10: return ret; yading@10: } yading@10: *pkt = movie->pkt0; yading@10: } yading@10: } yading@10: yading@10: pkt_out_id = pkt->stream_index > movie->max_stream_index ? -1 : yading@10: movie->out_index[pkt->stream_index]; yading@10: if (pkt_out_id < 0) { yading@10: av_free_packet(&movie->pkt0); yading@10: pkt->size = 0; /* ready for next run */ yading@10: pkt->data = NULL; yading@10: return 0; yading@10: } yading@10: st = &movie->st[pkt_out_id]; yading@10: outlink = ctx->outputs[pkt_out_id]; yading@10: yading@10: movie->frame = av_frame_alloc(); yading@10: if (!movie->frame) yading@10: return AVERROR(ENOMEM); yading@10: yading@10: switch (st->st->codec->codec_type) { yading@10: case AVMEDIA_TYPE_VIDEO: yading@10: ret = avcodec_decode_video2(st->st->codec, movie->frame, &got_frame, pkt); yading@10: break; yading@10: case AVMEDIA_TYPE_AUDIO: yading@10: ret = avcodec_decode_audio4(st->st->codec, movie->frame, &got_frame, pkt); yading@10: break; yading@10: default: yading@10: ret = AVERROR(ENOSYS); yading@10: break; yading@10: } yading@10: if (ret < 0) { yading@10: av_log(ctx, AV_LOG_WARNING, "Decode error: %s\n", av_err2str(ret)); yading@10: av_frame_free(&movie->frame); yading@10: return 0; yading@10: } yading@10: if (!ret) yading@10: ret = pkt->size; yading@10: yading@10: pkt->data += ret; yading@10: pkt->size -= ret; yading@10: if (pkt->size <= 0) { yading@10: av_free_packet(&movie->pkt0); yading@10: pkt->size = 0; /* ready for next run */ yading@10: pkt->data = NULL; yading@10: } yading@10: if (!got_frame) { yading@10: if (!ret) yading@10: st->done = 1; yading@10: av_frame_free(&movie->frame); yading@10: return 0; yading@10: } yading@10: yading@10: av_dlog(ctx, "movie_push_frame(): file:'%s' %s\n", movie->file_name, yading@10: describe_frameref(movie->frame, outlink)); yading@10: yading@10: movie->frame->pts = av_frame_get_best_effort_timestamp(movie->frame); yading@10: ret = ff_filter_frame(outlink, movie->frame); yading@10: movie->frame = NULL; yading@10: yading@10: if (ret < 0) yading@10: return ret; yading@10: return pkt_out_id == out_id; yading@10: } yading@10: yading@10: static int movie_request_frame(AVFilterLink *outlink) yading@10: { yading@10: AVFilterContext *ctx = outlink->src; yading@10: unsigned out_id = FF_OUTLINK_IDX(outlink); yading@10: int ret; yading@10: yading@10: while (1) { yading@10: ret = movie_push_frame(ctx, out_id); yading@10: if (ret) yading@10: return FFMIN(ret, 0); yading@10: } yading@10: } yading@10: yading@10: #if CONFIG_MOVIE_FILTER yading@10: yading@10: AVFILTER_DEFINE_CLASS(movie); yading@10: yading@10: static av_cold int movie_init(AVFilterContext *ctx) yading@10: { yading@10: return movie_common_init(ctx); yading@10: } yading@10: yading@10: AVFilter avfilter_avsrc_movie = { yading@10: .name = "movie", yading@10: .description = NULL_IF_CONFIG_SMALL("Read from a movie source."), yading@10: .priv_size = sizeof(MovieContext), yading@10: .priv_class = &movie_class, yading@10: .init = movie_init, yading@10: .uninit = movie_uninit, yading@10: .query_formats = movie_query_formats, yading@10: yading@10: .inputs = NULL, yading@10: .outputs = NULL, yading@10: .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, yading@10: }; yading@10: yading@10: #endif /* CONFIG_MOVIE_FILTER */ yading@10: yading@10: #if CONFIG_AMOVIE_FILTER yading@10: yading@10: #define amovie_options movie_options yading@10: AVFILTER_DEFINE_CLASS(amovie); yading@10: yading@10: static av_cold int amovie_init(AVFilterContext *ctx) yading@10: { yading@10: return movie_common_init(ctx); yading@10: } yading@10: yading@10: AVFilter avfilter_avsrc_amovie = { yading@10: .name = "amovie", yading@10: .description = NULL_IF_CONFIG_SMALL("Read audio from a movie source."), yading@10: .priv_size = sizeof(MovieContext), yading@10: .init = amovie_init, yading@10: .uninit = movie_uninit, yading@10: .query_formats = movie_query_formats, yading@10: yading@10: .inputs = NULL, yading@10: .outputs = NULL, yading@10: .priv_class = &amovie_class, yading@10: .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, yading@10: }; yading@10: yading@10: #endif /* CONFIG_AMOVIE_FILTER */