* 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"
UtvideoContext *c = avctx->priv_data;
int i;
- av_freep(&avctx->coded_frame);
av_freep(&c->slice_bits);
for (i = 0; i < 4; i++)
av_freep(&c->slice_buffer[i]);
static av_cold int utvideo_encode_init(AVCodecContext *avctx)
{
UtvideoContext *c = avctx->priv_data;
- int i;
+ 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);
+ }
- if (!avctx->coded_frame) {
- av_log(avctx, AV_LOG_ERROR, "Could not allocate frame.\n");
- utvideo_encode_close(avctx);
- return AVERROR(ENOMEM);
+ /* 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);
}
/* extradata size is 4 * 32bit */
}
for (i = 0; i < c->planes; i++) {
- c->slice_buffer[i] = av_malloc(avctx->width * (avctx->height + 1) +
+ 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");
/*
* 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 *dst[4], 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;
- int k = width;
+ int k = 2 * dst_stride;
unsigned int g;
for (j = 0; j < height; j++) {
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;
}
}
}
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;
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:
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(c->slice_buffer, 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, c->slice_buffer[i] + width,
- c->slice_buffer[i], 1, width,
+ 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[0], 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[0], 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;
+#if FF_API_CODED_FRAME
+FF_DISABLE_DEPRECATION_WARNINGS
avctx->coded_frame->key_frame = 1;
avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
pkt->size = bytestream2_tell_p(&pb);
pkt->flags |= AV_PKT_FLAG_KEY;
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"),
};