| @@ -0,0 +1,489 @@ |
| 1 | +/* |
| 2 | + * Copyright (c) 2010, Google, Inc. |
| 3 | + * |
| 4 | + * This file is part of FFmpeg. |
| 5 | + * |
| 6 | + * FFmpeg is free software; you can redistribute it and/or |
| 7 | + * modify it under the terms of the GNU Lesser General Public |
| 8 | + * License as published by the Free Software Foundation; either |
| 9 | + * version 2.1 of the License, or (at your option) any later version. |
| 10 | + * |
| 11 | + * FFmpeg is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | + * Lesser General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU Lesser General Public |
| 17 | + * License along with FFmpeg; if not, write to the Free Software |
| 18 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 19 | + */ |
| 20 | + |
| 21 | +/** |
| 22 | + * @file |
| 23 | + * VP8 encoder support via libvpx |
| 24 | + */ |
| 25 | + |
| 26 | +#define VPX_DISABLE_CTRL_TYPECHECKS 1 |
| 27 | +#define VPX_CODEC_DISABLE_COMPAT 1 |
| 28 | +#include <vpx/vpx_encoder.h> |
| 29 | +#include <vpx/vp8cx.h> |
| 30 | + |
| 31 | +#include "avcodec.h" |
| 32 | +#include "libavutil/base64.h" |
| 33 | + |
| 34 | +/** |
| 35 | + * Portion of struct vpx_codec_cx_pkt from vpx_encoder.h. |
| 36 | + * One encoded frame returned from the library. |
| 37 | + */ |
| 38 | +struct FrameListData { |
| 39 | + void *buf; /**≤ compressed data buffer */ |
| 40 | + size_t sz; /**≤ length of compressed data */ |
| 41 | + int64_t pts; /**≤ time stamp to show frame |
| 42 | + (in timebase units) */ |
| 43 | + unsigned long duration; /**≤ duration to show frame |
| 44 | + (in timebase units) */ |
| 45 | + uint32_t flags; /**≤ flags for this frame */ |
| 46 | + struct FrameListData *next; |
| 47 | +}; |
| 48 | + |
| 49 | +typedef struct VP8EncoderContext { |
| 50 | + struct vpx_codec_ctx encoder; |
| 51 | + struct vpx_image rawimg; |
| 52 | + struct vpx_fixed_buf twopass_stats; |
| 53 | + unsigned long deadline; //i.e., RT/GOOD/BEST |
| 54 | + struct FrameListData *coded_frame_list; |
| 55 | +} VP8Context; |
| 56 | + |
| 57 | +/** String mappings for enum vp8e_enc_control_id */ |
| 58 | +static const char *ctlidstr[] = { |
| 59 | + [VP8E_UPD_ENTROPY] = "VP8E_UPD_ENTROPY", |
| 60 | + [VP8E_UPD_REFERENCE] = "VP8E_UPD_REFERENCE", |
| 61 | + [VP8E_USE_REFERENCE] = "VP8E_USE_REFERENCE", |
| 62 | + [VP8E_SET_ROI_MAP] = "VP8E_SET_ROI_MAP", |
| 63 | + [VP8E_SET_ACTIVEMAP] = "VP8E_SET_ACTIVEMAP", |
| 64 | + [VP8E_SET_SCALEMODE] = "VP8E_SET_SCALEMODE", |
| 65 | + [VP8E_SET_CPUUSED] = "VP8E_SET_CPUUSED", |
| 66 | + [VP8E_SET_ENABLEAUTOALTREF] = "VP8E_SET_ENABLEAUTOALTREF", |
| 67 | + [VP8E_SET_NOISE_SENSITIVITY] = "VP8E_SET_NOISE_SENSITIVITY", |
| 68 | + [VP8E_SET_SHARPNESS] = "VP8E_SET_SHARPNESS", |
| 69 | + [VP8E_SET_STATIC_THRESHOLD] = "VP8E_SET_STATIC_THRESHOLD", |
| 70 | + [VP8E_SET_TOKEN_PARTITIONS] = "VP8E_SET_TOKEN_PARTITIONS", |
| 71 | + [VP8E_GET_LAST_QUANTIZER] = "VP8E_GET_LAST_QUANTIZER", |
| 72 | + [VP8E_SET_ARNR_MAXFRAMES] = "VP8E_SET_ARNR_MAXFRAMES", |
| 73 | + [VP8E_SET_ARNR_STRENGTH] = "VP8E_SET_ARNR_STRENGTH", |
| 74 | + [VP8E_SET_ARNR_TYPE] = "VP8E_SET_ARNR_TYPE", |
| 75 | +}; |
| 76 | + |
| 77 | +static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc) |
| 78 | +{ |
| 79 | + VP8Context *ctx = avctx->priv_data; |
| 80 | + const char *error = vpx_codec_error(&ctx->encoder); |
| 81 | + const char *detail = vpx_codec_error_detail(&ctx->encoder); |
| 82 | + |
| 83 | + av_log(avctx, AV_LOG_ERROR, "%s: %s\n", desc, error); |
| 84 | + if (detail) |
| 85 | + av_log(avctx, AV_LOG_ERROR, " Additional information: %s\n", detail); |
| 86 | +} |
| 87 | + |
| 88 | +static av_cold void dump_enc_cfg(AVCodecContext *avctx, |
| 89 | + const struct vpx_codec_enc_cfg *cfg) |
| 90 | +{ |
| 91 | + int width = -30; |
| 92 | + int level = AV_LOG_DEBUG; |
| 93 | + |
| 94 | + av_log(avctx, level, "vpx_codec_enc_cfg\n"); |
| 95 | + av_log(avctx, level, "generic settings\n" |
| 96 | + " %*s%u\n %*s%u\n %*s%u\n %*s%u\n %*s%u\n" |
| 97 | + " %*s{%u/%u}\n %*s%u\n %*s%d\n %*s%u\n", |
| 98 | + width, "g_usage:", cfg->g_usage, |
| 99 | + width, "g_threads:", cfg->g_threads, |
| 100 | + width, "g_profile:", cfg->g_profile, |
| 101 | + width, "g_w:", cfg->g_w, |
| 102 | + width, "g_h:", cfg->g_h, |
| 103 | + width, "g_timebase:", cfg->g_timebase.num, cfg->g_timebase.den, |
| 104 | + width, "g_error_resilient:", cfg->g_error_resilient, |
| 105 | + width, "g_pass:", cfg->g_pass, |
| 106 | + width, "g_lag_in_frames:", cfg->g_lag_in_frames); |
| 107 | + av_log(avctx, level, "rate control settings\n" |
| 108 | + " %*s%u\n %*s%u\n %*s%u\n %*s%u\n" |
| 109 | + " %*s%d\n %*s%p(%zu)\n %*s%u\n", |
| 110 | + width, "rc_dropframe_thresh:", cfg->rc_dropframe_thresh, |
| 111 | + width, "rc_resize_allowed:", cfg->rc_resize_allowed, |
| 112 | + width, "rc_resize_up_thresh:", cfg->rc_resize_up_thresh, |
| 113 | + width, "rc_resize_down_thresh:", cfg->rc_resize_down_thresh, |
| 114 | + width, "rc_end_usage:", cfg->rc_end_usage, |
| 115 | + width, "rc_twopass_stats_in:", cfg->rc_twopass_stats_in.buf, cfg->rc_twopass_stats_in.sz, |
| 116 | + width, "rc_target_bitrate:", cfg->rc_target_bitrate); |
| 117 | + av_log(avctx, level, "quantizer settings\n" |
| 118 | + " %*s%u\n %*s%u\n", |
| 119 | + width, "rc_min_quantizer:", cfg->rc_min_quantizer, |
| 120 | + width, "rc_max_quantizer:", cfg->rc_max_quantizer); |
| 121 | + av_log(avctx, level, "bitrate tolerance\n" |
| 122 | + " %*s%u\n %*s%u\n", |
| 123 | + width, "rc_undershoot_pct:", cfg->rc_undershoot_pct, |
| 124 | + width, "rc_overshoot_pct:", cfg->rc_overshoot_pct); |
| 125 | + av_log(avctx, level, "decoder buffer model\n" |
| 126 | + " %*s%u\n %*s%u\n %*s%u\n", |
| 127 | + width, "rc_buf_sz:", cfg->rc_buf_sz, |
| 128 | + width, "rc_buf_initial_sz:", cfg->rc_buf_initial_sz, |
| 129 | + width, "rc_buf_optimal_sz:", cfg->rc_buf_optimal_sz); |
| 130 | + av_log(avctx, level, "2 pass rate control settings\n" |
| 131 | + " %*s%u\n %*s%u\n %*s%u\n", |
| 132 | + width, "rc_2pass_vbr_bias_pct:", cfg->rc_2pass_vbr_bias_pct, |
| 133 | + width, "rc_2pass_vbr_minsection_pct:", cfg->rc_2pass_vbr_minsection_pct, |
| 134 | + width, "rc_2pass_vbr_maxsection_pct:", cfg->rc_2pass_vbr_maxsection_pct); |
| 135 | + av_log(avctx, level, "keyframing settings\n" |
| 136 | + " %*s%d\n %*s%u\n %*s%u\n", |
| 137 | + width, "kf_mode:", cfg->kf_mode, |
| 138 | + width, "kf_min_dist:", cfg->kf_min_dist, |
| 139 | + width, "kf_max_dist:", cfg->kf_max_dist); |
| 140 | + av_log(avctx, level, "\n"); |
| 141 | +} |
| 142 | + |
| 143 | +static void coded_frame_add(void *list, struct FrameListData *cx_frame) |
| 144 | +{ |
| 145 | + struct FrameListData **p = list; |
| 146 | + |
| 147 | + while (*p != NULL) |
| 148 | + p = &(*p)->next; |
| 149 | + *p = cx_frame; |
| 150 | + cx_frame->next = NULL; |
| 151 | +} |
| 152 | + |
| 153 | +static av_cold void free_coded_frame(struct FrameListData *cx_frame) |
| 154 | +{ |
| 155 | + av_freep(&cx_frame->buf); |
| 156 | + av_freep(&cx_frame); |
| 157 | +} |
| 158 | + |
| 159 | +static av_cold void free_frame_list(struct FrameListData *list) |
| 160 | +{ |
| 161 | + struct FrameListData *p = list; |
| 162 | + |
| 163 | + while (p) { |
| 164 | + list = list->next; |
| 165 | + free_coded_frame(p); |
| 166 | + p = list; |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | +static av_cold int codecctl_int(AVCodecContext *avctx, |
| 171 | + enum vp8e_enc_control_id id, int val) |
| 172 | +{ |
| 173 | + VP8Context *ctx = avctx->priv_data; |
| 174 | + char buf[80]; |
| 175 | + int width = -30; |
| 176 | + int res; |
| 177 | + |
| 178 | + snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]); |
| 179 | + av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, val); |
| 180 | + |
| 181 | + res = vpx_codec_control(&ctx->encoder, id, val); |
| 182 | + if (res != VPX_CODEC_OK) { |
| 183 | + snprintf(buf, sizeof(buf), "Failed to set %s codec control", |
| 184 | + ctlidstr[id]); |
| 185 | + log_encoder_error(avctx, buf); |
| 186 | + } |
| 187 | + |
| 188 | + return res == VPX_CODEC_OK ? 0 : AVERROR(EINVAL); |
| 189 | +} |
| 190 | + |
| 191 | +static av_cold int vp8_free(AVCodecContext *avctx) |
| 192 | +{ |
| 193 | + VP8Context *ctx = avctx->priv_data; |
| 194 | + |
| 195 | + vpx_codec_destroy(&ctx->encoder); |
| 196 | + av_freep(&ctx->twopass_stats.buf); |
| 197 | + av_freep(&avctx->coded_frame); |
| 198 | + av_freep(&avctx->stats_out); |
| 199 | + free_frame_list(ctx->coded_frame_list); |
| 200 | + return 0; |
| 201 | +} |
| 202 | + |
| 203 | +static av_cold int vp8_init(AVCodecContext *avctx) |
| 204 | +{ |
| 205 | + VP8Context *ctx = avctx->priv_data; |
| 206 | + const struct vpx_codec_iface *iface = &vpx_codec_vp8_cx_algo; |
| 207 | + int cpuused = 3; |
| 208 | + struct vpx_codec_enc_cfg enccfg; |
| 209 | + int res; |
| 210 | + |
| 211 | + av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str()); |
| 212 | + av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config()); |
| 213 | + |
| 214 | + if ((res = vpx_codec_enc_config_default(iface, &enccfg, 0)) != VPX_CODEC_OK) { |
| 215 | + av_log(avctx, AV_LOG_ERROR, "Failed to get config: %s\n", |
| 216 | + vpx_codec_err_to_string(res)); |
| 217 | + return AVERROR(EINVAL); |
| 218 | + } |
| 219 | + dump_enc_cfg(avctx, &enccfg); |
| 220 | + |
| 221 | + enccfg.g_w = avctx->width; |
| 222 | + enccfg.g_h = avctx->height; |
| 223 | + enccfg.g_timebase.num = avctx->time_base.num; |
| 224 | + enccfg.g_timebase.den = avctx->time_base.den; |
| 225 | + enccfg.g_threads = avctx->thread_count; |
| 226 | + |
| 227 | + if (avctx->flags & CODEC_FLAG_PASS1) |
| 228 | + enccfg.g_pass = VPX_RC_FIRST_PASS; |
| 229 | + else if (avctx->flags & CODEC_FLAG_PASS2) |
| 230 | + enccfg.g_pass = VPX_RC_LAST_PASS; |
| 231 | + else |
| 232 | + enccfg.g_pass = VPX_RC_ONE_PASS; |
| 233 | + |
| 234 | + if (avctx->rc_min_rate == avctx->rc_max_rate && |
| 235 | + avctx->rc_min_rate == avctx->bit_rate) |
| 236 | + enccfg.rc_end_usage = VPX_CBR; |
| 237 | + enccfg.rc_target_bitrate = av_rescale_rnd(avctx->bit_rate, 1, 1000, |
| 238 | + AV_ROUND_NEAR_INF); |
| 239 | + |
| 240 | + //convert [1,51] -> [0,63] |
| 241 | + enccfg.rc_min_quantizer = ((avctx->qmin * 5 + 1) >> 2) - 1; |
| 242 | + enccfg.rc_max_quantizer = ((avctx->qmax * 5 + 1) >> 2) - 1; |
| 243 | + |
| 244 | + if (avctx->keyint_min == avctx->gop_size) |
| 245 | + enccfg.kf_mode = VPX_KF_FIXED; |
| 246 | + //_enc_init() will balk if kf_min_dist is set in this case |
| 247 | + if (enccfg.kf_mode != VPX_KF_AUTO) |
| 248 | + enccfg.kf_min_dist = avctx->keyint_min; |
| 249 | + enccfg.kf_max_dist = avctx->gop_size; |
| 250 | + |
| 251 | + if (enccfg.g_pass == VPX_RC_FIRST_PASS) |
| 252 | + enccfg.g_lag_in_frames = 0; |
| 253 | + else if (enccfg.g_pass == VPX_RC_LAST_PASS) { |
| 254 | + int decode_size; |
| 255 | + |
| 256 | + if (!avctx->stats_in) { |
| 257 | + av_log(avctx, AV_LOG_ERROR, "No stats file for second pass\n"); |
| 258 | + return AVERROR_INVALIDDATA; |
| 259 | + } |
| 260 | + |
| 261 | + ctx->twopass_stats.sz = strlen(avctx->stats_in) * 3 / 4; |
| 262 | + ctx->twopass_stats.buf = av_malloc(ctx->twopass_stats.sz); |
| 263 | + if (!ctx->twopass_stats.buf) { |
| 264 | + av_log(avctx, AV_LOG_ERROR, |
| 265 | + "Stat buffer alloc (%zu bytes) failed\n", |
| 266 | + ctx->twopass_stats.sz); |
| 267 | + return AVERROR(ENOMEM); |
| 268 | + } |
| 269 | + decode_size = av_base64_decode(ctx->twopass_stats.buf, avctx->stats_in, |
| 270 | + ctx->twopass_stats.sz); |
| 271 | + if (decode_size < 0) { |
| 272 | + av_log(avctx, AV_LOG_ERROR, "Stat buffer decode failed\n"); |
| 273 | + return AVERROR_INVALIDDATA; |
| 274 | + } |
| 275 | + |
| 276 | + ctx->twopass_stats.sz = decode_size; |
| 277 | + enccfg.rc_twopass_stats_in = ctx->twopass_stats; |
| 278 | + } |
| 279 | + |
| 280 | + ctx->deadline = VPX_DL_GOOD_QUALITY; |
| 281 | + |
| 282 | + dump_enc_cfg(avctx, &enccfg); |
| 283 | + /* Construct Encoder Context */ |
| 284 | + res = vpx_codec_enc_init(&ctx->encoder, iface, &enccfg, 0); |
| 285 | + if (res != VPX_CODEC_OK) { |
| 286 | + log_encoder_error(avctx, "Failed to initialize encoder"); |
| 287 | + return AVERROR(EINVAL); |
| 288 | + } |
| 289 | + |
| 290 | + //codec control failures are currently treated only as warnings |
| 291 | + av_log(avctx, AV_LOG_DEBUG, "vpx_codec_control\n"); |
| 292 | + codecctl_int(avctx, VP8E_SET_CPUUSED, cpuused); |
| 293 | + codecctl_int(avctx, VP8E_SET_NOISE_SENSITIVITY, avctx->noise_reduction); |
| 294 | + |
| 295 | + //provide dummy value to initialize wrapper, values will be updated each _encode() |
| 296 | + vpx_img_wrap(&ctx->rawimg, VPX_IMG_FMT_I420, avctx->width, avctx->height, 1, |
| 297 | + (unsigned char*)1); |
| 298 | + |
| 299 | + avctx->coded_frame = avcodec_alloc_frame(); |
| 300 | + if (!avctx->coded_frame) { |
| 301 | + av_log(avctx, AV_LOG_ERROR, "Error allocating coded frame\n"); |
| 302 | + vp8_free(avctx); |
| 303 | + return AVERROR(ENOMEM); |
| 304 | + } |
| 305 | + return 0; |
| 306 | +} |
| 307 | + |
| 308 | +static inline void cx_pktcpy(struct FrameListData *dst, |
| 309 | + const struct vpx_codec_cx_pkt *src) |
| 310 | +{ |
| 311 | + dst->pts = src->data.frame.pts; |
| 312 | + dst->duration = src->data.frame.duration; |
| 313 | + dst->flags = src->data.frame.flags; |
| 314 | + dst->sz = src->data.frame.sz; |
| 315 | + dst->buf = src->data.frame.buf; |
| 316 | +} |
| 317 | + |
| 318 | +/** |
| 319 | + * Store coded frame information in format suitable for return from encode(). |
| 320 | + * |
| 321 | + * Write buffer information from @a cx_frame to @a buf & @a buf_size. |
| 322 | + * Timing/frame details to @a coded_frame. |
| 323 | + * @return Frame size written to @a buf on success |
| 324 | + * @return AVERROR(EINVAL) on error |
| 325 | + */ |
| 326 | +static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, |
| 327 | + uint8_t *buf, int buf_size, AVFrame *coded_frame) |
| 328 | +{ |
| 329 | + if ((int) cx_frame->sz <= buf_size) { |
| 330 | + buf_size = cx_frame->sz; |
| 331 | + memcpy(buf, cx_frame->buf, buf_size); |
| 332 | + coded_frame->pts = cx_frame->pts; |
| 333 | + coded_frame->key_frame = !!(cx_frame->flags & VPX_FRAME_IS_KEY); |
| 334 | + |
| 335 | + if (coded_frame->key_frame) |
| 336 | + coded_frame->pict_type = FF_I_TYPE; |
| 337 | + else |
| 338 | + coded_frame->pict_type = FF_P_TYPE; |
| 339 | + } else { |
| 340 | + av_log(avctx, AV_LOG_ERROR, |
| 341 | + "Compressed frame larger than storage provided! (%zu/%d)\n", |
| 342 | + cx_frame->sz, buf_size); |
| 343 | + return AVERROR(EINVAL); |
| 344 | + } |
| 345 | + return buf_size; |
| 346 | +} |
| 347 | + |
| 348 | +/** |
| 349 | + * Queue multiple output frames from the encoder, returning the front-most. |
| 350 | + * In cases where vpx_codec_get_cx_data() returns more than 1 frame append |
| 351 | + * the frame queue. Return the head frame if available. |
| 352 | + * @return Stored frame size |
| 353 | + * @return AVERROR(EINVAL) on output size error |
| 354 | + * @return AVERROR(ENOMEM) on coded frame queue data allocation error |
| 355 | + */ |
| 356 | +static int queue_frames(AVCodecContext *avctx, uint8_t *buf, int buf_size, |
| 357 | + AVFrame *coded_frame) |
| 358 | +{ |
| 359 | + VP8Context *ctx = avctx->priv_data; |
| 360 | + const struct vpx_codec_cx_pkt *pkt; |
| 361 | + const void *iter = NULL; |
| 362 | + int size = 0; |
| 363 | + |
| 364 | + if (ctx->coded_frame_list) { |
| 365 | + struct FrameListData *cx_frame = ctx->coded_frame_list; |
| 366 | + /* return the leading frame if we've already begun queueing */ |
| 367 | + size = storeframe(avctx, cx_frame, buf, buf_size, coded_frame); |
| 368 | + if (size < 0) |
| 369 | + return AVERROR(EINVAL); |
| 370 | + ctx->coded_frame_list = cx_frame->next; |
| 371 | + free_coded_frame(cx_frame); |
| 372 | + } |
| 373 | + |
| 374 | + /* consume all available output from the encoder before returning. buffers |
| 375 | + are only good through the next vpx_codec call */ |
| 376 | + while ((pkt = vpx_codec_get_cx_data(&ctx->encoder, &iter))) { |
| 377 | + switch (pkt->kind) { |
| 378 | + case VPX_CODEC_CX_FRAME_PKT: |
| 379 | + if (!size) { |
| 380 | + struct FrameListData cx_frame; |
| 381 | + |
| 382 | + /* avoid storing the frame when the list is empty and we haven't yet |
| 383 | + provided a frame for output */ |
| 384 | + assert(!ctx->coded_frame_list); |
| 385 | + cx_pktcpy(&cx_frame, pkt); |
| 386 | + size = storeframe(avctx, &cx_frame, buf, buf_size, coded_frame); |
| 387 | + if (size < 0) |
| 388 | + return AVERROR(EINVAL); |
| 389 | + } else { |
| 390 | + struct FrameListData *cx_frame = |
| 391 | + av_malloc(sizeof(struct FrameListData)); |
| 392 | + |
| 393 | + if (!cx_frame) { |
| 394 | + av_log(avctx, AV_LOG_ERROR, |
| 395 | + "Frame queue element alloc failed\n"); |
| 396 | + return AVERROR(ENOMEM); |
| 397 | + } |
| 398 | + cx_pktcpy(cx_frame, pkt); |
| 399 | + cx_frame->buf = av_malloc(cx_frame->sz); |
| 400 | + |
| 401 | + if (!cx_frame->buf) { |
| 402 | + av_log(avctx, AV_LOG_ERROR, |
| 403 | + "Data buffer alloc (%zu bytes) failed\n", |
| 404 | + cx_frame->sz); |
| 405 | + return AVERROR(ENOMEM); |
| 406 | + } |
| 407 | + memcpy(cx_frame->buf, pkt->data.frame.buf, pkt->data.frame.sz); |
| 408 | + coded_frame_add(&ctx->coded_frame_list, cx_frame); |
| 409 | + } |
| 410 | + break; |
| 411 | + case VPX_CODEC_STATS_PKT: { |
| 412 | + struct vpx_fixed_buf *stats = &ctx->twopass_stats; |
| 413 | + stats->buf = av_realloc(stats->buf, |
| 414 | + stats->sz + pkt->data.twopass_stats.sz); |
| 415 | + if (!stats->buf) { |
| 416 | + av_log(avctx, AV_LOG_ERROR, "Stat buffer realloc failed\n"); |
| 417 | + return AVERROR(ENOMEM); |
| 418 | + } |
| 419 | + memcpy((uint8_t*)stats->buf + stats->sz, |
| 420 | + pkt->data.twopass_stats.buf, pkt->data.twopass_stats.sz); |
| 421 | + stats->sz += pkt->data.twopass_stats.sz; |
| 422 | + break; |
| 423 | + } |
| 424 | + case VPX_CODEC_PSNR_PKT: //FIXME add support for CODEC_FLAG_PSNR |
| 425 | + case VPX_CODEC_CUSTOM_PKT: |
| 426 | + //ignore unsupported/unrecognized packet types |
| 427 | + break; |
| 428 | + } |
| 429 | + } |
| 430 | + |
| 431 | + return size; |
| 432 | +} |
| 433 | + |
| 434 | +static int vp8_encode(AVCodecContext *avctx, uint8_t *buf, int buf_size, |
| 435 | + void *data) |
| 436 | +{ |
| 437 | + VP8Context *ctx = avctx->priv_data; |
| 438 | + AVFrame *frame = data; |
| 439 | + struct vpx_image *rawimg = NULL; |
| 440 | + int64_t timestamp = 0; |
| 441 | + int res, coded_size; |
| 442 | + |
| 443 | + if (frame) { |
| 444 | + rawimg = &ctx->rawimg; |
| 445 | + rawimg->planes[VPX_PLANE_Y] = frame->data[0]; |
| 446 | + rawimg->planes[VPX_PLANE_U] = frame->data[1]; |
| 447 | + rawimg->planes[VPX_PLANE_V] = frame->data[2]; |
| 448 | + rawimg->stride[VPX_PLANE_Y] = frame->linesize[0]; |
| 449 | + rawimg->stride[VPX_PLANE_U] = frame->linesize[1]; |
| 450 | + rawimg->stride[VPX_PLANE_V] = frame->linesize[2]; |
| 451 | + timestamp = frame->pts; |
| 452 | + } |
| 453 | + |
| 454 | + res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, |
| 455 | + avctx->ticks_per_frame, 0, ctx->deadline); |
| 456 | + if (res != VPX_CODEC_OK) { |
| 457 | + log_encoder_error(avctx, "Error encoding frame"); |
| 458 | + return AVERROR_INVALIDDATA; |
| 459 | + } |
| 460 | + coded_size = queue_frames(avctx, buf, buf_size, avctx->coded_frame); |
| 461 | + |
| 462 | + if (!frame && avctx->flags & CODEC_FLAG_PASS1) { |
| 463 | + unsigned int b64_size = ((ctx->twopass_stats.sz + 2) / 3) * 4 + 1; |
| 464 | + |
| 465 | + avctx->stats_out = av_malloc(b64_size); |
| 466 | + if (!avctx->stats_out) { |
| 467 | + av_log(avctx, AV_LOG_ERROR, "Stat buffer alloc (%d bytes) failed\n", |
| 468 | + b64_size); |
| 469 | + return AVERROR(ENOMEM); |
| 470 | + } |
| 471 | + av_base64_encode(avctx->stats_out, b64_size, ctx->twopass_stats.buf, |
| 472 | + ctx->twopass_stats.sz); |
| 473 | + } |
| 474 | + return coded_size; |
| 475 | +} |
| 476 | + |
| 477 | +AVCodec libvpx_encoder = { |
| 478 | + "libvpx", |
| 479 | + AVMEDIA_TYPE_VIDEO, |
| 480 | + CODEC_ID_VP8, |
| 481 | + sizeof(VP8Context), |
| 482 | + vp8_init, |
| 483 | + vp8_encode, |
| 484 | + vp8_free, |
| 485 | + NULL, |
| 486 | + CODEC_CAP_DELAY, |
| 487 | + .pix_fmts = (const enum PixelFormat[]){PIX_FMT_YUV420P, PIX_FMT_NONE}, |
| 488 | + .long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"), |
| 489 | +}; |