ffmpeg / libavfilter / vsrc_movie.c @ 2912e87a
History | View | Annotate | Download (10.9 KB)
1 | 9409c381 | Stefano Sabatini | /*
|
---|---|---|---|
2 | * Copyright (c) 2010 Stefano Sabatini
|
||
3 | * Copyright (c) 2008 Victor Paesa
|
||
4 | *
|
||
5 | 2912e87a | Mans Rullgard | * This file is part of Libav.
|
6 | 9409c381 | Stefano Sabatini | *
|
7 | 2912e87a | Mans Rullgard | * Libav is free software; you can redistribute it and/or
|
8 | 9409c381 | Stefano Sabatini | * 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 | 2912e87a | Mans Rullgard | * Libav is distributed in the hope that it will be useful,
|
13 | 9409c381 | Stefano Sabatini | * 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 | 2912e87a | Mans Rullgard | * License along with Libav; if not, write to the Free Software
|
19 | 9409c381 | Stefano Sabatini | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 | */
|
||
21 | |||
22 | /**
|
||
23 | * @file
|
||
24 | * movie video source
|
||
25 | *
|
||
26 | * @todo use direct rendering (no allocation of a new frame)
|
||
27 | * @todo support a PTS correction mechanism
|
||
28 | * @todo support more than one output stream
|
||
29 | */
|
||
30 | |||
31 | /* #define DEBUG */
|
||
32 | |||
33 | #include <float.h> |
||
34 | #include "libavutil/avstring.h" |
||
35 | #include "libavutil/opt.h" |
||
36 | 737eb597 | Reinhard Tartler | #include "libavutil/imgutils.h" |
37 | 9409c381 | Stefano Sabatini | #include "libavformat/avformat.h" |
38 | #include "avfilter.h" |
||
39 | |||
40 | typedef struct { |
||
41 | const AVClass *class;
|
||
42 | int64_t seek_point; ///< seekpoint in microseconds
|
||
43 | double seek_point_d;
|
||
44 | char *format_name;
|
||
45 | char *file_name;
|
||
46 | int stream_index;
|
||
47 | |||
48 | AVFormatContext *format_ctx; |
||
49 | AVCodecContext *codec_ctx; |
||
50 | int is_done;
|
||
51 | AVFrame *frame; ///< video frame to store the decoded images in
|
||
52 | |||
53 | int w, h;
|
||
54 | AVFilterBufferRef *picref; |
||
55 | } MovieContext; |
||
56 | |||
57 | #define OFFSET(x) offsetof(MovieContext, x)
|
||
58 | |||
59 | static const AVOption movie_options[]= { |
||
60 | {"format_name", "set format name", OFFSET(format_name), FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX }, |
||
61 | {"f", "set format name", OFFSET(format_name), FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX }, |
||
62 | {"stream_index", "set stream index", OFFSET(stream_index), FF_OPT_TYPE_INT, -1, -1, INT_MAX }, |
||
63 | {"si", "set stream index", OFFSET(stream_index), FF_OPT_TYPE_INT, -1, -1, INT_MAX }, |
||
64 | {"seek_point", "set seekpoint (seconds)", OFFSET(seek_point_d), FF_OPT_TYPE_DOUBLE, 0, 0, (INT64_MAX-1) / 1000000 }, |
||
65 | {"sp", "set seekpoint (seconds)", OFFSET(seek_point_d), FF_OPT_TYPE_DOUBLE, 0, 0, (INT64_MAX-1) / 1000000 }, |
||
66 | {NULL},
|
||
67 | }; |
||
68 | |||
69 | static const char *movie_get_name(void *ctx) |
||
70 | { |
||
71 | return "movie"; |
||
72 | } |
||
73 | |||
74 | static const AVClass movie_class = { |
||
75 | "MovieContext",
|
||
76 | movie_get_name, |
||
77 | movie_options |
||
78 | }; |
||
79 | |||
80 | static int movie_init(AVFilterContext *ctx) |
||
81 | { |
||
82 | MovieContext *movie = ctx->priv; |
||
83 | AVInputFormat *iformat = NULL;
|
||
84 | AVCodec *codec; |
||
85 | int ret;
|
||
86 | int64_t timestamp; |
||
87 | |||
88 | av_register_all(); |
||
89 | |||
90 | // Try to find the movie format (container)
|
||
91 | iformat = movie->format_name ? av_find_input_format(movie->format_name) : NULL;
|
||
92 | |||
93 | movie->format_ctx = NULL;
|
||
94 | if ((ret = av_open_input_file(&movie->format_ctx, movie->file_name, iformat, 0, NULL)) < 0) { |
||
95 | av_log(ctx, AV_LOG_ERROR, |
||
96 | "Failed to av_open_input_file '%s'\n", movie->file_name);
|
||
97 | return ret;
|
||
98 | } |
||
99 | if ((ret = av_find_stream_info(movie->format_ctx)) < 0) |
||
100 | av_log(ctx, AV_LOG_WARNING, "Failed to find stream info\n");
|
||
101 | |||
102 | // if seeking requested, we execute it
|
||
103 | if (movie->seek_point > 0) { |
||
104 | timestamp = movie->seek_point; |
||
105 | // add the stream start time, should it exist
|
||
106 | if (movie->format_ctx->start_time != AV_NOPTS_VALUE) {
|
||
107 | if (timestamp > INT64_MAX - movie->format_ctx->start_time) {
|
||
108 | av_log(ctx, AV_LOG_ERROR, |
||
109 | "%s: seek value overflow with start_time:%"PRId64" seek_point:%"PRId64"\n", |
||
110 | movie->file_name, movie->format_ctx->start_time, movie->seek_point); |
||
111 | return AVERROR(EINVAL);
|
||
112 | } |
||
113 | timestamp += movie->format_ctx->start_time; |
||
114 | } |
||
115 | if ((ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD)) < 0) { |
||
116 | av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n", |
||
117 | movie->file_name, timestamp); |
||
118 | return ret;
|
||
119 | } |
||
120 | } |
||
121 | |||
122 | /* select the video stream */
|
||
123 | if ((ret = av_find_best_stream(movie->format_ctx, AVMEDIA_TYPE_VIDEO,
|
||
124 | movie->stream_index, -1, NULL, 0)) < 0) { |
||
125 | av_log(ctx, AV_LOG_ERROR, "No video stream with index '%d' found\n",
|
||
126 | movie->stream_index); |
||
127 | return ret;
|
||
128 | } |
||
129 | movie->stream_index = ret; |
||
130 | movie->codec_ctx = movie->format_ctx->streams[movie->stream_index]->codec; |
||
131 | |||
132 | /*
|
||
133 | * So now we've got a pointer to the so-called codec context for our video
|
||
134 | * stream, but we still have to find the actual codec and open it.
|
||
135 | */
|
||
136 | codec = avcodec_find_decoder(movie->codec_ctx->codec_id); |
||
137 | if (!codec) {
|
||
138 | av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n");
|
||
139 | return AVERROR(EINVAL);
|
||
140 | } |
||
141 | |||
142 | if ((ret = avcodec_open(movie->codec_ctx, codec)) < 0) { |
||
143 | av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n");
|
||
144 | return ret;
|
||
145 | } |
||
146 | |||
147 | if (!(movie->frame = avcodec_alloc_frame()) ) {
|
||
148 | av_log(ctx, AV_LOG_ERROR, "Failed to alloc frame\n");
|
||
149 | return AVERROR(ENOMEM);
|
||
150 | } |
||
151 | |||
152 | movie->w = movie->codec_ctx->width; |
||
153 | movie->h = movie->codec_ctx->height; |
||
154 | |||
155 | av_log(ctx, AV_LOG_INFO, "seek_point:%lld format_name:%s file_name:%s stream_index:%d\n",
|
||
156 | movie->seek_point, movie->format_name, movie->file_name, |
||
157 | movie->stream_index); |
||
158 | |||
159 | return 0; |
||
160 | } |
||
161 | |||
162 | static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) |
||
163 | { |
||
164 | MovieContext *movie = ctx->priv; |
||
165 | int ret;
|
||
166 | movie->class = &movie_class; |
||
167 | av_opt_set_defaults2(movie, 0, 0); |
||
168 | |||
169 | if (args)
|
||
170 | movie->file_name = av_get_token(&args, ":");
|
||
171 | if (!movie->file_name || !*movie->file_name) {
|
||
172 | av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
|
||
173 | return AVERROR(EINVAL);
|
||
174 | } |
||
175 | |||
176 | if (*args++ == ':' && (ret = av_set_options_string(movie, args, "=", ":")) < 0) { |
||
177 | av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
|
||
178 | return ret;
|
||
179 | } |
||
180 | |||
181 | movie->seek_point = movie->seek_point_d * 1000000 + 0.5; |
||
182 | |||
183 | return movie_init(ctx);
|
||
184 | } |
||
185 | |||
186 | static av_cold void uninit(AVFilterContext *ctx) |
||
187 | { |
||
188 | MovieContext *movie = ctx->priv; |
||
189 | |||
190 | av_free(movie->file_name); |
||
191 | av_free(movie->format_name); |
||
192 | if (movie->codec_ctx)
|
||
193 | avcodec_close(movie->codec_ctx); |
||
194 | if (movie->format_ctx)
|
||
195 | av_close_input_file(movie->format_ctx); |
||
196 | avfilter_unref_buffer(movie->picref); |
||
197 | av_freep(&movie->frame); |
||
198 | } |
||
199 | |||
200 | static int query_formats(AVFilterContext *ctx) |
||
201 | { |
||
202 | MovieContext *movie = ctx->priv; |
||
203 | enum PixelFormat pix_fmts[] = { movie->codec_ctx->pix_fmt, PIX_FMT_NONE };
|
||
204 | |||
205 | avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); |
||
206 | return 0; |
||
207 | } |
||
208 | |||
209 | static int config_output_props(AVFilterLink *outlink) |
||
210 | { |
||
211 | MovieContext *movie = outlink->src->priv; |
||
212 | |||
213 | outlink->w = movie->w; |
||
214 | outlink->h = movie->h; |
||
215 | outlink->time_base = movie->format_ctx->streams[movie->stream_index]->time_base; |
||
216 | |||
217 | return 0; |
||
218 | } |
||
219 | |||
220 | static int movie_get_frame(AVFilterLink *outlink) |
||
221 | { |
||
222 | MovieContext *movie = outlink->src->priv; |
||
223 | AVPacket pkt; |
||
224 | int ret, frame_decoded;
|
||
225 | AVStream *st = movie->format_ctx->streams[movie->stream_index]; |
||
226 | |||
227 | if (movie->is_done == 1) |
||
228 | return 0; |
||
229 | |||
230 | while ((ret = av_read_frame(movie->format_ctx, &pkt)) >= 0) { |
||
231 | // Is this a packet from the video stream?
|
||
232 | if (pkt.stream_index == movie->stream_index) {
|
||
233 | movie->codec_ctx->reordered_opaque = pkt.pos; |
||
234 | avcodec_decode_video2(movie->codec_ctx, movie->frame, &frame_decoded, &pkt); |
||
235 | |||
236 | if (frame_decoded) {
|
||
237 | /* FIXME: avoid the memcpy */
|
||
238 | movie->picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE | AV_PERM_PRESERVE | |
||
239 | AV_PERM_REUSE2, outlink->w, outlink->h); |
||
240 | av_image_copy(movie->picref->data, movie->picref->linesize, |
||
241 | movie->frame->data, movie->frame->linesize, |
||
242 | movie->picref->format, outlink->w, outlink->h); |
||
243 | |||
244 | /* FIXME: use a PTS correction mechanism as that in
|
||
245 | * ffplay.c when some API will be available for that */
|
||
246 | /* use pkt_dts if pkt_pts is not available */
|
||
247 | movie->picref->pts = movie->frame->pkt_pts == AV_NOPTS_VALUE ? |
||
248 | movie->frame->pkt_dts : movie->frame->pkt_pts; |
||
249 | |||
250 | movie->picref->pos = movie->frame->reordered_opaque; |
||
251 | movie->picref->video->pixel_aspect = st->sample_aspect_ratio.num ? |
||
252 | st->sample_aspect_ratio : movie->codec_ctx->sample_aspect_ratio; |
||
253 | movie->picref->video->interlaced = movie->frame->interlaced_frame; |
||
254 | movie->picref->video->top_field_first = movie->frame->top_field_first; |
||
255 | av_dlog(outlink->src, |
||
256 | "movie_get_frame(): file:'%s' pts:%"PRId64" time:%lf pos:%"PRId64" aspect:%d/%d\n", |
||
257 | movie->file_name, movie->picref->pts, |
||
258 | (double)movie->picref->pts * av_q2d(st->time_base),
|
||
259 | movie->picref->pos, |
||
260 | movie->picref->video->pixel_aspect.num, movie->picref->video->pixel_aspect.den); |
||
261 | // We got it. Free the packet since we are returning
|
||
262 | av_free_packet(&pkt); |
||
263 | |||
264 | return 0; |
||
265 | } |
||
266 | } |
||
267 | // Free the packet that was allocated by av_read_frame
|
||
268 | av_free_packet(&pkt); |
||
269 | } |
||
270 | |||
271 | // On multi-frame source we should stop the mixing process when
|
||
272 | // the movie source does not have more frames
|
||
273 | if (ret == AVERROR_EOF)
|
||
274 | movie->is_done = 1;
|
||
275 | return ret;
|
||
276 | } |
||
277 | |||
278 | static int request_frame(AVFilterLink *outlink) |
||
279 | { |
||
280 | AVFilterBufferRef *outpicref; |
||
281 | MovieContext *movie = outlink->src->priv; |
||
282 | int ret;
|
||
283 | |||
284 | if (movie->is_done)
|
||
285 | return AVERROR_EOF;
|
||
286 | if ((ret = movie_get_frame(outlink)) < 0) |
||
287 | return ret;
|
||
288 | |||
289 | outpicref = avfilter_ref_buffer(movie->picref, ~0);
|
||
290 | avfilter_start_frame(outlink, outpicref); |
||
291 | avfilter_draw_slice(outlink, 0, outlink->h, 1); |
||
292 | avfilter_end_frame(outlink); |
||
293 | |||
294 | return 0; |
||
295 | } |
||
296 | |||
297 | AVFilter avfilter_vsrc_movie = { |
||
298 | .name = "movie",
|
||
299 | .description = NULL_IF_CONFIG_SMALL("Read from a movie source."),
|
||
300 | .priv_size = sizeof(MovieContext),
|
||
301 | .init = init, |
||
302 | .uninit = uninit, |
||
303 | .query_formats = query_formats, |
||
304 | |||
305 | .inputs = (AVFilterPad[]) {{ .name = NULL }},
|
||
306 | .outputs = (AVFilterPad[]) {{ .name = "default",
|
||
307 | .type = AVMEDIA_TYPE_VIDEO, |
||
308 | .request_frame = request_frame, |
||
309 | .config_props = config_output_props, }, |
||
310 | { .name = NULL}},
|
||
311 | }; |