| @@ -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 | }; |