[FFmpeg-devel] [PATCH] avcodec: export motion vectors in frame side data on demand
Stefano Sabatini
stefasab at gmail.com
Thu Aug 14 13:19:10 CEST 2014
On date Monday 2014-08-11 15:22:59 +0200, Clément Bœsch encoded:
> From: Clément Bœsch <clement at stupeflix.com>
>
> The reasoning behind this addition is that various third party
> applications are interested in getting some motion information out of a
> video "for free" when it is available.
>
> It was considered to export other information as well (such as the intra
> information about the block, or the quantization) but the structure
> might have ended up into a half full-generic, half full of codec
> specific cruft. If more information is necessary, it should either be
> added in the "flags" field of the AVExportedMV structure, or in another
> side-data.
>
> This commit also includes an example exporting them in a CSV stream.
> ---
> TODO: avcodec version bump & APIChanges entry
> ---
> .gitignore | 1 +
> configure | 2 +
> doc/Makefile | 1 +
> doc/codecs.texi | 3 +
> doc/examples/Makefile | 1 +
> doc/examples/extract_mvs.c | 185 +++++++++++++++++++++++++++++++++++++++++++++
> libavcodec/avcodec.h | 1 +
> libavcodec/mpegvideo.c | 102 ++++++++++++++++++++++++-
> libavcodec/options_table.h | 1 +
> libavutil/frame.h | 6 ++
> libavutil/mvinfo.h | 49 ++++++++++++
You probably need to add this to the list of public headers in
libavcodec/Makefile.
> 11 files changed, 351 insertions(+), 1 deletion(-)
> create mode 100644 doc/examples/extract_mvs.c
> create mode 100644 libavutil/mvinfo.h
>
> diff --git a/.gitignore b/.gitignore
> index cb370bb..480fbe0 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -39,6 +39,7 @@
> /doc/examples/avio_reading
> /doc/examples/decoding_encoding
> /doc/examples/demuxing_decoding
> +/doc/examples/extract_mvs
> /doc/examples/filter_audio
> /doc/examples/filtering_audio
> /doc/examples/filtering_video
> diff --git a/configure b/configure
> index 0ac6132..a93fc06 100755
> --- a/configure
> +++ b/configure
> @@ -1306,6 +1306,7 @@ EXAMPLE_LIST="
> avio_reading_example
> decoding_encoding_example
> demuxing_decoding_example
> + extract_mvs_example
> filter_audio_example
> filtering_audio_example
> filtering_video_example
> @@ -2586,6 +2587,7 @@ zoompan_filter_deps="swscale"
> avio_reading="avformat avcodec avutil"
> avcodec_example_deps="avcodec avutil"
> demuxing_decoding_example_deps="avcodec avformat avutil"
> +extract_mvs_example_deps="avcodec avformat avutil"
> filter_audio_example_deps="avfilter avutil"
> filtering_audio_example_deps="avfilter avcodec avformat avutil"
> filtering_video_example_deps="avfilter avcodec avformat avutil"
> diff --git a/doc/Makefile b/doc/Makefile
> index 99f588a..2fb9058 100644
> --- a/doc/Makefile
> +++ b/doc/Makefile
> @@ -39,6 +39,7 @@ DOCS = $(DOCS-yes)
> DOC_EXAMPLES-$(CONFIG_AVIO_READING_EXAMPLE) += avio_reading
> DOC_EXAMPLES-$(CONFIG_AVCODEC_EXAMPLE) += avcodec
> DOC_EXAMPLES-$(CONFIG_DEMUXING_DECODING_EXAMPLE) += demuxing_decoding
> +DOC_EXAMPLES-$(CONFIG_EXTRACT_MVS_EXAMPLE) += extract_mvs
> DOC_EXAMPLES-$(CONFIG_FILTER_AUDIO_EXAMPLE) += filter_audio
> DOC_EXAMPLES-$(CONFIG_FILTERING_AUDIO_EXAMPLE) += filtering_audio
> DOC_EXAMPLES-$(CONFIG_FILTERING_VIDEO_EXAMPLE) += filtering_video
> diff --git a/doc/codecs.texi b/doc/codecs.texi
> index 1160e5d..7aaa229 100644
> --- a/doc/codecs.texi
> +++ b/doc/codecs.texi
> @@ -797,6 +797,9 @@ Frame data might be split into multiple chunks.
> Show all frames before the first keyframe.
> @item skiprd
> Deprecated, use mpegvideo private options instead.
> + at item export_mvs
> +Export motion vectors into frame side-data (see @code{AV_FRAME_DATA_MV_INFO})
> +for codecs that support it. See also @file{doc/examples/export_mvs.c}.
> @end table
>
> @item error @var{integer} (@emph{encoding,video})
> diff --git a/doc/examples/Makefile b/doc/examples/Makefile
> index 03c7021..07251fe 100644
> --- a/doc/examples/Makefile
> +++ b/doc/examples/Makefile
> @@ -14,6 +14,7 @@ LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS)
> EXAMPLES= avio_reading \
> decoding_encoding \
> demuxing_decoding \
> + extract_mvs \
> filtering_video \
> filtering_audio \
> metadata \
> diff --git a/doc/examples/extract_mvs.c b/doc/examples/extract_mvs.c
> new file mode 100644
> index 0000000..69f76cd
> --- /dev/null
> +++ b/doc/examples/extract_mvs.c
> @@ -0,0 +1,185 @@
> +/*
> + * Copyright (c) 2012 Stefano Sabatini
> + * Copyright (c) 2014 Clément Bœsch
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include <libavutil/mvinfo.h>
> +#include <libavformat/avformat.h>
> +
> +static AVFormatContext *fmt_ctx = NULL;
> +static AVCodecContext *video_dec_ctx = NULL;
> +static AVStream *video_stream = NULL;
> +static const char *src_filename = NULL;
> +
> +static int video_stream_idx = -1;
> +static AVFrame *frame = NULL;
> +static AVPacket pkt;
> +static int video_frame_count = 0;
> +
> +static int decode_packet(int *got_frame, int cached)
> +{
> + int decoded = pkt.size;
> +
> + *got_frame = 0;
> +
> + if (pkt.stream_index == video_stream_idx) {
> + int ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
> + if (ret < 0) {
> + fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret));
> + return ret;
> + }
> +
> + if (*got_frame) {
> + int i;
> + AVFrameSideData *sd;
> +
> + video_frame_count++;
> + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MV_INFO);
> + if (sd) {
> + const AVExportedMV *mvs = (const AVExportedMV *)sd->data;
> + for (i = 0; i < sd->size / sizeof(*mvs); i++) {
> + const AVExportedMV *mv = &mvs[i];
> + printf("%d,%2d,%2d,%2d,%4d,%4d,%4d,%4d,0x%016x\n",
> + video_frame_count, mv->source,
> + mv->w, mv->h, mv->src_x, mv->src_y,
> + mv->dst_x, mv->dst_y, mv->flags);
> + }
> + }
> + }
> + }
> +
> + return decoded;
> +}
> +
> +static int open_codec_context(int *stream_idx,
> + AVFormatContext *fmt_ctx, enum AVMediaType type)
> +{
> + int ret;
> + AVStream *st;
> + AVCodecContext *dec_ctx = NULL;
> + AVCodec *dec = NULL;
> + AVDictionary *opts = NULL;
> +
> + ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
> + if (ret < 0) {
> + fprintf(stderr, "Could not find %s stream in input file '%s'\n",
> + av_get_media_type_string(type), src_filename);
> + return ret;
> + } else {
> + *stream_idx = ret;
> + st = fmt_ctx->streams[*stream_idx];
> +
> + /* find decoder for the stream */
> + dec_ctx = st->codec;
> + dec = avcodec_find_decoder(dec_ctx->codec_id);
> + if (!dec) {
> + fprintf(stderr, "Failed to find %s codec\n",
> + av_get_media_type_string(type));
> + return AVERROR(EINVAL);
> + }
> +
> + /* Init the video decoder */
> + av_dict_set(&opts, "flags2", "+export_mvs", 0);
> + if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
> + fprintf(stderr, "Failed to open %s codec\n",
> + av_get_media_type_string(type));
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + int ret = 0, got_frame;
> +
> + if (argc != 2) {
> + fprintf(stderr, "Usage: %s <video>\n", argv[0]);
> + exit(1);
> + }
> + src_filename = argv[1];
> +
> + av_register_all();
> +
> + if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
> + fprintf(stderr, "Could not open source file %s\n", src_filename);
> + exit(1);
> + }
> +
> + if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
> + fprintf(stderr, "Could not find stream information\n");
> + exit(1);
> + }
> +
> + if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
> + video_stream = fmt_ctx->streams[video_stream_idx];
> + video_dec_ctx = video_stream->codec;
> + }
> +
> + av_dump_format(fmt_ctx, 0, src_filename, 0);
> +
> + if (!video_stream) {
> + fprintf(stderr, "Could not find video stream in the input, aborting\n");
> + ret = 1;
> + goto end;
> + }
> +
> + frame = av_frame_alloc();
> + if (!frame) {
> + fprintf(stderr, "Could not allocate frame\n");
> + ret = AVERROR(ENOMEM);
> + goto end;
> + }
> +
> + printf("framenum,source,blockw,blockh,srcx,srcy,dstx,dsty,flags\n");
> +
> + /* initialize packet, set data to NULL, let the demuxer fill it */
> + av_init_packet(&pkt);
> + pkt.data = NULL;
> + pkt.size = 0;
> +
> + /* read frames from the file */
> + while (av_read_frame(fmt_ctx, &pkt) >= 0) {
> + AVPacket orig_pkt = pkt;
> + do {
> + ret = decode_packet(&got_frame, 0);
> + if (ret < 0)
> + break;
> + pkt.data += ret;
> + pkt.size -= ret;
> + } while (pkt.size > 0);
> + av_free_packet(&orig_pkt);
> + }
> +
> + /* flush cached frames */
> + pkt.data = NULL;
> + pkt.size = 0;
> + do {
> + decode_packet(&got_frame, 1);
> + } while (got_frame);
> +
> +end:
> + avcodec_close(video_dec_ctx);
> + avformat_close_input(&fmt_ctx);
> + av_frame_free(&frame);
> + return ret < 0;
> +}
Alternatively, hack demuxing_decoding.c since much code is shared
(this has pluses - less code duplication, smaller maintainance cost, and
cons - more complexity for the user).
Also, do you think this could be exposed by ffprobe?
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 3b6a750..efe3dbd 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -767,6 +767,7 @@ typedef struct RcOverride{
>
> #define CODEC_FLAG2_CHUNKS 0x00008000 ///< Input bitstream might be truncated at a packet boundaries instead of only at frame boundaries.
> #define CODEC_FLAG2_SHOW_ALL 0x00400000 ///< Show all frames before the first keyframe
> +#define CODEC_FLAG2_EXPORT_MVS 0x10000000 ///< Export motion vectors through frame side data
>
> /* Unsupported options :
> * Syntax Arithmetic coding (SAC)
> diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c
> index 4672359..fe11089 100644
> --- a/libavcodec/mpegvideo.c
> +++ b/libavcodec/mpegvideo.c
> @@ -31,6 +31,7 @@
> #include "libavutil/avassert.h"
> #include "libavutil/imgutils.h"
> #include "libavutil/internal.h"
> +#include "libavutil/mvinfo.h"
> #include "libavutil/timer.h"
> #include "avcodec.h"
> #include "blockdsp.h"
> @@ -596,7 +597,8 @@ static int alloc_picture_tables(MpegEncContext *s, Picture *pic)
> return AVERROR(ENOMEM);
> }
>
> - if (s->out_format == FMT_H263 || s->encoding || s->avctx->debug_mv) {
> + if (s->out_format == FMT_H263 || s->encoding || s->avctx->debug_mv ||
> + (s->avctx->flags2 & CODEC_FLAG2_EXPORT_MVS)) {
> int mv_size = 2 * (b8_array_size + 4) * sizeof(int16_t);
> int ref_index_size = 4 * mb_array_size;
>
> @@ -2104,6 +2106,24 @@ static void draw_arrow(uint8_t *buf, int sx, int sy, int ex,
> draw_line(buf, sx, sy, ex, ey, w, h, stride, color);
> }
>
> +static int add_mb(AVExportedMV *mb, uint32_t mb_type,
> + int dst_x, int dst_y,
> + int src_x, int src_y,
> + int direction)
> +{
> + if (dst_x == src_x && dst_y == src_y)
> + return 0;
> + mb->w = IS_8X8(mb_type) || IS_8X16(mb_type) ? 8 : 16;
> + mb->h = IS_8X8(mb_type) || IS_16X8(mb_type) ? 8 : 16;
> + mb->src_x = src_x;
> + mb->src_y = src_y;
> + mb->dst_x = dst_x;
> + mb->dst_y = dst_y;
> + mb->source = direction ? 1 : -1;
> + mb->flags = 0; // XXX: does mb_type contain extra information that could be exported here?
> + return 1;
> +}
> +
> /**
> * Print debugging info for the given picture.
> */
> @@ -2112,6 +2132,86 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, uint8_t *mbskip_
> int *low_delay,
> int mb_width, int mb_height, int mb_stride, int quarter_sample)
> {
> + if ((avctx->flags2 & CODEC_FLAG2_EXPORT_MVS) && mbtype_table && motion_val[0]) {
> + const int shift = 1 + quarter_sample;
> + const int mv_sample_log2 = avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_SVQ3 ? 2 : 1;
> + const int mv_stride = (mb_width << mv_sample_log2) +
> + (avctx->codec->id == AV_CODEC_ID_H264 ? 0 : 1);
> + int mb_x, mb_y, mbcount = 0;
> +
> + /* width * height * directions * 4MB (4MB for IS_8x8) */
this comment together with the following "2 * 4" is confusing
(especially for a naive reader - as me)
> + AVExportedMV *mvs = av_malloc_array(mb_width * mb_height, 2 * 4 * sizeof(AVExportedMV));
> + if (!mvs)
> + return;
> +
> + for (mb_y = 0; mb_y < mb_height; mb_y++) {
> + for (mb_x = 0; mb_x < mb_width; mb_x++) {
> + int i, direction, mb_type = mbtype_table[mb_x + mb_y * mb_stride];
> + for (direction = 0; direction < 2; direction++) {
> + if (!USES_LIST(mb_type, direction))
> + continue;
> + if (IS_8X8(mb_type)) {
> + for (i = 0; i < 4; i++) {
> + int sx = mb_x * 16 + 4 + 8 * (i & 1);
> + int sy = mb_y * 16 + 4 + 8 * (i >> 1);
> + int xy = (mb_x * 2 + (i & 1) +
> + (mb_y * 2 + (i >> 1)) * mv_stride) << (mv_sample_log2 - 1);
> + int mx = (motion_val[direction][xy][0] >> shift) + sx;
> + int my = (motion_val[direction][xy][1] >> shift) + sy;
> + mbcount += add_mb(mvs + mbcount, mb_type, sx, sy, mx, my, direction);
> + }
> + } else if (IS_16X8(mb_type)) {
> + for (i = 0; i < 2; i++) {
> + int sx = mb_x * 16 + 8;
> + int sy = mb_y * 16 + 4 + 8 * i;
> + int xy = (mb_x * 2 + (mb_y * 2 + i) * mv_stride) << (mv_sample_log2 - 1);
> + int mx = (motion_val[direction][xy][0] >> shift);
> + int my = (motion_val[direction][xy][1] >> shift);
> +
> + if (IS_INTERLACED(mb_type))
> + my *= 2;
> +
> + mbcount += add_mb(mvs + mbcount, mb_type, sx, sy, mx + sx, my + sy, direction);
> + }
> + } else if (IS_8X16(mb_type)) {
> + for (i = 0; i < 2; i++) {
> + int sx = mb_x * 16 + 4 + 8 * i;
> + int sy = mb_y * 16 + 8;
> + int xy = (mb_x * 2 + i + mb_y * 2 * mv_stride) << (mv_sample_log2 - 1);
> + int mx = motion_val[direction][xy][0] >> shift;
> + int my = motion_val[direction][xy][1] >> shift;
> +
> + if (IS_INTERLACED(mb_type))
> + my *= 2;
> +
> + mbcount += add_mb(mvs + mbcount, mb_type, sx, sy, mx + sx, my + sy, direction);
> + }
> + } else {
> + int sx = mb_x * 16 + 8;
> + int sy = mb_y * 16 + 8;
> + int xy = (mb_x + mb_y * mv_stride) << mv_sample_log2;
> + int mx = (motion_val[direction][xy][0]>>shift) + sx;
> + int my = (motion_val[direction][xy][1]>>shift) + sy;
> + mbcount += add_mb(mvs + mbcount, mb_type, sx, sy, mx, my, direction);
> + }
> + }
> + }
> + }
Uhm, duplicated non-trivial code, probably you can create a dedicated
routine to factorize this with the code below in the function.
> + if (mbcount) {
> + AVFrameSideData *sd;
> +
> + av_log(avctx, AV_LOG_DEBUG, "Adding %d MVs info to frame %d\n", mbcount, avctx->frame_number);
> + sd = av_frame_new_side_data(pict, AV_FRAME_DATA_MV_INFO, mbcount * sizeof(AVExportedMV));
> + if (!sd)
> + return;
> + memcpy(sd->data, mvs, mbcount * sizeof(AVExportedMV));
> + }
> +
> + av_freep(&mvs);
> + }
> +
> + /* TODO: export all the following to make them accessible for users (and filters) */
> if (avctx->hwaccel || !mbtype_table
> || (avctx->codec->capabilities&CODEC_CAP_HWACCEL_VDPAU))
> return;
> diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
> index 2e9dfa0..7000531 100644
> --- a/libavcodec/options_table.h
> +++ b/libavcodec/options_table.h
> @@ -88,6 +88,7 @@ static const AVOption avcodec_options[] = {
> {"local_header", "place global headers at every keyframe instead of in extradata", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_LOCAL_HEADER }, INT_MIN, INT_MAX, V|E, "flags2"},
> {"chunks", "Frame data might be split into multiple chunks", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_CHUNKS }, INT_MIN, INT_MAX, V|D, "flags2"},
> {"showall", "Show all frames before the first keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_SHOW_ALL }, INT_MIN, INT_MAX, V|D, "flags2"},
> +{"export_mvs", "export motion vectors through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_EXPORT_MVS}, INT_MIN, INT_MAX, V|D, "flags2"},
> {"me_method", "set motion estimation method", OFFSET(me_method), AV_OPT_TYPE_INT, {.i64 = ME_EPZS }, INT_MIN, INT_MAX, V|E, "me_method"},
> {"zero", "zero motion estimation (fastest)", 0, AV_OPT_TYPE_CONST, {.i64 = ME_ZERO }, INT_MIN, INT_MAX, V|E, "me_method" },
> {"full", "full motion estimation (slowest)", 0, AV_OPT_TYPE_CONST, {.i64 = ME_FULL }, INT_MIN, INT_MAX, V|E, "me_method" },
> diff --git a/libavutil/frame.h b/libavutil/frame.h
> index dbbdd29..bd3be03 100644
> --- a/libavutil/frame.h
> +++ b/libavutil/frame.h
> @@ -87,6 +87,12 @@ enum AVFrameSideDataType {
> * in ETSI TS 101 154 using AVActiveFormatDescription enum.
> */
> AV_FRAME_DATA_AFD,
> + /**
> + * Motion vectors exported by some codecs (on demand through
> + * -flags2 export_mvs).
Nit, since this is library documentation:
* Motion vectors exported by some codecs (on demand through the
* export_mvs flag set in the libavcodec AVCodecContext flags2
* option).
> + * The data is the AVExportedMV struct defined in libavutil/mvinfo.h.
> + */
> + AV_FRAME_DATA_MV_INFO,
Nit+: I'd prefer to color it as AV_FRAME_DATA_MV or
AV_FRAME_DATA_MOTION_VECTOR, since "INFO" is really generic (and is
never used in the other enum named constants).
> };
>
> enum AVActiveFormatDescription {
> diff --git a/libavutil/mvinfo.h b/libavutil/mvinfo.h
> new file mode 100644
> index 0000000..735f1b9
> --- /dev/null
> +++ b/libavutil/mvinfo.h
> @@ -0,0 +1,49 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVUTIL_MVINFO_H
> +#define AVUTIL_MVINFO_H
> +
> +#include <stdint.h>
> +
> +typedef struct AVExportedMV {
> + /**
> + * Where the current comes from; negative value for past, positive value future.
> + * XXX: set exact relative ref frame reference instead of a +/- 1 "direction".
> + */
> + int32_t source;
> + /**
> + * Width and height of the block.
> + */
> + uint8_t w, h;
> + /**
> + * Absolute source position.
> + */
> + uint16_t src_x, src_y;
> + /**
> + * Absolute destination position.
> + */
> + uint16_t dst_x, dst_y;
> + /**
> + * Extra flag information.
> + * Currently unused.
> + */
> + uint64_t flags;
> +} AVExportedMV;
> +
> +#endif /* AVUTIL_MVINFO_H */
Nit++: AVMVInfo or simply AVMotionVector ("Exported" and "Info" are too
generic).
--
FFmpeg = Fostering and Forgiving Mysterious Proud Easy Generator
More information about the ffmpeg-devel
mailing list