diff options
author | Vignesh Venkatasubramanian <vigneshv@google.com> | 2013-05-08 16:59:33 -0700 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2013-05-17 15:11:23 +0200 |
commit | 98c292a7a8f273050febc61acd725e572aa8d3ed (patch) | |
tree | 0cfbc64ae402feb26c5ed0f85c14fc6726f28360 | |
parent | 2fb193b1e7428fba0fe43ca9c231b3f822d65774 (diff) | |
download | ffmpeg-98c292a7a8f273050febc61acd725e572aa8d3ed.tar.gz |
Adding support for encoding VP8 Alpha
This patch adds support for encoding VP8 files with alpha. The alpha channel
is encoded separately and the output is placed in AVPacket's side_data. The
muxer then muxes it into the BlockAdditional element of the matroska container.
More details on spec here: http://goo.gl/wCP1y
Signed-off-by: Vignesh Venkatasubramanian <vigneshv@google.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | libavcodec/libvpxenc.c | 99 | ||||
-rw-r--r-- | libavformat/mux.c | 1 |
2 files changed, 96 insertions, 4 deletions
diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index 4f1adb85c4..f99e94cae9 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -33,6 +33,7 @@ #include "libavutil/avassert.h" #include "libavutil/base64.h" #include "libavutil/common.h" +#include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -43,6 +44,8 @@ struct FrameListData { void *buf; /**< compressed data buffer */ size_t sz; /**< length of compressed data */ + void *buf_alpha; + size_t sz_alpha; int64_t pts; /**< time stamp to show frame (in timebase units) */ unsigned long duration; /**< duration to show frame @@ -58,6 +61,9 @@ typedef struct VP8EncoderContext { AVClass *class; struct vpx_codec_ctx encoder; struct vpx_image rawimg; + struct vpx_codec_ctx encoder_alpha; + struct vpx_image rawimg_alpha; + uint8_t is_alpha; struct vpx_fixed_buf twopass_stats; int deadline; //i.e., RT/GOOD/BEST uint64_t sse[4]; @@ -186,6 +192,8 @@ static void coded_frame_add(void *list, struct FrameListData *cx_frame) static av_cold void free_coded_frame(struct FrameListData *cx_frame) { av_freep(&cx_frame->buf); + if (cx_frame->buf_alpha) + av_freep(&cx_frame->buf_alpha); av_freep(&cx_frame); } @@ -226,6 +234,8 @@ static av_cold int vp8_free(AVCodecContext *avctx) VP8Context *ctx = avctx->priv_data; vpx_codec_destroy(&ctx->encoder); + if (ctx->is_alpha) + vpx_codec_destroy(&ctx->encoder_alpha); av_freep(&ctx->twopass_stats.buf); av_freep(&avctx->coded_frame); av_freep(&avctx->stats_out); @@ -238,12 +248,16 @@ static av_cold int vpx_init(AVCodecContext *avctx, { VP8Context *ctx = avctx->priv_data; struct vpx_codec_enc_cfg enccfg; + struct vpx_codec_enc_cfg enccfg_alpha; vpx_codec_flags_t flags = (avctx->flags & CODEC_FLAG_PSNR) ? VPX_CODEC_USE_PSNR : 0; int 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 (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) + ctx->is_alpha = 1; + if ((res = vpx_codec_enc_config_default(iface, &enccfg, 0)) != VPX_CODEC_OK) { av_log(avctx, AV_LOG_ERROR, "Failed to get config: %s\n", vpx_codec_err_to_string(res)); @@ -377,6 +391,15 @@ static av_cold int vpx_init(AVCodecContext *avctx, return AVERROR(EINVAL); } + if (ctx->is_alpha) { + enccfg_alpha = enccfg; + res = vpx_codec_enc_init(&ctx->encoder_alpha, iface, &enccfg_alpha, flags); + if (res != VPX_CODEC_OK) { + log_encoder_error(avctx, "Failed to initialize alpha encoder"); + return AVERROR(EINVAL); + } + } + //codec control failures are currently treated only as warnings av_log(avctx, AV_LOG_DEBUG, "vpx_codec_control\n"); if (ctx->cpu_used != INT_MIN) @@ -404,6 +427,10 @@ static av_cold int vpx_init(AVCodecContext *avctx, vpx_img_wrap(&ctx->rawimg, VPX_IMG_FMT_I420, avctx->width, avctx->height, 1, (unsigned char*)1); + if (ctx->is_alpha) + vpx_img_wrap(&ctx->rawimg_alpha, VPX_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"); @@ -415,6 +442,7 @@ static av_cold int vpx_init(AVCodecContext *avctx, static inline void cx_pktcpy(struct FrameListData *dst, const struct vpx_codec_cx_pkt *src, + const struct vpx_codec_cx_pkt *src_alpha, VP8Context *ctx) { dst->pts = src->data.frame.pts; @@ -438,6 +466,14 @@ static inline void cx_pktcpy(struct FrameListData *dst, } else { dst->frame_number = -1; /* sanity marker */ } + if (src_alpha) { + dst->buf_alpha = src_alpha->data.frame.buf; + dst->sz_alpha = src_alpha->data.frame.sz; + } + else { + dst->buf_alpha = NULL; + dst->sz_alpha = 0; + } } /** @@ -451,6 +487,7 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, AVPacket *pkt, AVFrame *coded_frame) { int ret = ff_alloc_packet2(avctx, pkt, cx_frame->sz); + uint8_t *side_data; if (ret >= 0) { memcpy(pkt->data, cx_frame->buf, pkt->size); pkt->pts = pkt->dts = cx_frame->pts; @@ -475,6 +512,18 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, } cx_frame->have_sse = 0; } + if (cx_frame->sz_alpha > 0) { + side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + cx_frame->sz_alpha + 8); + if(side_data == NULL) { + av_free_packet(pkt); + av_free(pkt); + return AVERROR(ENOMEM); + } + AV_WB64(side_data, 1); + memcpy(side_data + 8, cx_frame->buf_alpha, cx_frame->sz_alpha); + } } else { return ret; } @@ -494,7 +543,9 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out, { VP8Context *ctx = avctx->priv_data; const struct vpx_codec_cx_pkt *pkt; + const struct vpx_codec_cx_pkt *pkt_alpha = NULL; const void *iter = NULL; + const void *iter_alpha = NULL; int size = 0; if (ctx->coded_frame_list) { @@ -509,7 +560,9 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out, /* 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))) { + while ((pkt = vpx_codec_get_cx_data(&ctx->encoder, &iter)) && + (!ctx->is_alpha || + (ctx->is_alpha && (pkt_alpha = vpx_codec_get_cx_data(&ctx->encoder_alpha, &iter_alpha))))) { switch (pkt->kind) { case VPX_CODEC_CX_FRAME_PKT: if (!size) { @@ -518,7 +571,7 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out, /* avoid storing the frame when the list is empty and we haven't yet provided a frame for output */ av_assert0(!ctx->coded_frame_list); - cx_pktcpy(&cx_frame, pkt, ctx); + cx_pktcpy(&cx_frame, pkt, pkt_alpha, ctx); size = storeframe(avctx, &cx_frame, pkt_out, coded_frame); if (size < 0) return size; @@ -531,7 +584,7 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out, "Frame queue element alloc failed\n"); return AVERROR(ENOMEM); } - cx_pktcpy(cx_frame, pkt, ctx); + cx_pktcpy(cx_frame, pkt, pkt_alpha, ctx); cx_frame->buf = av_malloc(cx_frame->sz); if (!cx_frame->buf) { @@ -542,6 +595,17 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out, return AVERROR(ENOMEM); } memcpy(cx_frame->buf, pkt->data.frame.buf, pkt->data.frame.sz); + if (ctx->is_alpha) { + cx_frame->buf_alpha = av_malloc(cx_frame->sz_alpha); + if (!cx_frame->buf_alpha) { + av_log(avctx, AV_LOG_ERROR, + "Data buffer alloc (%zu bytes) failed\n", + cx_frame->sz_alpha); + av_free(cx_frame); + return AVERROR(ENOMEM); + } + memcpy(cx_frame->buf_alpha, pkt_alpha->data.frame.buf, pkt_alpha->data.frame.sz); + } coded_frame_add(&ctx->coded_frame_list, cx_frame); } break; @@ -580,6 +644,7 @@ static int vp8_encode(AVCodecContext *avctx, AVPacket *pkt, { VP8Context *ctx = avctx->priv_data; struct vpx_image *rawimg = NULL; + struct vpx_image *rawimg_alpha = NULL; int64_t timestamp = 0; int res, coded_size; vpx_enc_frame_flags_t flags = 0; @@ -592,6 +657,17 @@ static int vp8_encode(AVCodecContext *avctx, AVPacket *pkt, rawimg->stride[VPX_PLANE_Y] = frame->linesize[0]; rawimg->stride[VPX_PLANE_U] = frame->linesize[1]; rawimg->stride[VPX_PLANE_V] = frame->linesize[2]; + if (ctx->is_alpha) { + uint8_t *u_plane, *v_plane; + rawimg_alpha = &ctx->rawimg_alpha; + rawimg_alpha->planes[VPX_PLANE_Y] = frame->data[3]; + u_plane = av_malloc(frame->linesize[1] * frame->height); + memset(u_plane, 0x80, frame->linesize[1] * frame->height); + rawimg_alpha->planes[VPX_PLANE_U] = u_plane; + v_plane = av_malloc(frame->linesize[2] * frame->height); + memset(v_plane, 0x80, frame->linesize[2] * frame->height); + rawimg_alpha->planes[VPX_PLANE_V] = v_plane; + } timestamp = frame->pts; if (frame->pict_type == AV_PICTURE_TYPE_I) flags |= VPX_EFLAG_FORCE_KF; @@ -603,6 +679,16 @@ static int vp8_encode(AVCodecContext *avctx, AVPacket *pkt, log_encoder_error(avctx, "Error encoding frame"); return AVERROR_INVALIDDATA; } + + if (ctx->is_alpha) { + res = vpx_codec_encode(&ctx->encoder_alpha, rawimg_alpha, timestamp, + avctx->ticks_per_frame, flags, ctx->deadline); + if (res != VPX_CODEC_OK) { + log_encoder_error(avctx, "Error encoding alpha frame"); + return AVERROR_INVALIDDATA; + } + } + coded_size = queue_frames(avctx, pkt, avctx->coded_frame); if (!frame && avctx->flags & CODEC_FLAG_PASS1) { @@ -618,6 +704,11 @@ static int vp8_encode(AVCodecContext *avctx, AVPacket *pkt, ctx->twopass_stats.sz); } + if (rawimg_alpha) { + av_free(rawimg_alpha->planes[VPX_PLANE_U]); + av_free(rawimg_alpha->planes[VPX_PLANE_V]); + } + *got_packet = !!coded_size; return 0; } @@ -692,7 +783,7 @@ AVCodec ff_libvpx_vp8_encoder = { .encode2 = vp8_encode, .close = vp8_free, .capabilities = CODEC_CAP_DELAY | CODEC_CAP_AUTO_THREADS, - .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_NONE }, .long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"), .priv_class = &class_vp8, .defaults = defaults, diff --git a/libavformat/mux.c b/libavformat/mux.c index 9de8e69050..9b4b0b1249 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -584,6 +584,7 @@ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, #endif pkt->buf = NULL; av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-allocated memory + av_copy_packet_side_data(&this_pktl->pkt, &this_pktl->pkt); // copy side data if (s->streams[pkt->stream_index]->last_in_packet_buffer) { next_point = &(st->last_in_packet_buffer->next); |