Commit 7700501e authored by Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf

Contribs: take libvpx GIT and not the tarballs

Because now the license is ok. And update the patches on ffmpeg
parent 06ed4654
...@@ -911,18 +911,18 @@ DISTCLEAN_PKG += faad2-$(FAAD2_VERSION).tar.gz ...@@ -911,18 +911,18 @@ DISTCLEAN_PKG += faad2-$(FAAD2_VERSION).tar.gz
# libvp8 # libvp8
# *************************************************************************** # ***************************************************************************
libvpx-$(VPX_VERSION).tar.bz2: #ibvpx-$(VPX_VERSION).tar.bz2:
$(WGET) $(VPX_URL) #$(WGET) $(VPX_URL)
vpx: libvpx-$(VPX_VERSION).tar.bz2 libvpx:
$(EXTRACT_BZ2) $(GIT) clone git://review.webmproject.org/libvpx.git
patch -p0 < Patches/libvpx-cross.patch
ifdef HAVE_DARWIN_OS ifdef HAVE_DARWIN_OS
patch -p0 < Patches/libvpx-darwin.patch patch -p0 < Patches/libvpx-darwin.patch
endif endif
ifdef HAVE_WIN32 ifdef HAVE_WIN32
VPX_TARGET=x86-win32-gcc VPX_TARGET=x86-win32-gcc
CROSS=
else else
ifdef HAVE_DARWIN_OS ifdef HAVE_DARWIN_OS
ifdef HAVE_DARWIN_64 ifdef HAVE_DARWIN_64
...@@ -939,16 +939,16 @@ VPX_TARGET=FIXME ...@@ -939,16 +939,16 @@ VPX_TARGET=FIXME
endif endif
endif endif
.vpx: vpx .libvpx: libvpx
(cd $<; ./configure --target=$(VPX_TARGET) --disable-install-bins --disable-install-srcs --disable-install-libs --disable-examples && XCC=$(CC) XNM=$(NM) XSTRIP=$(STRIP) make && make install) (cd $<; CROSS=$(HOST)- ./configure --target=$(VPX_TARGET) --disable-install-bins --disable-install-srcs --disable-install-libs --disable-examples && make && make install)
(cd $<; cp vp8/*.h vpx_codec/*.h vpx_ports/*.h $(PREFIX)/include) # Of course, why the hell would one expect it to be listed or in make install? (rm -rf $(PREFIX)/include/vpx/ && mkdir $(PREFIX)/include/vpx/; cd $< && cp vpx/*.h vpx_ports/*.h $(PREFIX)/include/vpx/) # Of course, why the hell would one expect it to be listed or in make install?
rm $(PREFIX)/include/config.h rm $(PREFIX)/include/vpx/config.h
(cd $<; $(RANLIB) libvpx.a && cp libvpx.a $(PREFIX)/lib) # Of course, why the hell would one expect it to be listed or in make install? (cd $<; $(RANLIB) libvpx.a && cp libvpx.a $(PREFIX)/lib) # Of course, why the hell would one expect it to be listed or in make install?
touch $@ touch $@
CLEAN_FILE += .vpx CLEAN_FILE += .libvpx
CLEAN_PKG += vpx CLEAN_PKG += libvpx
DISTCLEAN_PKG += libvpx-$(VPX_VERSION).tar.bz2 #DISTCLEAN_PKG += libvpx-$(VPX_VERSION).tar.bz2
# *************************************************************************** # ***************************************************************************
# lame # lame
...@@ -1068,7 +1068,6 @@ else ...@@ -1068,7 +1068,6 @@ else
ffmpeg: ffmpeg:
endif endif
$(SVN) co $(FFMPEG_SVN) ffmpeg $(SVN) co $(FFMPEG_SVN) ffmpeg
(cd ffmpeg; patch -p0 < ../Patches/ffmpeg-libvpx.patch;)
ifdef HAVE_ISA_THUMB ifdef HAVE_ISA_THUMB
patch -p0 < Patches/ffmpeg-avcodec-no-thumb.patch patch -p0 < Patches/ffmpeg-avcodec-no-thumb.patch
endif endif
...@@ -1117,10 +1116,10 @@ else ...@@ -1117,10 +1116,10 @@ else
ifdef HAVE_UCLIBC ifdef HAVE_UCLIBC
.ffmpeg: ffmpeg .ffmpeg: ffmpeg
else else
.ffmpeg: ffmpeg .lame .gsm .ffmpeg: ffmpeg .lame .gsm .libvpx
endif endif
endif endif
(cd $<; $(HOSTCC) ./configure --prefix=$(PREFIX) --extra-cflags="$(CFLAGS) $(FFMPEG_CFLAGS)" --extra-ldflags="$(LDFLAGS)" $(FFMPEGCONF) --disable-shared --enable-static && make && make install-libs install-headers) (cd $<; $(HOSTCC) ./configure --prefix=$(PREFIX) --extra-cflags="$(CFLAGS) $(FFMPEG_CFLAGS) -DHAVE_STDINT_H" --extra-ldflags="$(LDFLAGS)" $(FFMPEGCONF) --disable-shared --enable-static && make && make install-libs install-headers)
touch $@ touch $@
ifdef SVN ifdef SVN
...@@ -1323,7 +1322,7 @@ goom$(GOOM2k4_VERSION).tar.gz: ...@@ -1323,7 +1322,7 @@ goom$(GOOM2k4_VERSION).tar.gz:
goom: goom$(GOOM2k4_VERSION).tar.gz goom: goom$(GOOM2k4_VERSION).tar.gz
$(EXTRACT_GZ) $(EXTRACT_GZ)
ifdef HAVE_WIN32 ifdef HAVE_WIN32
(cd $@; dos2unix configure.in) (cd $@; fromdos configure.in)
patch -p0 < Patches/goom2k4-0-win32.patch patch -p0 < Patches/goom2k4-0-win32.patch
else else
patch -p0 < Patches/goom2k4-0-mmx.patch patch -p0 < Patches/goom2k4-0-mmx.patch
......
Index: configure
===================================================================
--- configure (revision 23210)
+++ configure (working copy)
@@ -183,6 +183,7 @@ External library support:
--enable-libtheora enable Theora encoding via libtheora [no]
--enable-libvorbis enable Vorbis encoding via libvorbis,
native implementation exists [no]
+ --enable-libvpx enable VP8 support via libvpx [no]
--enable-libx264 enable H.264 encoding via x264 [no]
--enable-libxvid enable Xvid encoding via xvidcore,
native MPEG-4/Xvid encoder exists [no]
@@ -927,6 +928,7 @@ CONFIG_LIST="
libspeex
libtheora
libvorbis
+ libvpx
libx264
libxvid
lpc
@@ -1349,6 +1351,8 @@ libschroedinger_encoder_deps="libschroed
libspeex_decoder_deps="libspeex"
libtheora_encoder_deps="libtheora"
libvorbis_encoder_deps="libvorbis"
+libvpx_decoder_deps="libvpx"
+libvpx_encoder_deps="libvpx"
libx264_encoder_deps="libx264"
libxvid_encoder_deps="libxvid"
@@ -2617,6 +2621,9 @@ enabled libschroedinger && add_cflags $(
enabled libspeex && require libspeex speex/speex.h speex_decoder_init -lspeex
enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg
enabled libvorbis && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbisenc -lvorbis -logg
+enabled libvpx && add_cflags -DHAVE_STDINT_H &&
+ require2 libvpx "vpx_decoder.h vp8dx.h" "vpx_codec_dec_init_ver" -lvpx &&
+ require2 libvpx "vpx_encoder.h vp8cx.h" "vpx_codec_enc_init_ver" -lvpx
enabled libx264 && require libx264 x264.h x264_encoder_encode -lx264 -lm &&
{ check_cpp_condition x264.h "X264_BUILD >= 90" ||
die "ERROR: libx264 version must be >= 0.90."; }
@@ -2886,6 +2893,7 @@ echo "libschroedinger enabled ${libsch
echo "libspeex enabled ${libspeex-no}"
echo "libtheora enabled ${libtheora-no}"
echo "libvorbis enabled ${libvorbis-no}"
+echo "libvpx enabled ${libvpx-no}"
echo "libx264 enabled ${libx264-no}"
echo "libxvid enabled ${libxvid-no}"
echo "zlib enabled ${zlib-no}"
Index: libavcodec/libvpxdec.c
===================================================================
--- libavcodec/libvpxdec.c (revision 0)
+++ libavcodec/libvpxdec.c (revision 0)
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010, Google, Inc.
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * VP8 decoder support via libvpx
+ */
+
+#ifndef HAVE_STDINT_H
+# define HAVE_STDINT_H 1
+#endif
+
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include <vpx_decoder.h>
+#include <vp8dx.h>
+
+#include "avcodec.h"
+
+typedef struct VP8DecoderContext {
+ vpx_codec_ctx_t decoder;
+} VP8Context;
+
+static av_cold int vp8_init(AVCodecContext *avctx)
+{
+ VP8Context *ctx = avctx->priv_data;
+ const struct vpx_codec_iface *iface = &vpx_codec_vp8_dx_algo;
+ struct vpx_codec_dec_cfg deccfg = {
+ /* token partitions+1 would be a decent choice */
+ .threads = FFMIN(avctx->thread_count, 16)
+ };
+
+ av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str());
+ av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config());
+
+ if (vpx_codec_dec_init(&ctx->decoder, iface, &deccfg, 0) != VPX_CODEC_OK) {
+ const char *error = vpx_codec_error(&ctx->decoder);
+ av_log(avctx, AV_LOG_ERROR, "Failed to initialize decoder: %s\n",
+ error);
+ return AVERROR(EINVAL);
+ }
+
+ avctx->pix_fmt = PIX_FMT_YUV420P;
+ return 0;
+}
+
+static int vp8_decode(AVCodecContext *avctx,
+ void *data, int *data_size, AVPacket *avpkt)
+{
+ VP8Context *ctx = avctx->priv_data;
+ AVFrame *picture = data;
+ const void *iter = NULL;
+ vpx_image_t *img;
+
+ /* end of stream, vp8 does no buffering */
+ if (!avpkt->size)
+ return 0;
+
+ if (vpx_codec_decode(&ctx->decoder, avpkt->data, avpkt->size, NULL, 0) !=
+ VPX_CODEC_OK) {
+ const char *error = vpx_codec_error(&ctx->decoder);
+ const char *detail = vpx_codec_error_detail(&ctx->decoder);
+
+ av_log(avctx, AV_LOG_ERROR, "Failed to decode frame: %s\n", error);
+ if (detail)
+ av_log(avctx, AV_LOG_ERROR, " Additional information: %s\n",
+ detail);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((img = vpx_codec_get_frame(&ctx->decoder, &iter))) {
+ if (img->fmt != IMG_FMT_I420) {
+ av_log(avctx, AV_LOG_ERROR, "Unsupported output colorspace (%d)\n",
+ img->fmt);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((int) img->d_w != avctx->width || (int) img->d_h != avctx->height) {
+ av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
+ avctx->width, avctx->height, img->d_w, img->d_h);
+ if (avcodec_check_dimensions(avctx, img->d_w, img->d_h))
+ return AVERROR_INVALIDDATA;
+ avcodec_set_dimensions(avctx, img->d_w, img->d_h);
+ }
+ picture->data[0] = img->planes[0];
+ picture->data[1] = img->planes[1];
+ picture->data[2] = img->planes[2];
+ picture->data[3] = NULL;
+ picture->linesize[0] = img->stride[0];
+ picture->linesize[1] = img->stride[1];
+ picture->linesize[2] = img->stride[2];
+ picture->linesize[3] = 0;
+ *data_size = sizeof(AVPicture);
+ }
+ return avpkt->size;
+}
+
+static av_cold int vp8_free(AVCodecContext *avctx)
+{
+ VP8Context *ctx = avctx->priv_data;
+ vpx_codec_destroy(&ctx->decoder);
+ return 0;
+}
+
+AVCodec libvpx_decoder = {
+ "libvpx",
+ AVMEDIA_TYPE_VIDEO,
+ CODEC_ID_VP8,
+ sizeof(VP8Context),
+ vp8_init,
+ NULL, /* encode */
+ vp8_free,
+ vp8_decode,
+ 0, /* capabilities */
+ .long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"),
+};
Index: libavcodec/options.c
===================================================================
--- libavcodec/options.c (revision 23210)
+++ libavcodec/options.c (working copy)
@@ -414,6 +414,19 @@ static const AVOption options[]={
{"intra_refresh", "use periodic insertion of intra blocks instead of keyframes", 0, FF_OPT_TYPE_CONST, CODEC_FLAG2_INTRA_REFRESH, INT_MIN, INT_MAX, V|E, "flags2"},
{"crf_max", "in crf mode, prevents vbv from lowering quality beyond this point", OFFSET(crf_max), FF_OPT_TYPE_FLOAT, DEFAULT, 0, 51, V|E},
{"log_level_offset", "set the log level offset", OFFSET(log_level_offset), FF_OPT_TYPE_INT, 0, INT_MIN, INT_MAX },
+{"spatial_rsmpl", "Enable spatial resampling (VP8)", OFFSET(spatial_rsmpl), FF_OPT_TYPE_INT, 0, 0, 1, V|E},
+{"spatial_rsmpl_up", "Spatial resampling up watermark, percentage of target data buffer. (VP8)", OFFSET(spatial_rsmpl_up), FF_OPT_TYPE_INT, 60, 0, 100, V|E},
+{"spatial_rsmpl_down", "Spatial resampling down watermark, percentage of target data buffer. (VP8)", OFFSET(spatial_rsmpl_down), FF_OPT_TYPE_INT, 30, 0, 100, V|E},
+{"vbr_bias", "Two-pass mode CBR/VBR bias, 0-100 (VP8)", OFFSET(vbr_bias), FF_OPT_TYPE_INT, 50, 0, 100, V|E},
+{"lag", "Allow lagged encoding, given as frames (VP8)", OFFSET(lag), FF_OPT_TYPE_INT, 0, 0, INT_MAX, V|E},
+{"sharpness", "[0-7] (VP8)", OFFSET(sharpness), FF_OPT_TYPE_INT, 0, 0, 7, V|E},
+{"altref", "Allow use of alternate reference frame", OFFSET(altref), FF_OPT_TYPE_INT, 0, 0, 1, V|E},
+{"ar_max_frames", "Max frames used in creating alt. ref. [0,25]", OFFSET(ar_max_frames), FF_OPT_TYPE_INT, 0, 0, 25, V|E},
+{"ar_type", "Filter type used in creating alt. ref.", OFFSET(ar_type), FF_OPT_TYPE_INT, 0, 0, INT_MAX, V|E},
+{"ar_strength", "Filter strength used in creating alt. ref. [0,6]", OFFSET(ar_strength), FF_OPT_TYPE_INT, 0, 0, 6, V|E},
+{"mb_static_threshold", "", OFFSET(mb_static_threshold), FF_OPT_TYPE_INT, 800, 0, INT_MAX, V|E},
+{"rc_opt_occupancy", "number of bits which should be kept in the rc buffer during decoding", OFFSET(rc_optimal_buffer_occupancy), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX, V|E},
+{"token_partitions", "Number of sub-streams in bitstream (1,2,4,8). Used for parallelized decoding.", OFFSET(token_partitions), FF_OPT_TYPE_INT, 1, 1, INT_MAX, V|E},
{NULL},
};
Index: libavcodec/allcodecs.c
===================================================================
--- libavcodec/allcodecs.c (revision 23210)
+++ libavcodec/allcodecs.c (working copy)
@@ -349,6 +349,7 @@ void avcodec_register_all(void)
REGISTER_DECODER (LIBSPEEX, libspeex);
REGISTER_ENCODER (LIBTHEORA, libtheora);
REGISTER_ENCODER (LIBVORBIS, libvorbis);
+ REGISTER_ENCDEC (LIBVPX, libvpx);
REGISTER_ENCODER (LIBX264, libx264);
REGISTER_ENCODER (LIBXVID, libxvid);
Index: libavcodec/avcodec.h
===================================================================
--- libavcodec/avcodec.h (revision 23210)
+++ libavcodec/avcodec.h (working copy)
@@ -30,7 +30,7 @@
#include "libavutil/avutil.h"
#define LIBAVCODEC_VERSION_MAJOR 52
-#define LIBAVCODEC_VERSION_MINOR 68
+#define LIBAVCODEC_VERSION_MINOR 69
#define LIBAVCODEC_VERSION_MICRO 3
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
@@ -2659,6 +2659,160 @@ typedef struct AVCodecContext {
float crf_max;
int log_level_offset;
+
+ /**
+ * Enable spatial resampling
+ *
+ * Spatial resampling allows the codec to compress a lower resolution
+ * version of the frame, which is then upscaled by the encoder to the
+ * correct presentation resolution. This increases visual quality at low
+ * data rates, at the expense of CPU time on the encoder/decoder.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int spatial_rsmpl;
+
+ /**
+ * Spatial resampling up watermark.
+ *
+ * This threshold is described as a percentage of the target data buffer.
+ * When the data buffer rises above this percentage of fullness, the
+ * encoder will step up to a higher resolution version of the frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int spatial_rsmpl_up;
+
+ /**
+ * Spatial resampling down watermark.
+ *
+ * This threshold is described as a percentage of the target data buffer.
+ * When the data buffer falls below this percentage of fullness, the
+ * encoder will step down to a lower resolution version of the frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int spatial_rsmpl_down;
+
+ /**
+ * Two-pass mode CBR/VBR bias.
+ *
+ * Bias, expressed on a scale of 0 to 100, for determining target size for
+ * the current frame. The value 0 indicates the optimal CBR mode value
+ * should be used. The value 100 indicates the optimal VBR mode value
+ * should be used. Values in between indicate which way the encoder should
+ * "lean." RC mode bias between CBR and VBR(0-100: 0->CBR, 100->VBR)
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int vbr_bias;
+
+ /**
+ * Allow lagged encoding.
+ *
+ * If set, this value allows the encoder to consume a number of input
+ * frames before producing output frames. This allows the encoder to base
+ * decisions for the current frame on future frames. This does increase the
+ * latency of the encoding pipeline, so it is not appropriate in all
+ * situations (ex: realtime encoding). Half the output average output
+ * framerate is a reasonable default in other cases.
+ *
+ * Note that this is a maximum value -- the encoder may produce frames
+ * sooner than the given limit. Set this value to 0 to disable this
+ * feature.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int lag;
+
+ /**
+ * Control sharpness preprocessing
+ *
+ * This setting does not impact any other setting and is largely a matter
+ * of personal preference. A low sharpness setting will result in fewer
+ * visible artifacts but may blur the image somewhat; a high sharpness will
+ * result in a sharper image but may result in more visible artifacts.
+ * Valid Range: [0,7]
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int sharpness;
+
+ /**
+ * Allow encoder to automatically set and use alternate reference frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int altref;
+
+ /**
+ * Set the max number of frames blurred creating the alternate reference frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int ar_max_frames;
+
+ /**
+ * Set the type of filter to use for the alternate reference frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int ar_type;
+
+ /**
+ * Set the filter strength for the alternate reference frame.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int ar_strength;
+
+ /**
+ * Set the threshold for macroblocks treated as static.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int mb_static_threshold;
+
+ /**
+ * Number of bits which should be maintained in rc buffer during decoding.
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int rc_optimal_buffer_occupancy;
+
+ /**
+ * Number of token partitions.
+ * Valid Values: {1,2,4,8}
+ * - encoding: Set by user.
+ * - decoding: unused
+ *
+ * \attention VP8 specific
+ */
+ int token_partitions;
} AVCodecContext;
/**
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile (revision 23210)
+++ libavcodec/Makefile (working copy)
@@ -512,6 +512,7 @@ OBJS-$(CONFIG_OGG_DEMUXER) +
dirac.o mpeg12data.o
OBJS-$(CONFIG_OGG_MUXER) += xiph.o flacdec.o flacdata.o flac.o
OBJS-$(CONFIG_RTP_MUXER) += mpegvideo.o
+OBJS-$(CONFIG_WEBM_MUXER) += xiph.o
# external codec libraries
OBJS-$(CONFIG_LIBDIRAC_DECODER) += libdiracdec.o
@@ -536,6 +537,8 @@ OBJS-$(CONFIG_LIBSCHROEDINGER_ENCODER)
OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o
OBJS-$(CONFIG_LIBTHEORA_ENCODER) += libtheoraenc.o
OBJS-$(CONFIG_LIBVORBIS_ENCODER) += libvorbis.o
+OBJS-$(CONFIG_LIBVPX_DECODER) += libvpxdec.o
+OBJS-$(CONFIG_LIBVPX_ENCODER) += libvpxenc.o
OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o
OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvidff.o libxvid_rc.o
Index: libavcodec/libvpxenc.c
===================================================================
--- libavcodec/libvpxenc.c (revision 0)
+++ libavcodec/libvpxenc.c (revision 0)
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2010, Google, Inc.
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * VP8 encoder support via libvpx
+ *
+ * @par VPx SDK param./#AVCodecContext param./Assoc. cli opt {{{
+ * <table>
+ * <tr><td>g_threads</td><td>AVCodecContext::thread_count</td><td>-threads</td></tr>
+ * <tr><td>g_lag_in_frames</td><td>AVCodecContext::lag</td><td>-lag</td></tr>
+ * <tr><td>rc_target_bitrate</td><td>AVCodecContext::bit_rate/1000</td><td>-vb</td></tr>
+ * <tr><td>kf_max_dist</td><td>AVCodecContext::gop_size</td><td>-g</td></tr>
+ * <tr><td>kf_min_dist</td><td>AVCodecContext::keyint_min</td><td>-keyint_min</td></tr>
+ * <tr><td><code>VPX_KF_FIXED</code></td><td>AVCodecContext::keyint_min == AVCodecContext::gop_size</td><td>-keyint_min -g</td></tr>
+ * <tr><td>rc_min_quantizer</td><td>((AVCodecContext::qmin*5+1)&gt;&gt;2) - 1</td><td>-qmin</td></tr>
+ * <tr><td>rc_max_quantizer</td><td>((AVCodecContext::qmax*5+1)&gt;&gt;2) - 1</td><td>-qmax</td></tr>
+ * <tr><td>rc_buf_sz</td><td>AVCodecContext::rc_buffer_size*1000 / AVCodecContext::bit_rate</td><td>-bufsize -vb</td></tr>
+ * <tr><td>rc_buf_initial_sz</td><td>AVCodecContext::rc_initial_buffer_occupancy*1000 / AVCodecContext::bit_rate</td><td>-rc_init_occupancy -vb</td></tr>
+ * <tr><td>rc_buf_optimal_sz</td><td>AVCodecContext::rc_optimal_buffer_occupancy*1000 / AVCodecContext::bit_rate</td><td>-rc_opt_occupancy -vb</td></tr>
+ * <tr><td>rc_dropframe_thresh</td><td>AVCodecContext::frame_skip_threshold</td><td>-skip_threshold</td></tr>
+ * <tr><td>rc_undershoot_pct</td><td>AVCodecContext::rc_buffer_aggressivity*100</td><td>-rc_buf_aggressivity</td></tr>
+ * <tr><td>rc_overshoot_pct</td><td>AVCodecContext::bit_rate_tolerance*100 / AVCodecContext::bit_rate</td><td>-bt -vb</td></tr>
+ * <tr><td>rc_resize_allowed</td><td>AVCodecContext::spatial_rsmpl</td><td>-spatial_rsmpl</td></tr>
+ * <tr><td>rc_resize_up_thresh</td><td>AVCodecContext::spatial_rsmpl_up</td><td>-spatial_rsmpl_up</td></tr>
+ * <tr><td>rc_resize_down_thresh</td><td>AVCodecContext::spatial_rsmpl_down</td><td>-spatial_rsmpl_down</td></tr>
+ * <tr><td>rc_2pass_vbr_bias_pct</td><td>AVCodecContext::vbr_bias</td><td>-vbr_bias</td></tr>
+ * <tr><td>rc_2pass_vbr_maxsection_pct</td><td>AVCodecContext::rc_max_rate*100 / AVCodecContext::bit_rate</td><td>-maxrate -vb</td></tr>
+ * <tr><td>rc_2pass_vbr_minsection_pct</td><td>AVCodecContext::rc_min_rate*100 / AVCodecContext::bit_rate</td><td>-minrate -vb</td></tr>
+ * <tr><td>VPX_CBR</td><td>AVCodecContext::rc_min_rate == AVCodecContext::rc_max_rate &amp;&amp; AVCodecContext::rc_min_rate == AVCodecContext::bit_rate</td><td>-minrate -maxrate -vb</td></tr>
+ * <tr><td>g_profile</td><td>AVCodecContext::profile</td><td>-profile</td></tr>
+ * <tr><td>g_error_resilient</td><td>AVCodecContext::level&lt;0</td><td>-level</td></tr>
+ * <tr><td><code>VPX_DL_BEST_QUALITY(1)/VPX_DL_GOOD_QUALITY(2)/VPX_DL_REALTIME(3)</code></td><td>#FFABS(AVCodecContext::level)/100</td><td>-level</td></tr>
+ * <tr><td><code>VP8E_SET_CPUUSED</code></td><td>#FFABS(AVCodecContext::level)%%100-16</td><td>-level</td></tr>
+ * <tr><td><code>VP8E_SET_NOISE_SENSITIVITY</code></td><td>AVCodecContext::noise_reduction</td><td>-nr</td></tr>
+ * <tr><td><code>VP8E_SET_SHARPNESS</code></td><td>AVCodecContext::sharpness</td><td>-sharpness</td></tr>
+ * <tr><td><code>VP8E_SET_ENABLEAUTOALTREF</code></td><td>AVCodecContext::altref</td><td>-altref</td></tr>
+ * <tr><td><code>VP8E_SET_ARNR_MAXFRAMES</code></td><td>AVCodecContext::ar_max_frames</td><td>-ar_max_frames</td></tr>
+ * <tr><td><code>VP8E_SET_ARNR_TYPE</code></td><td>AVCodecContext::ar_type</td><td>-ar_type</td></tr>
+ * <tr><td><code>VP8E_SET_ARNR_STRENGTH</code></td><td>AVCodecContext::ar_strength</td><td>-ar_strength</td></tr>
+ * <tr><td><code>VP8E_SET_STATIC_THRESHOLD</code></td><td>AVCodecContext::mb_static_threshold</td><td>-mb_static_threshold</td></tr>
+ * <tr><td><code>VP8E_SET_TOKEN_PARTITIONS</code></td><td>AVCodecContext::token_partitions</td><td>-token_partitions</td></tr>
+ * </table> }}}
+ */
+
+#include "avcodec.h"
+#include "libavutil/base64.h"
+
+#ifndef HAVE_STDINT_H
+# define HAVE_STDINT_H 1
+#endif
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include <vpx_encoder.h>
+#include <vp8cx.h>
+
+/**
+ * Portion of #vpx_codec_cx_pkt_t from vpx_encoder.h.
+ * One encoded frame returned from the library.
+ */
+struct FrameListData {
+ void *buf; /**< compressed data buffer */
+ size_t sz; /**< length of compressed data */
+ vpx_codec_pts_t pts; /**< time stamp to show frame
+ (in timebase units) */
+ unsigned long duration; /**< duration to show frame
+ (in timebase units) */
+ vpx_codec_frame_flags_t flags; /**< flags for this frame */
+ struct FrameListData *next;
+};
+
+typedef struct VP8EncoderContext {
+ vpx_codec_ctx_t encoder;
+ vpx_image_t rawimg;
+ vpx_fixed_buf_t twopass_stats;
+ unsigned long deadline; //i.e., RT/GOOD/BEST
+ struct FrameListData* coded_frame_list;
+} VP8Context;
+
+static int vp8_free(AVCodecContext *avctx);
+static void free_frame_list(struct FrameListData* list);
+static void free_coded_frame(struct FrameListData* cx_frame);
+static void coded_frame_add(void* list, struct FrameListData* cx_frame);
+static void log_encoder_error(AVCodecContext *avctx, const char* desc);
+static void dump_enc_cfg(AVCodecContext *avctx, const vpx_codec_enc_cfg_t* cfg);
+
+static av_cold int vp8_init(AVCodecContext *avctx)
+{
+ VP8Context* const ctx = avctx->priv_data;
+ vpx_codec_iface_t* const iface = &vpx_codec_vp8_cx_algo;
+ const int log_field_width = -30;
+ const unsigned int usage = 0; //should be 0 for current VP8
+ const vpx_codec_flags_t flags = 0;
+ int cpuused = 3;
+ vpx_codec_enc_cfg_t enccfg;
+ vpx_codec_err_t res;
+
+ av_log(avctx,AV_LOG_INFO,"%s\n",vpx_codec_version_str());
+ av_log(avctx,AV_LOG_VERBOSE,"%s\n",vpx_codec_build_config());
+
+ if((res= vpx_codec_enc_config_default(iface,&enccfg,usage))!=VPX_CODEC_OK) {
+ av_log(avctx,AV_LOG_ERROR,"Failed to get config: %s\n",vpx_codec_err_to_string(res));
+ return -1;
+ }
+ dump_enc_cfg(avctx,&enccfg);
+
+ enccfg.g_w = avctx->width;
+ enccfg.g_h = avctx->height;
+
+ /*With altref set an additional frame at the same pts may be produced.
+ Increasing the time_base gives the library a window to place these frames
+ ensuring strictly increasing timestamps.*/
+ if(avctx->altref) {
+ avctx->ticks_per_frame = 2;
+ avctx->time_base = av_mul_q(avctx->time_base,(AVRational){1,avctx->ticks_per_frame});
+ }
+ enccfg.g_timebase.num = avctx->time_base.num;
+ enccfg.g_timebase.den = avctx->time_base.den;
+
+ enccfg.g_threads = FFMIN(avctx->thread_count,64);
+ if(avctx->flags&CODEC_FLAG_PASS1) enccfg.g_pass= VPX_RC_FIRST_PASS;
+ else if(avctx->flags&CODEC_FLAG_PASS2) enccfg.g_pass= VPX_RC_LAST_PASS;
+ else enccfg.g_pass= VPX_RC_ONE_PASS;
+
+ enccfg.rc_resize_allowed = avctx->spatial_rsmpl;
+ enccfg.rc_resize_up_thresh = avctx->spatial_rsmpl_up;
+ enccfg.rc_resize_down_thresh = avctx->spatial_rsmpl_down;
+ enccfg.rc_dropframe_thresh = avctx->frame_skip_threshold;
+
+ if(avctx->rc_min_rate==avctx->rc_max_rate && avctx->rc_min_rate==avctx->bit_rate)
+ enccfg.rc_end_usage = VPX_CBR;
+ enccfg.rc_target_bitrate = (unsigned int)av_rescale_rnd(avctx->bit_rate,1,1000,AV_ROUND_NEAR_INF);
+ enccfg.rc_overshoot_pct = avctx->bit_rate_tolerance*100/avctx->bit_rate;
+
+ //convert [1,51] -> [0,63]
+ enccfg.rc_min_quantizer = ((avctx->qmin*5+1)>>2) - 1;
+ enccfg.rc_max_quantizer = ((avctx->qmax*5+1)>>2) - 1;
+ if(avctx->rc_buffer_size)
+ enccfg.rc_buf_sz = avctx->rc_buffer_size*1000/avctx->bit_rate;
+ if(avctx->rc_initial_buffer_occupancy)
+ enccfg.rc_buf_initial_sz = avctx->rc_initial_buffer_occupancy*1000/avctx->bit_rate;
+ if(avctx->rc_optimal_buffer_occupancy)
+ enccfg.rc_buf_optimal_sz = avctx->rc_optimal_buffer_occupancy*1000/avctx->bit_rate;
+ if(avctx->rc_buffer_aggressivity)
+ enccfg.rc_undershoot_pct = (unsigned int)round(avctx->rc_buffer_aggressivity*100);
+
+ enccfg.rc_2pass_vbr_bias_pct= avctx->vbr_bias;
+ if(avctx->rc_min_rate)
+ enccfg.rc_2pass_vbr_minsection_pct = avctx->rc_min_rate*100/avctx->bit_rate;
+ if(avctx->rc_max_rate)
+ enccfg.rc_2pass_vbr_maxsection_pct = avctx->rc_max_rate*100/avctx->bit_rate;
+
+ if(avctx->keyint_min==avctx->gop_size) enccfg.kf_mode= VPX_KF_FIXED;
+ //_enc_init() will balk if kf_min_dist is set in this case
+ if(enccfg.kf_mode!=VPX_KF_AUTO) enccfg.kf_min_dist= avctx->keyint_min;
+ enccfg.kf_max_dist = avctx->gop_size;
+ enccfg.g_lag_in_frames= avctx->lag;
+
+ if(enccfg.g_pass==VPX_RC_FIRST_PASS) enccfg.g_lag_in_frames= 0;
+ if(enccfg.g_pass==VPX_RC_LAST_PASS) {
+ int decode_size;
+
+ if(!avctx->stats_in) {
+ av_log(avctx,AV_LOG_ERROR,"No stats file for second pass\n");
+ return -1;
+ }
+
+ ctx->twopass_stats.sz = strlen(avctx->stats_in) * 3/4;
+ ctx->twopass_stats.buf = av_malloc(ctx->twopass_stats.sz);
+ if(!ctx->twopass_stats.buf) {
+ av_log(avctx,AV_LOG_ERROR,"Stat buffer alloc (%zu bytes) failed\n",ctx->twopass_stats.sz);
+ return AVERROR(ENOMEM);
+ }
+ decode_size =
+ av_base64_decode(ctx->twopass_stats.buf, avctx->stats_in, ctx->twopass_stats.sz);
+ if(decode_size<0) {
+ av_log(avctx,AV_LOG_ERROR,"Stat buffer decode failed\n");
+ return -1;
+ }
+
+ ctx->twopass_stats.sz = decode_size;
+ enccfg.rc_twopass_stats_in = ctx->twopass_stats;
+ }
+
+ if(avctx->profile!=FF_PROFILE_UNKNOWN) enccfg.g_profile= avctx->profile;
+ switch(FFABS(avctx->level)/100) {
+ case 1: ctx->deadline = VPX_DL_BEST_QUALITY; break;
+ case 2: default: ctx->deadline = VPX_DL_GOOD_QUALITY; break;
+ case 3: ctx->deadline = VPX_DL_REALTIME; break;
+ }
+ av_log(avctx,AV_LOG_DEBUG,"Using deadline: %lu\n",ctx->deadline);
+
+ if(avctx->level!=FF_LEVEL_UNKNOWN) {
+ enccfg.g_error_resilient = avctx->level<0;
+ cpuused = FFABS(avctx->level)%100-16;
+ }
+
+ dump_enc_cfg(avctx,&enccfg);
+ /* Construct Encoder Context */
+ res = vpx_codec_enc_init(&ctx->encoder,iface,&enccfg,flags);
+ if(res!=VPX_CODEC_OK) { log_encoder_error(avctx,"Failed to initialize encoder"); return -1; }
+
+ //codec control failures are currently treated only as warnings
+ av_log(avctx,AV_LOG_DEBUG,"vpx_codec_control\n");
+ #define codecctl(id,val)\
+ do if(av_log(avctx,AV_LOG_DEBUG,"%*s%d\n",log_field_width," "#id":",val),\
+ (res= vpx_codec_control(&ctx->encoder,id,val))!=VPX_CODEC_OK) {\
+ log_encoder_error(avctx,"Failed to set "#id" codec control");\
+ } while(0);
+ codecctl(VP8E_SET_CPUUSED,cpuused);
+ codecctl(VP8E_SET_NOISE_SENSITIVITY,avctx->noise_reduction);
+ codecctl(VP8E_SET_SHARPNESS,avctx->sharpness);
+ codecctl(VP8E_SET_ENABLEAUTOALTREF,avctx->altref);
+ codecctl(VP8E_SET_ARNR_MAXFRAMES,avctx->ar_max_frames);
+ codecctl(VP8E_SET_ARNR_TYPE,avctx->ar_type);
+ codecctl(VP8E_SET_ARNR_STRENGTH,avctx->ar_strength);
+ codecctl(VP8E_SET_STATIC_THRESHOLD,avctx->mb_static_threshold);
+ codecctl(VP8E_SET_TOKEN_PARTITIONS,av_log2(avctx->token_partitions));
+ #undef codecctl
+
+ //provide dummy value to initialize wrapper, values will be updated each _encode()
+ vpx_img_wrap(&ctx->rawimg,IMG_FMT_I420,avctx->width,avctx->height,1,(unsigned char*)1);
+
+ avctx->coded_frame = avcodec_alloc_frame();
+ if(!avctx->coded_frame) {
+ av_log(avctx,AV_LOG_ERROR,"Error allocating coded frame\n");
+ vp8_free(avctx);
+ return AVERROR(ENOMEM);
+ }
+ return 0;
+}
+
+static inline void cx_pktcpy(struct FrameListData* dst, const vpx_codec_cx_pkt_t* src)
+{
+ dst->pts = src->data.frame.pts;
+ dst->duration = src->data.frame.duration;
+ dst->flags = src->data.frame.flags;
+ dst->sz = src->data.frame.sz;
+ dst->buf = src->data.frame.buf;
+}
+
+/**
+ * Store coded frame information in format suitable for return from encode().
+ *
+ * Write buffer information from @a cx_frame to @a buf & @a buf_size.
+ * Timing/frame details to @a coded_frame.
+ * @return Frame size written to @a buf on success
+ * @return -1 on error
+ */
+static int storeframe(AVCodecContext *avctx, struct FrameListData* cx_frame,
+ uint8_t* buf, int buf_size, AVFrame* coded_frame)
+{
+ if((int)cx_frame->sz<=buf_size) {
+ buf_size = cx_frame->sz;
+ memcpy(buf,cx_frame->buf,buf_size);
+ coded_frame->pts = cx_frame->pts;
+ coded_frame->key_frame = !!(cx_frame->flags&VPX_FRAME_IS_KEY);
+
+ if(coded_frame->key_frame)
+ coded_frame->pict_type = FF_I_TYPE;
+ else
+ coded_frame->pict_type = FF_P_TYPE;
+ } else {
+ av_log(avctx,AV_LOG_ERROR,
+ "Compressed frame larger than storage provided! (%zu/%d)\n",
+ cx_frame->sz,buf_size);
+ return -1;
+ }
+ return buf_size;
+}
+
+/**
+ * Queue multiple output frames from the encoder, returning the front-most.
+ * In cases where vpx_codec_get_cx_data() returns more than 1 frame append
+ * the frame queue. Return the head frame if available.
+ * @return Stored frame size
+ * @return -1 on error
+ */
+static int queue_frames(AVCodecContext *avctx, uint8_t* buf, int buf_size, AVFrame* coded_frame)
+{
+ VP8Context* const ctx = avctx->priv_data;
+ const vpx_codec_cx_pkt_t* pkt;
+ vpx_codec_iter_t iter = NULL;
+ int size = 0;
+
+ if(ctx->coded_frame_list) {
+ struct FrameListData* const cx_frame = ctx->coded_frame_list;
+ /*return the leading frame if we've already begun queueing*/
+ size = storeframe(avctx,cx_frame,buf,buf_size,coded_frame);
+ if(size<0)
+ return -1;
+ ctx->coded_frame_list = cx_frame->next;
+ free_coded_frame(cx_frame);
+ }
+
+ /*consume all available output from the encoder before returning. buffers
+ are only good through the next vpx_codec call*/
+ while( (pkt= vpx_codec_get_cx_data(&ctx->encoder,&iter)) ) {
+ switch(pkt->kind) {
+ case VPX_CODEC_CX_FRAME_PKT: {
+ if(!size) {
+ struct FrameListData cx_frame;
+
+ /*avoid storing the frame when the list is empty and we haven't yet
+ provided a frame for output*/
+ assert(!ctx->coded_frame_list);
+ cx_pktcpy(&cx_frame,pkt);
+ size = storeframe(avctx,&cx_frame,buf,buf_size,coded_frame);
+ if(size<0)
+ return -1;
+ } else {
+ struct FrameListData* const cx_frame = av_malloc(sizeof(struct FrameListData));
+
+ if(!cx_frame) {
+ av_log(avctx,AV_LOG_ERROR,"Frame queue element alloc failed\n");
+ return AVERROR(ENOMEM);
+ }
+ cx_pktcpy(cx_frame,pkt);
+ cx_frame->buf = av_malloc(cx_frame->sz);
+
+ if(!cx_frame->buf) {
+ av_log(avctx,AV_LOG_ERROR,"Data buffer alloc (%zu bytes) failed\n",
+ cx_frame->sz);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(cx_frame->buf,pkt->data.frame.buf,pkt->data.frame.sz);
+ coded_frame_add(&ctx->coded_frame_list,cx_frame);
+ }
+ break; }
+ case VPX_CODEC_STATS_PKT: {
+ vpx_fixed_buf_t* const stats = &ctx->twopass_stats;
+ stats->buf = av_realloc(stats->buf,stats->sz+pkt->data.twopass_stats.sz);
+ if(!stats->buf) {
+ av_log(avctx,AV_LOG_ERROR,"Stat buffer realloc failed\n");
+ return AVERROR(ENOMEM);
+ }
+ memcpy((uint8_t*)stats->buf + stats->sz,
+ pkt->data.twopass_stats.buf,
+ pkt->data.twopass_stats.sz);
+ stats->sz += pkt->data.twopass_stats.sz;
+ break; }
+ case VPX_CODEC_CUSTOM_PKT:
+ //ignore unsupported/unrecognized packet types
+ break;
+ }
+ }
+
+ return size;
+}
+
+static int vp8_encode(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data)
+{
+ VP8Context* const ctx = avctx->priv_data;
+ AVFrame* const frame = data;
+ vpx_image_t* rawimg = NULL;
+ int coded_size;
+
+ if(frame) {
+ rawimg = &ctx->rawimg;
+ rawimg->planes[PLANE_Y] = frame->data[0];
+ rawimg->planes[PLANE_U] = frame->data[1];
+ rawimg->planes[PLANE_V] = frame->data[2];
+ rawimg->stride[PLANE_Y] = frame->linesize[0];
+ rawimg->stride[PLANE_U] = frame->linesize[1];
+ rawimg->stride[PLANE_V] = frame->linesize[2];
+ }
+
+ { vpx_codec_err_t res;
+ const vpx_codec_pts_t timestamp = frame?frame->pts:0;
+ const unsigned long framedur = avctx->ticks_per_frame;
+ const vpx_enc_frame_flags_t flags = 0; //VPX_EFLAG_FORCE_KF
+ const unsigned long deadline = ctx->deadline;
+
+ //FIXME use vpx_codec_set_cx_data_buf?
+ res = vpx_codec_encode(&ctx->encoder,rawimg,timestamp,framedur,flags,deadline);
+ if(res!=VPX_CODEC_OK) { log_encoder_error(avctx,"Error encoding frame"); return -1; } }
+ coded_size = queue_frames(avctx,buf,buf_size,avctx->coded_frame);
+
+ if(!frame && avctx->flags&CODEC_FLAG_PASS1) {
+ const unsigned int b64_size = ((ctx->twopass_stats.sz + 2) / 3) * 4 + 1;
+
+ avctx->stats_out = av_malloc(b64_size);
+ if(!avctx->stats_out) {
+ av_log(avctx,AV_LOG_ERROR,"Stat buffer alloc (%d bytes) failed\n",b64_size);
+ return AVERROR(ENOMEM);
+ }
+ av_base64_encode(avctx->stats_out, b64_size, ctx->twopass_stats.buf, ctx->twopass_stats.sz);
+ }
+ return coded_size;
+}
+
+static av_cold int vp8_free(AVCodecContext *avctx)
+{
+ VP8Context* const ctx = avctx->priv_data;
+
+ vpx_codec_destroy(&ctx->encoder);
+ av_freep(&ctx->twopass_stats.buf);
+ av_freep(&avctx->coded_frame);
+ av_freep(&avctx->stats_out);
+ free_frame_list(ctx->coded_frame_list);
+ return 0;
+}
+
+static av_cold void free_coded_frame(struct FrameListData* cx_frame)
+{
+ av_freep(&cx_frame->buf);
+ av_freep(&cx_frame);
+}
+
+static av_cold void free_frame_list(struct FrameListData* list)
+{
+ struct FrameListData* p = list;
+
+ while(p) {
+ list = list->next;
+ free_coded_frame(p);
+ p = list;
+ }
+}
+
+static void coded_frame_add(void* list, struct FrameListData* cx_frame)
+{
+ struct FrameListData** p = list;
+
+ while(*p!=NULL) p= &(*p)->next;
+ *p = cx_frame;
+ cx_frame->next = NULL;
+}
+
+static av_cold void log_encoder_error(AVCodecContext *avctx, const char* desc)
+{
+ VP8Context* const ctx = avctx->priv_data;
+ const char* error = vpx_codec_error(&ctx->encoder);
+ const char* detail = vpx_codec_error_detail(&ctx->encoder);
+
+ av_log(avctx,AV_LOG_ERROR,"%s: %s\n",desc,error);
+ if(detail) av_log(avctx,AV_LOG_ERROR," Additional information: %s\n",detail);
+}
+
+static av_cold void dump_enc_cfg(AVCodecContext *avctx, const vpx_codec_enc_cfg_t* cfg)
+{
+ const int width = -30;
+ const int level = AV_LOG_DEBUG;
+
+ av_log(avctx,level,"vpx_codec_enc_cfg\n");
+ av_log(avctx,level," %*s%u\n", width, " g_usage:", cfg->g_usage);
+ av_log(avctx,level," %*s%u\n", width, " g_threads:", cfg->g_threads);
+ av_log(avctx,level," %*s%u\n", width, " g_profile:", cfg->g_profile);
+ av_log(avctx,level," %*s%u\n", width, " g_w:", cfg->g_w);
+ av_log(avctx,level," %*s%u\n", width, " g_h:", cfg->g_h);
+ av_log(avctx,level," %*s{%u/%u}\n", width, " g_timebase:", cfg->g_timebase.num, cfg->g_timebase.den);
+ av_log(avctx,level," %*s%u\n", width, " g_error_resilient:", cfg->g_error_resilient);
+ av_log(avctx,level," %*s%d\n", width, " g_pass:", cfg->g_pass);
+ av_log(avctx,level," %*s%u\n", width, " g_lag_in_frames:", cfg->g_lag_in_frames);
+ av_log(avctx,level," %*s%u\n", width, " rc_dropframe_thresh:", cfg->rc_dropframe_thresh);
+ av_log(avctx,level," %*s%u\n", width, " rc_resize_allowed:", cfg->rc_resize_allowed);
+ av_log(avctx,level," %*s%u\n", width, " rc_resize_up_thresh:", cfg->rc_resize_up_thresh);
+ av_log(avctx,level," %*s%u\n", width, " rc_resize_down_thresh:", cfg->rc_resize_down_thresh);
+ av_log(avctx,level," %*s%d\n", width, " rc_end_usage:", cfg->rc_end_usage);
+ av_log(avctx,level," %*s%p(%zu)\n", width, " rc_twopass_stats_in:", cfg->rc_twopass_stats_in.buf, cfg->rc_twopass_stats_in.sz);
+ av_log(avctx,level," %*s%u\n", width, " rc_target_bitrate:", cfg->rc_target_bitrate);
+ av_log(avctx,level," %*s%u\n", width, " rc_min_quantizer:", cfg->rc_min_quantizer);
+ av_log(avctx,level," %*s%u\n", width, " rc_max_quantizer:", cfg->rc_max_quantizer);
+ av_log(avctx,level," %*s%u\n", width, " rc_undershoot_pct:", cfg->rc_undershoot_pct);
+ av_log(avctx,level," %*s%u\n", width, " rc_overshoot_pct:", cfg->rc_overshoot_pct);
+ av_log(avctx,level," %*s%u\n", width, " rc_buf_sz:", cfg->rc_buf_sz);
+ av_log(avctx,level," %*s%u\n", width, " rc_buf_initial_sz:", cfg->rc_buf_initial_sz);
+ av_log(avctx,level," %*s%u\n", width, " rc_buf_optimal_sz:", cfg->rc_buf_optimal_sz);
+ av_log(avctx,level," %*s%u\n", width, " rc_2pass_vbr_bias_pct:", cfg->rc_2pass_vbr_bias_pct);
+ av_log(avctx,level," %*s%u\n", width, " rc_2pass_vbr_minsection_pct:", cfg->rc_2pass_vbr_minsection_pct);
+ av_log(avctx,level," %*s%u\n", width, " rc_2pass_vbr_maxsection_pct:", cfg->rc_2pass_vbr_maxsection_pct);
+ av_log(avctx,level," %*s%d\n", width, " kf_mode:", cfg->kf_mode);
+ av_log(avctx,level," %*s%u\n", width, " kf_min_dist:", cfg->kf_min_dist);
+ av_log(avctx,level," %*s%u\n", width, " kf_max_dist:", cfg->kf_max_dist);
+ av_log(avctx,level,"\n");
+}
+
+AVCodec libvpx_encoder = {
+ "libvpx",
+ AVMEDIA_TYPE_VIDEO,
+ CODEC_ID_VP8,
+ sizeof(VP8Context),
+ vp8_init,
+ vp8_encode,
+ vp8_free,
+ NULL,
+ CODEC_CAP_DELAY,
+ .pix_fmts = (const enum PixelFormat[]) { PIX_FMT_YUV420P, PIX_FMT_NONE },
+ .long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"),
+};
Index: ffpresets/libvpx-720p50_60.ffpreset
===================================================================
--- ffpresets/libvpx-720p50_60.ffpreset (revision 0)
+++ ffpresets/libvpx-720p50_60.ffpreset (revision 0)
@@ -0,0 +1,17 @@
+vcodec=libvpx
+bt=4M
+g=120
+lag=25
+level=216
+mb_static_threshold=0
+profile=0
+qmax=42
+qmin=10
+rc_buf_aggressivity=0.95
+token_partitions=4
+vb=2M
+
+#ignored unless using -pass 2
+altref=1
+maxrate=24M
+minrate=100k
Index: ffpresets/libvpx-1080p.ffpreset
===================================================================
--- ffpresets/libvpx-1080p.ffpreset (revision 0)
+++ ffpresets/libvpx-1080p.ffpreset (revision 0)
@@ -0,0 +1,17 @@
+vcodec=libvpx
+bt=4M
+g=120
+lag=16
+level=216
+mb_static_threshold=0
+profile=1
+qmax=42
+qmin=10
+rc_buf_aggressivity=0.95
+token_partitions=4
+vb=2M
+
+#ignored unless using -pass 2
+altref=1
+maxrate=24M
+minrate=100k
Index: ffpresets/libvpx-1080p50_60.ffpreset
===================================================================
--- ffpresets/libvpx-1080p50_60.ffpreset (revision 0)
+++ ffpresets/libvpx-1080p50_60.ffpreset (revision 0)
@@ -0,0 +1,17 @@
+vcodec=libvpx
+bt=4M
+g=120
+lag=25
+level=216
+mb_static_threshold=0
+profile=1
+qmax=42
+qmin=10
+rc_buf_aggressivity=0.95
+token_partitions=4
+vb=2M
+
+#ignored unless using -pass 2
+altref=1
+maxrate=24M
+minrate=100k
Index: ffpresets/libvpx-360p.ffpreset
===================================================================
--- ffpresets/libvpx-360p.ffpreset (revision 0)
+++ ffpresets/libvpx-360p.ffpreset (revision 0)
@@ -0,0 +1,16 @@
+vcodec=libvpx
+bt=1.5M
+g=120
+lag=16
+level=216
+mb_static_threshold=0
+profile=0
+qmax=51
+qmin=1
+rc_buf_aggressivity=0.95
+vb=768k
+
+#ignored unless using -pass 2
+altref=1
+maxrate=1.5M
+minrate=40k
Index: ffpresets/libvpx-720p.ffpreset
===================================================================
--- ffpresets/libvpx-720p.ffpreset (revision 0)
+++ ffpresets/libvpx-720p.ffpreset (revision 0)
@@ -0,0 +1,17 @@
+vcodec=libvpx
+bt=4M
+g=120
+lag=16
+level=216
+mb_static_threshold=0
+profile=0
+qmax=42
+qmin=10
+rc_buf_aggressivity=0.95
+token_partitions=4
+vb=2M
+
+#ignored unless using -pass 2
+altref=1
+maxrate=24M
+minrate=100k
Index: libavformat/Makefile
===================================================================
--- libavformat/Makefile (revision 23210)
+++ libavformat/Makefile (working copy)
@@ -255,6 +255,8 @@ OBJS-$(CONFIG_W64_DEMUXER)
OBJS-$(CONFIG_WAV_DEMUXER) += wav.o riff.o raw.o
OBJS-$(CONFIG_WAV_MUXER) += wav.o riff.o
OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o
+OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \
+ riff.o
OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood.o
OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood.o
OBJS-$(CONFIG_WV_DEMUXER) += wv.o apetag.o id3v1.o
Index: libavformat/allformats.c
===================================================================
--- libavformat/allformats.c (revision 23210)
+++ libavformat/allformats.c (working copy)
@@ -203,6 +203,7 @@ void av_register_all(void)
REGISTER_DEMUXER (W64, w64);
REGISTER_MUXDEMUX (WAV, wav);
REGISTER_DEMUXER (WC3, wc3);
+ REGISTER_MUXER (WEBM, webm);
REGISTER_DEMUXER (WSAUD, wsaud);
REGISTER_DEMUXER (WSVQA, wsvqa);
REGISTER_DEMUXER (WV, wv);
--- libavformat/matroskaenc.c.orig 2010-05-24 23:47:39.000000000 +0200
+++ libavformat/matroskaenc.c 2010-05-24 23:49:11.000000000 +0200
@@ -20,15 +20,21 @@
*/
#include "avformat.h"
+#if CONFIG_MATROSKA_MUXER
#include "riff.h"
#include "isom.h"
+#endif
#include "matroska.h"
+#if CONFIG_MATROSKA_MUXER
#include "avc.h"
#include "flacenc.h"
+#endif
#include "libavutil/intreadwrite.h"
#include "libavutil/md5.h"
#include "libavcodec/xiph.h"
+#if CONFIG_MATROSKA_MUXER
#include "libavcodec/mpeg4audio.h"
+#endif
typedef struct ebml_master {
int64_t pos; ///< absolute offset in the file where the master's elements start
@@ -81,6 +87,12 @@
mkv_track *tracks;
struct AVMD5 *md5_ctx;
+ int max_cluster_size;
+ int max_cluster_pts;
+#if CONFIG_WEBM_MUXER
+ unsigned int audio_buffer_size;
+ AVPacket cur_audio_pkt;
+#endif
} MatroskaMuxContext;
@@ -429,6 +441,7 @@
return 0;
}
+#if CONFIG_MATROSKA_MUXER
static void get_aac_sample_rates(AVFormatContext *s, AVCodecContext *codec, int *sample_rate, int *output_sample_rate)
{
int sri;
@@ -455,6 +468,7 @@
*output_sample_rate = ff_mpeg4audio_sample_rates[sri];
}
}
+#endif
static int mkv_write_codecprivate(AVFormatContext *s, ByteIOContext *pb, AVCodecContext *codec, int native_id, int qt_id)
{
@@ -469,12 +483,15 @@
if (native_id) {
if (codec->codec_id == CODEC_ID_VORBIS || codec->codec_id == CODEC_ID_THEORA)
ret = put_xiph_codecpriv(s, dyn_cp, codec);
+#if CONFIG_MATROSKA_MUXER
else if (codec->codec_id == CODEC_ID_FLAC)
ret = ff_flac_write_header(dyn_cp, codec, 1);
else if (codec->codec_id == CODEC_ID_H264)
ret = ff_isom_write_avcc(dyn_cp, codec->extradata, codec->extradata_size);
+#endif
else if (codec->extradata_size)
put_buffer(dyn_cp, codec->extradata, codec->extradata_size);
+#if CONFIG_MATROSKA_MUXER
} else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) {
if (qt_id) {
if (!codec->codec_tag)
@@ -503,6 +520,7 @@
codec->codec_tag = tag;
ff_put_wav_header(dyn_cp, codec);
+#endif
}
codecpriv_size = url_close_dyn_buf(dyn_cp, &codecpriv);
@@ -537,8 +555,10 @@
if (!bit_depth)
bit_depth = av_get_bits_per_sample_format(codec->sample_fmt);
+#if CONFIG_MATROSKA_MUXER
if (codec->codec_id == CODEC_ID_AAC)
get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate);
+#endif
track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0);
put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1);
@@ -565,9 +585,18 @@
switch (codec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
+#if CONFIG_WEBM_MUXER
+ if (codec->codec_id != CODEC_ID_VP8 &&
+ !strcmp("webm", s->oformat->name)) {
+ av_log(s, AV_LOG_INFO, "WebM only supports VP8 video.\n");
+ return -1;
+ }
+#endif
+
put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO);
put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9);
+#if CONFIG_MATROSKA_MUXER
if (!native_id &&
ff_codec_get_tag(codec_movvideo_tags, codec->codec_id) &&
(!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id)
@@ -583,7 +612,7 @@
put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC");
mkv->tracks[i].write_dts = 1;
}
-
+#endif
subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0);
// XXX: interlace flag?
put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width);
@@ -597,6 +626,13 @@
break;
case AVMEDIA_TYPE_AUDIO:
+#if CONFIG_WEBM_MUXER
+ if (codec->codec_id != CODEC_ID_VORBIS &&
+ !strcmp("webm", s->oformat->name)) {
+ av_log(s, AV_LOG_INFO, "WebM only supports Vorbis audio.\n");
+ return -1;
+ }
+#endif
put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO);
if (!native_id)
@@ -693,7 +729,7 @@
put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1);
put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4);
put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8);
- put_ebml_string (pb, EBML_ID_DOCTYPE , "matroska");
+ put_ebml_string (pb, EBML_ID_DOCTYPE , s->oformat->name);
put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2);
put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2);
end_ebml_master(pb, ebml_header);
@@ -746,6 +782,20 @@
if (mkv->cues == NULL)
return AVERROR(ENOMEM);
+#if CONFIG_WEBM_MUXER
+ av_init_packet(&mkv->cur_audio_pkt);
+ mkv->cur_audio_pkt.size = 0;
+ mkv->audio_buffer_size = 0;
+#endif
+
+ if (!strcmp("webm", s->oformat->name)) {
+ mkv->max_cluster_size = 500*1024*1024; // 500 MB
+ mkv->max_cluster_pts = 30000; // 30 seconds
+ } else {
+ mkv->max_cluster_size = 5*1024*1024; // 5 MB
+ mkv->max_cluster_pts = 5000; // 5 seconds
+ }
+
put_flush_packet(pb);
return 0;
}
@@ -831,10 +881,12 @@
av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
"pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n",
url_ftell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration, flags);
+#if CONFIG_MATROSKA_MUXER
if (codec->codec_id == CODEC_ID_H264 && codec->extradata_size > 0 &&
(AV_RB24(codec->extradata) == 1 || AV_RB32(codec->extradata) == 1))
ff_avc_parse_nal_units_buf(pkt->data, &data, &size);
else
+#endif
data = pkt->data;
put_ebml_id(pb, blockid);
put_ebml_num(pb, size+4, 0);
@@ -910,9 +962,10 @@
if (ret < 0) return ret;
}
- // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming
+ // start a new cluster every 32k / 1 sec for streaming
if ((url_is_streamed(s->pb) && (url_ftell(pb) > 32*1024 || ts > mkv->cluster_pts + 1000))
- || url_ftell(pb) > mkv->cluster_pos + 5*1024*1024 || ts > mkv->cluster_pts + 5000) {
+ || url_ftell(pb) > mkv->cluster_pos + mkv->max_cluster_size
+ || ts > mkv->cluster_pts + mkv->max_cluster_pts) {
av_log(s, AV_LOG_DEBUG, "Starting new cluster at offset %" PRIu64
" bytes, pts %" PRIu64 "\n", url_ftell(pb), ts);
end_ebml_master(pb, mkv->cluster);
@@ -924,7 +977,59 @@
mkv->duration = FFMAX(mkv->duration, ts + duration);
return 0;
}
+#if CONFIG_WEBM_MUXER
+static int mkv_copy_packet(MatroskaMuxContext *mkv, const AVPacket *pkt)
+{
+ uint8_t *data = mkv->cur_audio_pkt.data;
+ mkv->cur_audio_pkt = *pkt;
+ mkv->cur_audio_pkt.data = av_fast_realloc(data, &mkv->audio_buffer_size, pkt->size);
+ if (mkv->cur_audio_pkt.data == NULL)
+ return AVERROR(ENOMEM);
+
+ memcpy(mkv->cur_audio_pkt.data, pkt->data, pkt->size);
+ mkv->cur_audio_pkt.size = pkt->size;
+ return 0;
+}
+
+static int mkv_write_packet2(AVFormatContext *s, AVPacket *pkt)
+{
+ MatroskaMuxContext *mkv = s->priv_data;
+ ByteIOContext *pb = s->pb;
+ AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
+ int keyframe = !!(pkt->flags & PKT_FLAG_KEY);
+ int ret = 0;
+
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe && mkv->cluster_pos) {
+ // Start a new cluster when we get a key frame
+ int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
+
+ av_log(s, AV_LOG_DEBUG, "Starting new cluster at offset %" PRIu64
+ " bytes, pts %" PRIu64 "\n", url_ftell(pb), ts);
+ end_ebml_master(pb, mkv->cluster);
+ mkv->cluster_pos = 0;
+ if (mkv->dyn_bc)
+ mkv_flush_dynbuf(s);
+ }
+
+ // Check if we have an audio packet cached
+ if ( (codec->codec_type == AVMEDIA_TYPE_AUDIO || codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ && mkv->cur_audio_pkt.size > 0) {
+ ret = mkv_write_packet(s, &mkv->cur_audio_pkt);
+ mkv->cur_audio_pkt.size = 0;
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret);
+ return ret;
+ }
+ }
+
+ if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ ret = mkv_copy_packet(mkv, pkt);
+ else
+ ret = mkv_write_packet(s, pkt);
+ return ret;
+}
+#endif
static int mkv_write_trailer(AVFormatContext *s)
{
MatroskaMuxContext *mkv = s->priv_data;
@@ -932,6 +1037,18 @@
int64_t currentpos, second_seekhead, cuespos;
int ret;
+#if CONFIG_WEBM_MUXER
+ // Check if we have an audio packet cached
+ if (mkv->cur_audio_pkt.size > 0) {
+ ret = mkv_write_packet(s, &mkv->cur_audio_pkt);
+ mkv->cur_audio_pkt.size = 0;
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret);
+ return ret;
+ }
+ }
+#endif
+
if (mkv->dyn_bc) {
end_ebml_master(mkv->dyn_bc, mkv->cluster);
mkv_flush_dynbuf(s);
@@ -970,10 +1087,15 @@
end_ebml_master(pb, mkv->segment);
av_free(mkv->md5_ctx);
av_free(mkv->tracks);
+#if CONFIG_WEBM_MUXER
+ if (!strcmp("webm", s->oformat->name))
+ av_destruct_packet(&mkv->cur_audio_pkt);
+#endif
put_flush_packet(pb);
return 0;
}
+#if CONFIG_MATROSKA_MUXER
AVOutputFormat matroska_muxer = {
"matroska",
NULL_IF_CONFIG_SMALL("Matroska file format"),
@@ -989,7 +1111,23 @@
.codec_tag = (const AVCodecTag* const []){ff_codec_bmp_tags, ff_codec_wav_tags, 0},
.subtitle_codec = CODEC_ID_TEXT,
};
-
+#endif
+#if CONFIG_WEBM_MUXER
+AVOutputFormat webm_muxer = {
+ "webm",
+ NULL_IF_CONFIG_SMALL("WebM file format"),
+ "video/webm",
+ "webm",
+ sizeof(MatroskaMuxContext),
+ CODEC_ID_VORBIS,
+ CODEC_ID_VP8,
+ mkv_write_header,
+ mkv_write_packet2,
+ mkv_write_trailer,
+ .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
+};
+#endif
+#if CONFIG_MATROSKA_AUDIO_MUXER
AVOutputFormat matroska_audio_muxer = {
"matroska",
NULL_IF_CONFIG_SMALL("Matroska file format"),
@@ -1004,3 +1142,4 @@
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){ff_codec_wav_tags, 0},
};
+#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment