* Ut Video encoder
*/
+#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "avcodec.h"
#include "internal.h"
+#include "bswapdsp.h"
#include "bytestream.h"
#include "put_bits.h"
-#include "dsputil.h"
+#include "huffyuvencdsp.h"
#include "mathops.h"
#include "utvideo.h"
+#include "huffman.h"
/* Compare huffentry symbols */
static int huff_cmp_sym(const void *a, const void *b)
static av_cold int utvideo_encode_close(AVCodecContext *avctx)
{
UtvideoContext *c = avctx->priv_data;
+ int i;
av_freep(&avctx->coded_frame);
av_freep(&c->slice_bits);
- av_freep(&c->slice_buffer);
+ for (i = 0; i < 4; i++)
+ av_freep(&c->slice_buffer[i]);
return 0;
}
static av_cold int utvideo_encode_init(AVCodecContext *avctx)
{
UtvideoContext *c = avctx->priv_data;
-
+ int i, subsampled_height;
uint32_t original_format;
c->avctx = avctx;
c->frame_info_size = 4;
+ c->slice_stride = FFALIGN(avctx->width, 32);
switch (avctx->pix_fmt) {
- case PIX_FMT_RGB24:
+ case AV_PIX_FMT_RGB24:
c->planes = 3;
avctx->codec_tag = MKTAG('U', 'L', 'R', 'G');
original_format = UTVIDEO_RGB;
break;
- case PIX_FMT_RGBA:
+ case AV_PIX_FMT_RGBA:
c->planes = 4;
avctx->codec_tag = MKTAG('U', 'L', 'R', 'A');
original_format = UTVIDEO_RGBA;
break;
- case PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV420P:
if (avctx->width & 1 || avctx->height & 1) {
av_log(avctx, AV_LOG_ERROR,
"4:2:0 video requires even width and height.\n");
return AVERROR_INVALIDDATA;
}
c->planes = 3;
- avctx->codec_tag = MKTAG('U', 'L', 'Y', '0');
+ if (avctx->colorspace == AVCOL_SPC_BT709)
+ avctx->codec_tag = MKTAG('U', 'L', 'H', '0');
+ else
+ avctx->codec_tag = MKTAG('U', 'L', 'Y', '0');
original_format = UTVIDEO_420;
break;
- case PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV422P:
if (avctx->width & 1) {
av_log(avctx, AV_LOG_ERROR,
"4:2:2 video requires even width.\n");
return AVERROR_INVALIDDATA;
}
c->planes = 3;
- avctx->codec_tag = MKTAG('U', 'L', 'Y', '2');
+ if (avctx->colorspace == AVCOL_SPC_BT709)
+ avctx->codec_tag = MKTAG('U', 'L', 'H', '2');
+ else
+ avctx->codec_tag = MKTAG('U', 'L', 'Y', '2');
original_format = UTVIDEO_422;
break;
default:
return AVERROR_INVALIDDATA;
}
- ff_dsputil_init(&c->dsp, avctx);
+ ff_bswapdsp_init(&c->bdsp);
+ ff_huffyuvencdsp_init(&c->hdsp);
/* Check the prediction method, and error out if unsupported */
if (avctx->prediction_method < 0 || avctx->prediction_method > 4) {
return AVERROR_OPTION_NOT_FOUND;
}
- avctx->coded_frame = avcodec_alloc_frame();
+ /*
+ * Check the asked slice count for obviously invalid
+ * values (> 256 or negative).
+ */
+ if (avctx->slices > 256 || avctx->slices < 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Slice count %d is not supported in Ut Video (theoretical range is 0-256).\n",
+ avctx->slices);
+ return AVERROR(EINVAL);
+ }
+
+ /* Check that the slice count is not larger than the subsampled height */
+ subsampled_height = avctx->height >> av_pix_fmt_desc_get(avctx->pix_fmt)->log2_chroma_h;
+ if (avctx->slices > subsampled_height) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Slice count %d is larger than the subsampling-applied height %d.\n",
+ avctx->slices, subsampled_height);
+ return AVERROR(EINVAL);
+ }
+
+ avctx->coded_frame = av_frame_alloc();
if (!avctx->coded_frame) {
av_log(avctx, AV_LOG_ERROR, "Could not allocate frame.\n");
return AVERROR(ENOMEM);
}
- c->slice_buffer = av_malloc(avctx->width * avctx->height +
- FF_INPUT_BUFFER_PADDING_SIZE);
-
- if (!c->slice_buffer) {
- av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer 1.\n");
- utvideo_encode_close(avctx);
- return AVERROR(ENOMEM);
+ for (i = 0; i < c->planes; i++) {
+ c->slice_buffer[i] = av_malloc(c->slice_stride * (avctx->height + 2) +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!c->slice_buffer[i]) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer 1.\n");
+ utvideo_encode_close(avctx);
+ return AVERROR(ENOMEM);
+ }
}
/*
/*
* Set how many slices are going to be used.
- * Set one slice for now.
+ * By default uses multiple slices depending on the subsampled height.
+ * This enables multithreading in the official decoder.
*/
- c->slices = 1;
+ if (!avctx->slices) {
+ c->slices = subsampled_height / 120;
+
+ if (!c->slices)
+ c->slices = 1;
+ else if (c->slices > 256)
+ c->slices = 256;
+ } else {
+ c->slices = avctx->slices;
+ }
/* Set compression mode */
c->compression = COMP_HUFF;
return 0;
}
-static void mangle_rgb_planes(uint8_t *src, int step, int stride, int width,
- int height)
+static void mangle_rgb_planes(uint8_t *dst[4], int dst_stride, uint8_t *src,
+ int step, int stride, int width, int height)
{
int i, j;
- uint8_t r, g, b;
+ int k = 2 * dst_stride;
+ unsigned int g;
for (j = 0; j < height; j++) {
- for (i = 0; i < width * step; i += step) {
- r = src[i];
- g = src[i + 1];
- b = src[i + 2];
-
- src[i] = r - g + 0x80;
- src[i + 2] = b - g + 0x80;
+ if (step == 3) {
+ for (i = 0; i < width * step; i += step) {
+ g = src[i + 1];
+ dst[0][k] = g;
+ g += 0x80;
+ dst[1][k] = src[i + 2] - g;
+ dst[2][k] = src[i + 0] - g;
+ k++;
+ }
+ } else {
+ for (i = 0; i < width * step; i += step) {
+ g = src[i + 1];
+ dst[0][k] = g;
+ g += 0x80;
+ dst[1][k] = src[i + 2] - g;
+ dst[2][k] = src[i + 0] - g;
+ dst[3][k] = src[i + 3];
+ k++;
+ }
}
- src += stride;
- }
-}
-
-/* Write data to a plane, no prediction applied */
-static void write_plane(uint8_t *src, uint8_t *dst, int step, int stride,
- int width, int height)
-{
- int i, j;
-
- for (j = 0; j < height; j++) {
- for (i = 0; i < width * step; i += step)
- *dst++ = src[i];
-
+ k += dst_stride - width;
src += stride;
}
}
/* Write data to a plane with left prediction */
-static void left_predict(uint8_t *src, uint8_t *dst, int step, int stride,
+static void left_predict(uint8_t *src, uint8_t *dst, int stride,
int width, int height)
{
int i, j;
prev = 0x80; /* Set the initial value */
for (j = 0; j < height; j++) {
- for (i = 0; i < width * step; i += step) {
+ for (i = 0; i < width; i++) {
*dst++ = src[i] - prev;
prev = src[i];
}
}
/* Write data to a plane with median prediction */
-static void median_predict(uint8_t *src, uint8_t *dst, int step, int stride,
+static void median_predict(UtvideoContext *c, uint8_t *src, uint8_t *dst, int stride,
int width, int height)
{
int i, j;
- int A, B, C;
+ int A, B;
uint8_t prev;
/* First line uses left neighbour prediction */
prev = 0x80; /* Set the initial value */
- for (i = 0; i < width * step; i += step) {
+ for (i = 0; i < width; i++) {
*dst++ = src[i] - prev;
prev = src[i];
}
* Second line uses top prediction for the first sample,
* and median for the rest.
*/
- C = src[-stride];
- *dst++ = src[0] - C;
- A = src[0];
- for (i = step; i < width * step; i += step) {
- B = src[i - stride];
- *dst++ = src[i] - mid_pred(A, B, (A + B - C) & 0xFF);
- C = B;
- A = src[i];
- }
-
- src += stride;
+ A = B = 0;
/* Rest of the coded part uses median prediction */
- for (j = 2; j < height; j++) {
- for (i = 0; i < width * step; i += step) {
- B = src[i - stride];
- *dst++ = src[i] - mid_pred(A, B, (A + B - C) & 0xFF);
- C = B;
- A = src[i];
- }
+ for (j = 1; j < height; j++) {
+ c->hdsp.sub_hfyu_median_pred(dst, src - stride, src, width, &A, &B);
+ dst += width;
src += stride;
}
}
/* Count the usage of values in a plane */
static void count_usage(uint8_t *src, int width,
- int height, uint32_t *counts)
+ int height, uint64_t *counts)
{
int i, j;
}
}
-static uint32_t add_weights(uint32_t w1, uint32_t w2)
-{
- uint32_t max = (w1 & 0xFF) > (w2 & 0xFF) ? (w1 & 0xFF) : (w2 & 0xFF);
-
- return ((w1 & 0xFFFFFF00) + (w2 & 0xFFFFFF00)) | (1 + max);
-}
-
-static void up_heap(uint32_t val, uint32_t *heap, uint32_t *weights)
-{
- uint32_t initial_val = heap[val];
-
- while (weights[initial_val] < weights[heap[val >> 1]]) {
- heap[val] = heap[val >> 1];
- val >>= 1;
- }
-
- heap[val] = initial_val;
-}
-
-static void down_heap(uint32_t nr_heap, uint32_t *heap, uint32_t *weights)
-{
- uint32_t val = 1;
- uint32_t val2;
- uint32_t initial_val = heap[val];
-
- while (1) {
- val2 = val << 1;
-
- if (val2 > nr_heap)
- break;
-
- if (val2 < nr_heap && weights[heap[val2 + 1]] < weights[heap[val2]])
- val2++;
-
- if (weights[initial_val] < weights[heap[val2]])
- break;
-
- heap[val] = heap[val2];
-
- val = val2;
- }
-
- heap[val] = initial_val;
-}
-
-/* Calculate the huffman code lengths from value counts */
-static void calculate_code_lengths(uint8_t *lengths, uint32_t *counts)
-{
- uint32_t nr_nodes, nr_heap, node1, node2;
- int i, j;
- int32_t k;
-
- /* Heap and node entries start from 1 */
- uint32_t weights[512];
- uint32_t heap[512];
- int32_t parents[512];
-
- /* Set initial weights */
- for (i = 0; i < 256; i++)
- weights[i + 1] = (counts[i] ? counts[i] : 1) << 8;
-
- nr_nodes = 256;
- nr_heap = 0;
-
- heap[0] = 0;
- weights[0] = 0;
- parents[0] = -2;
-
- /* Create initial nodes */
- for (i = 1; i <= 256; i++) {
- parents[i] = -1;
-
- heap[++nr_heap] = i;
- up_heap(nr_heap, heap, weights);
- }
-
- /* Build the tree */
- while (nr_heap > 1) {
- node1 = heap[1];
- heap[1] = heap[nr_heap--];
-
- down_heap(nr_heap, heap, weights);
-
- node2 = heap[1];
- heap[1] = heap[nr_heap--];
-
- down_heap(nr_heap, heap, weights);
-
- nr_nodes++;
-
- parents[node1] = parents[node2] = nr_nodes;
- weights[nr_nodes] = add_weights(weights[node1], weights[node2]);
- parents[nr_nodes] = -1;
-
- heap[++nr_heap] = nr_nodes;
-
- up_heap(nr_heap, heap, weights);
- }
-
- /* Generate lengths */
- for (i = 1; i <= 256; i++) {
- j = 0;
- k = i;
-
- while (parents[k] >= 0) {
- k = parents[k];
- j++;
- }
-
- lengths[i - 1] = j;
- }
-}
-
/* Calculate the actual huffman codes from the code lengths */
static void calculate_codes(HuffEntry *he)
{
}
static int encode_plane(AVCodecContext *avctx, uint8_t *src,
- uint8_t *dst, int step, int stride,
+ uint8_t *dst, int stride,
int width, int height, PutByteContext *pb)
{
UtvideoContext *c = avctx->priv_data;
uint8_t lengths[256];
- uint32_t counts[256] = { 0 };
+ uint64_t counts[256] = { 0 };
HuffEntry he[256];
for (i = 0; i < c->slices; i++) {
sstart = send;
send = height * (i + 1) / c->slices;
- write_plane(src + sstart * stride, dst + sstart * width,
- step, stride, width, send - sstart);
+ av_image_copy_plane(dst + sstart * width, width,
+ src + sstart * stride, stride,
+ width, send - sstart);
}
break;
case PRED_LEFT:
sstart = send;
send = height * (i + 1) / c->slices;
left_predict(src + sstart * stride, dst + sstart * width,
- step, stride, width, send - sstart);
+ stride, width, send - sstart);
}
break;
case PRED_MEDIAN:
for (i = 0; i < c->slices; i++) {
sstart = send;
send = height * (i + 1) / c->slices;
- median_predict(src + sstart * stride, dst + sstart * width,
- step, stride, width, send - sstart);
+ median_predict(c, src + sstart * stride, dst + sstart * width,
+ stride, width, send - sstart);
}
break;
default:
}
/* Calculate huffman lengths */
- calculate_code_lengths(lengths, counts);
+ ff_huff_gen_len_table(lengths, counts);
/*
* Write the plane's header into the output packet:
slice_len = offset - slice_len;
/* Byteswap the written huffman codes */
- c->dsp.bswap_buf((uint32_t *) c->slice_bits,
- (uint32_t *) c->slice_bits,
- slice_len >> 2);
+ c->bdsp.bswap_buf((uint32_t *) c->slice_bits,
+ (uint32_t *) c->slice_bits,
+ slice_len >> 2);
/* Write the offset to the stream */
bytestream2_put_le32(pb, offset);
}
/* In case of RGB, mangle the planes to Ut Video's format */
- if (avctx->pix_fmt == PIX_FMT_RGBA || avctx->pix_fmt == PIX_FMT_RGB24)
- mangle_rgb_planes(pic->data[0], c->planes, pic->linesize[0], width,
- height);
+ if (avctx->pix_fmt == AV_PIX_FMT_RGBA || avctx->pix_fmt == AV_PIX_FMT_RGB24)
+ mangle_rgb_planes(c->slice_buffer, c->slice_stride, pic->data[0],
+ c->planes, pic->linesize[0], width, height);
/* Deal with the planes */
switch (avctx->pix_fmt) {
- case PIX_FMT_RGB24:
- case PIX_FMT_RGBA:
+ case AV_PIX_FMT_RGB24:
+ case AV_PIX_FMT_RGBA:
for (i = 0; i < c->planes; i++) {
- ret = encode_plane(avctx, pic->data[0] + ff_ut_rgb_order[i],
- c->slice_buffer, c->planes, pic->linesize[0],
+ ret = encode_plane(avctx, c->slice_buffer[i] + 2 * c->slice_stride,
+ c->slice_buffer[i], c->slice_stride,
width, height, &pb);
if (ret) {
}
}
break;
- case PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV422P:
for (i = 0; i < c->planes; i++) {
- ret = encode_plane(avctx, pic->data[i], c->slice_buffer, 1,
+ ret = encode_plane(avctx, pic->data[i], c->slice_buffer[0],
pic->linesize[i], width >> !!i, height, &pb);
if (ret) {
}
}
break;
- case PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV420P:
for (i = 0; i < c->planes; i++) {
- ret = encode_plane(avctx, pic->data[i], c->slice_buffer, 1,
+ ret = encode_plane(avctx, pic->data[i], c->slice_buffer[0],
pic->linesize[i], width >> !!i, height >> !!i,
&pb);
* At least currently Ut Video is IDR only.
* Set flags accordingly.
*/
- avctx->coded_frame->reference = 0;
avctx->coded_frame->key_frame = 1;
avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
AVCodec ff_utvideo_encoder = {
.name = "utvideo",
+ .long_name = NULL_IF_CONFIG_SMALL("Ut Video"),
.type = AVMEDIA_TYPE_VIDEO,
- .id = CODEC_ID_UTVIDEO,
+ .id = AV_CODEC_ID_UTVIDEO,
.priv_data_size = sizeof(UtvideoContext),
.init = utvideo_encode_init,
.encode2 = utvideo_encode_frame,
.close = utvideo_encode_close,
- .pix_fmts = (const enum PixelFormat[]) {
- PIX_FMT_RGB24, PIX_FMT_RGBA, PIX_FMT_YUV422P,
- PIX_FMT_YUV420P, PIX_FMT_NONE
+ .pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_YUV422P,
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE
},
- .long_name = NULL_IF_CONFIG_SMALL("Ut Video"),
};