--- /dev/null
+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,128 @@
++/*
++ * 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)>>2) - 1</td><td>-qmin</td></tr>
++ * <tr><td>rc_max_quantizer</td><td>((AVCodecContext::qmax*5+1)>>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 && 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<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/matroskaenc.c
+===================================================================
+--- libavformat/matroskaenc.c (revision 23210)
++++ libavformat/matroskaenc.c (working copy)
+@@ -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 @@ typedef struct MatroskaMuxContext {
+ 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 @@ static int put_xiph_codecpriv(AVFormatCo
+ 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 @@ static void get_aac_sample_rates(AVForma
+ *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 @@ static int mkv_write_codecprivate(AVForm
+ 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 @@ static int mkv_write_codecprivate(AVForm
+ 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 @@ static int mkv_write_tracks(AVFormatCont
+ 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);
+@@ -566,8 +586,16 @@ static int mkv_write_tracks(AVFormatCont
+
+ 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);
+
++#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 +611,7 @@ static int mkv_write_tracks(AVFormatCont
+ 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 +625,13 @@ static int mkv_write_tracks(AVFormatCont
+ 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 +728,7 @@ static int mkv_write_header(AVFormatCont
+ 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 +781,20 @@ static int mkv_write_header(AVFormatCont
+ 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 +880,12 @@ static void mkv_write_block(AVFormatCont
+ 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 +961,10 @@ static int mkv_write_packet(AVFormatCont
+ 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 +976,59 @@ static int mkv_write_packet(AVFormatCont
+ 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 +1036,18 @@ static int mkv_write_trailer(AVFormatCon
+ 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 +1086,15 @@ static int mkv_write_trailer(AVFormatCon
+ 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 +1110,23 @@ AVOutputFormat matroska_muxer = {
+ .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 +1141,4 @@ AVOutputFormat matroska_audio_muxer = {
+ .flags = AVFMT_GLOBALHEADER,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_wav_tags, 0},
+ };
++#endif
+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);