Libav 0.7.1
|
00001 /* 00002 * Copyright (c) 2010 Stefano Sabatini 00003 * Copyright (c) 2008 Victor Paesa 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00031 /* #define DEBUG */ 00032 00033 #include <float.h> 00034 #include "libavutil/avstring.h" 00035 #include "libavutil/opt.h" 00036 #include "libavutil/imgutils.h" 00037 #include "libavformat/avformat.h" 00038 #include "avfilter.h" 00039 00040 typedef struct { 00041 const AVClass *class; 00042 int64_t seek_point; 00043 double seek_point_d; 00044 char *format_name; 00045 char *file_name; 00046 int stream_index; 00047 00048 AVFormatContext *format_ctx; 00049 AVCodecContext *codec_ctx; 00050 int is_done; 00051 AVFrame *frame; 00052 00053 int w, h; 00054 AVFilterBufferRef *picref; 00055 } MovieContext; 00056 00057 #define OFFSET(x) offsetof(MovieContext, x) 00058 00059 static const AVOption movie_options[]= { 00060 {"format_name", "set format name", OFFSET(format_name), FF_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, 00061 {"f", "set format name", OFFSET(format_name), FF_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, 00062 {"stream_index", "set stream index", OFFSET(stream_index), FF_OPT_TYPE_INT, {.dbl = -1}, -1, INT_MAX }, 00063 {"si", "set stream index", OFFSET(stream_index), FF_OPT_TYPE_INT, {.dbl = -1}, -1, INT_MAX }, 00064 {"seek_point", "set seekpoint (seconds)", OFFSET(seek_point_d), FF_OPT_TYPE_DOUBLE, {.dbl = 0}, 0, (INT64_MAX-1) / 1000000 }, 00065 {"sp", "set seekpoint (seconds)", OFFSET(seek_point_d), FF_OPT_TYPE_DOUBLE, {.dbl = 0}, 0, (INT64_MAX-1) / 1000000 }, 00066 {NULL}, 00067 }; 00068 00069 static const char *movie_get_name(void *ctx) 00070 { 00071 return "movie"; 00072 } 00073 00074 static const AVClass movie_class = { 00075 "MovieContext", 00076 movie_get_name, 00077 movie_options 00078 }; 00079 00080 static int movie_init(AVFilterContext *ctx) 00081 { 00082 MovieContext *movie = ctx->priv; 00083 AVInputFormat *iformat = NULL; 00084 AVCodec *codec; 00085 int ret; 00086 int64_t timestamp; 00087 00088 av_register_all(); 00089 00090 // Try to find the movie format (container) 00091 iformat = movie->format_name ? av_find_input_format(movie->format_name) : NULL; 00092 00093 movie->format_ctx = NULL; 00094 if ((ret = avformat_open_input(&movie->format_ctx, movie->file_name, iformat, NULL)) < 0) { 00095 av_log(ctx, AV_LOG_ERROR, 00096 "Failed to avformat_open_input '%s'\n", movie->file_name); 00097 return ret; 00098 } 00099 if ((ret = av_find_stream_info(movie->format_ctx)) < 0) 00100 av_log(ctx, AV_LOG_WARNING, "Failed to find stream info\n"); 00101 00102 // if seeking requested, we execute it 00103 if (movie->seek_point > 0) { 00104 timestamp = movie->seek_point; 00105 // add the stream start time, should it exist 00106 if (movie->format_ctx->start_time != AV_NOPTS_VALUE) { 00107 if (timestamp > INT64_MAX - movie->format_ctx->start_time) { 00108 av_log(ctx, AV_LOG_ERROR, 00109 "%s: seek value overflow with start_time:%"PRId64" seek_point:%"PRId64"\n", 00110 movie->file_name, movie->format_ctx->start_time, movie->seek_point); 00111 return AVERROR(EINVAL); 00112 } 00113 timestamp += movie->format_ctx->start_time; 00114 } 00115 if ((ret = av_seek_frame(movie->format_ctx, -1, timestamp, AVSEEK_FLAG_BACKWARD)) < 0) { 00116 av_log(ctx, AV_LOG_ERROR, "%s: could not seek to position %"PRId64"\n", 00117 movie->file_name, timestamp); 00118 return ret; 00119 } 00120 } 00121 00122 /* select the video stream */ 00123 if ((ret = av_find_best_stream(movie->format_ctx, AVMEDIA_TYPE_VIDEO, 00124 movie->stream_index, -1, NULL, 0)) < 0) { 00125 av_log(ctx, AV_LOG_ERROR, "No video stream with index '%d' found\n", 00126 movie->stream_index); 00127 return ret; 00128 } 00129 movie->stream_index = ret; 00130 movie->codec_ctx = movie->format_ctx->streams[movie->stream_index]->codec; 00131 00132 /* 00133 * So now we've got a pointer to the so-called codec context for our video 00134 * stream, but we still have to find the actual codec and open it. 00135 */ 00136 codec = avcodec_find_decoder(movie->codec_ctx->codec_id); 00137 if (!codec) { 00138 av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n"); 00139 return AVERROR(EINVAL); 00140 } 00141 00142 if ((ret = avcodec_open(movie->codec_ctx, codec)) < 0) { 00143 av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n"); 00144 return ret; 00145 } 00146 00147 if (!(movie->frame = avcodec_alloc_frame()) ) { 00148 av_log(ctx, AV_LOG_ERROR, "Failed to alloc frame\n"); 00149 return AVERROR(ENOMEM); 00150 } 00151 00152 movie->w = movie->codec_ctx->width; 00153 movie->h = movie->codec_ctx->height; 00154 00155 av_log(ctx, AV_LOG_INFO, "seek_point:%"PRIi64" format_name:%s file_name:%s stream_index:%d\n", 00156 movie->seek_point, movie->format_name, movie->file_name, 00157 movie->stream_index); 00158 00159 return 0; 00160 } 00161 00162 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) 00163 { 00164 MovieContext *movie = ctx->priv; 00165 int ret; 00166 movie->class = &movie_class; 00167 av_opt_set_defaults2(movie, 0, 0); 00168 00169 if (args) 00170 movie->file_name = av_get_token(&args, ":"); 00171 if (!movie->file_name || !*movie->file_name) { 00172 av_log(ctx, AV_LOG_ERROR, "No filename provided!\n"); 00173 return AVERROR(EINVAL); 00174 } 00175 00176 if (*args++ == ':' && (ret = av_set_options_string(movie, args, "=", ":")) < 0) { 00177 av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); 00178 return ret; 00179 } 00180 00181 movie->seek_point = movie->seek_point_d * 1000000 + 0.5; 00182 00183 return movie_init(ctx); 00184 } 00185 00186 static av_cold void uninit(AVFilterContext *ctx) 00187 { 00188 MovieContext *movie = ctx->priv; 00189 00190 av_free(movie->file_name); 00191 av_free(movie->format_name); 00192 if (movie->codec_ctx) 00193 avcodec_close(movie->codec_ctx); 00194 if (movie->format_ctx) 00195 av_close_input_file(movie->format_ctx); 00196 avfilter_unref_buffer(movie->picref); 00197 av_freep(&movie->frame); 00198 } 00199 00200 static int query_formats(AVFilterContext *ctx) 00201 { 00202 MovieContext *movie = ctx->priv; 00203 enum PixelFormat pix_fmts[] = { movie->codec_ctx->pix_fmt, PIX_FMT_NONE }; 00204 00205 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); 00206 return 0; 00207 } 00208 00209 static int config_output_props(AVFilterLink *outlink) 00210 { 00211 MovieContext *movie = outlink->src->priv; 00212 00213 outlink->w = movie->w; 00214 outlink->h = movie->h; 00215 outlink->time_base = movie->format_ctx->streams[movie->stream_index]->time_base; 00216 00217 return 0; 00218 } 00219 00220 static int movie_get_frame(AVFilterLink *outlink) 00221 { 00222 MovieContext *movie = outlink->src->priv; 00223 AVPacket pkt; 00224 int ret, frame_decoded; 00225 AVStream *st = movie->format_ctx->streams[movie->stream_index]; 00226 00227 if (movie->is_done == 1) 00228 return 0; 00229 00230 while ((ret = av_read_frame(movie->format_ctx, &pkt)) >= 0) { 00231 // Is this a packet from the video stream? 00232 if (pkt.stream_index == movie->stream_index) { 00233 movie->codec_ctx->reordered_opaque = pkt.pos; 00234 avcodec_decode_video2(movie->codec_ctx, movie->frame, &frame_decoded, &pkt); 00235 00236 if (frame_decoded) { 00237 /* FIXME: avoid the memcpy */ 00238 movie->picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE | AV_PERM_PRESERVE | 00239 AV_PERM_REUSE2, outlink->w, outlink->h); 00240 av_image_copy(movie->picref->data, movie->picref->linesize, 00241 movie->frame->data, movie->frame->linesize, 00242 movie->picref->format, outlink->w, outlink->h); 00243 00244 /* FIXME: use a PTS correction mechanism as that in 00245 * ffplay.c when some API will be available for that */ 00246 /* use pkt_dts if pkt_pts is not available */ 00247 movie->picref->pts = movie->frame->pkt_pts == AV_NOPTS_VALUE ? 00248 movie->frame->pkt_dts : movie->frame->pkt_pts; 00249 00250 movie->picref->pos = movie->frame->reordered_opaque; 00251 movie->picref->video->pixel_aspect = st->sample_aspect_ratio.num ? 00252 st->sample_aspect_ratio : movie->codec_ctx->sample_aspect_ratio; 00253 movie->picref->video->interlaced = movie->frame->interlaced_frame; 00254 movie->picref->video->top_field_first = movie->frame->top_field_first; 00255 movie->picref->video->key_frame = movie->frame->key_frame; 00256 movie->picref->video->pict_type = movie->frame->pict_type; 00257 av_dlog(outlink->src, 00258 "movie_get_frame(): file:'%s' pts:%"PRId64" time:%lf pos:%"PRId64" aspect:%d/%d\n", 00259 movie->file_name, movie->picref->pts, 00260 (double)movie->picref->pts * av_q2d(st->time_base), 00261 movie->picref->pos, 00262 movie->picref->video->pixel_aspect.num, movie->picref->video->pixel_aspect.den); 00263 // We got it. Free the packet since we are returning 00264 av_free_packet(&pkt); 00265 00266 return 0; 00267 } 00268 } 00269 // Free the packet that was allocated by av_read_frame 00270 av_free_packet(&pkt); 00271 } 00272 00273 // On multi-frame source we should stop the mixing process when 00274 // the movie source does not have more frames 00275 if (ret == AVERROR_EOF) 00276 movie->is_done = 1; 00277 return ret; 00278 } 00279 00280 static int request_frame(AVFilterLink *outlink) 00281 { 00282 AVFilterBufferRef *outpicref; 00283 MovieContext *movie = outlink->src->priv; 00284 int ret; 00285 00286 if (movie->is_done) 00287 return AVERROR_EOF; 00288 if ((ret = movie_get_frame(outlink)) < 0) 00289 return ret; 00290 00291 outpicref = avfilter_ref_buffer(movie->picref, ~0); 00292 avfilter_start_frame(outlink, outpicref); 00293 avfilter_draw_slice(outlink, 0, outlink->h, 1); 00294 avfilter_end_frame(outlink); 00295 avfilter_unref_buffer(movie->picref); 00296 movie->picref = NULL; 00297 00298 return 0; 00299 } 00300 00301 AVFilter avfilter_vsrc_movie = { 00302 .name = "movie", 00303 .description = NULL_IF_CONFIG_SMALL("Read from a movie source."), 00304 .priv_size = sizeof(MovieContext), 00305 .init = init, 00306 .uninit = uninit, 00307 .query_formats = query_formats, 00308 00309 .inputs = (AVFilterPad[]) {{ .name = NULL }}, 00310 .outputs = (AVFilterPad[]) {{ .name = "default", 00311 .type = AVMEDIA_TYPE_VIDEO, 00312 .request_frame = request_frame, 00313 .config_props = config_output_props, }, 00314 { .name = NULL}}, 00315 };