ffmpeg/libavcodec/vaapi_encode_mjpeg.c
U. Artie Eoff f70c397456 vaapi_encode_mjpeg: fix bad component id bug
The compound literals assigned to "components"
only exist within the scope of the if/else
block (thanks Mark Thompson for the better
explanation).

Thus, after this if/else block, "components"
ends up pointing to an arbitrary/undefined
array.  With some compilers and depending on
optimization settings, these arbitrary values
may end up being the same value (i.e. 0 with
GNU GCC 9.x).  Unfortunately, the GNU GCC
compiler, at least, never prints any warnings
about this.

This patch fixes this issue by assigning the
constant arrays to local variables at function
scope and then pointing "components" to those
as necessary.

Fixes #7915

Signed-off-by: U. Artie Eoff <ullysses.a.eoff@intel.com>
2019-06-12 22:27:13 +01:00

575 lines
18 KiB
C

/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <va/va.h>
#include <va/va_enc_jpeg.h>
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avcodec.h"
#include "bytestream.h"
#include "cbs.h"
#include "cbs_jpeg.h"
#include "internal.h"
#include "jpegtables.h"
#include "mjpeg.h"
#include "put_bits.h"
#include "vaapi_encode.h"
// Standard JPEG quantisation tables, in zigzag order.
static const unsigned char vaapi_encode_mjpeg_quant_luminance[64] = {
16, 11, 12, 14, 12, 10, 16, 14,
13, 14, 18, 17, 16, 19, 24, 40,
26, 24, 22, 22, 24, 49, 35, 37,
29, 40, 58, 51, 61, 60, 57, 51,
56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 55, 56, 80, 109, 81, 87,
95, 98, 103, 104, 103, 62, 77, 113,
121, 112, 100, 120, 92, 101, 103, 99,
};
static const unsigned char vaapi_encode_mjpeg_quant_chrominance[64] = {
17, 18, 18, 24, 21, 24, 47, 26,
26, 47, 99, 66, 56, 66, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
};
typedef struct VAAPIEncodeMJPEGContext {
VAAPIEncodeContext common;
// User options.
int jfif;
int huffman;
// Derived settings.
int quality;
uint8_t jfif_data[14];
// Writer structures.
JPEGRawFrameHeader frame_header;
JPEGRawScan scan;
JPEGRawApplicationData jfif_header;
JPEGRawQuantisationTableSpecification quant_tables;
JPEGRawHuffmanTableSpecification huffman_tables;
CodedBitstreamContext *cbc;
CodedBitstreamFragment current_fragment;
} VAAPIEncodeMJPEGContext;
static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice,
char *data, size_t *data_len)
{
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
CodedBitstreamFragment *frag = &priv->current_fragment;
int err;
if (priv->jfif) {
err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
JPEG_MARKER_APPN + 0,
&priv->jfif_header, NULL);
if (err < 0)
goto fail;
}
err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
JPEG_MARKER_DQT,
&priv->quant_tables, NULL);
if (err < 0)
goto fail;
err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
JPEG_MARKER_SOF0,
&priv->frame_header, NULL);
if (err < 0)
goto fail;
if (priv->huffman) {
err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
JPEG_MARKER_DHT,
&priv->huffman_tables, NULL);
if (err < 0)
goto fail;
}
err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
JPEG_MARKER_SOS,
&priv->scan, NULL);
if (err < 0)
goto fail;
err = ff_cbs_write_fragment_data(priv->cbc, frag);
if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to write image header.\n");
goto fail;
}
if (*data_len < 8 * frag->data_size) {
av_log(avctx, AV_LOG_ERROR, "Image header too large: "
"%zu < %zu.\n", *data_len, 8 * frag->data_size);
err = AVERROR(ENOSPC);
goto fail;
}
// Remove the EOI at the end of the fragment.
memcpy(data, frag->data, frag->data_size - 2);
*data_len = 8 * (frag->data_size - 2);
err = 0;
fail:
ff_cbs_fragment_reset(priv->cbc, frag);
return err;
}
static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
int index, int *type,
char *data, size_t *data_len)
{
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
int t, i, k;
if (index == 0) {
// Write quantisation tables.
JPEGRawFrameHeader *fh = &priv->frame_header;
JPEGRawQuantisationTableSpecification *dqt = &priv->quant_tables;
VAQMatrixBufferJPEG *quant;
if (*data_len < sizeof(*quant))
return AVERROR(ENOSPC);
*type = VAQMatrixBufferType;
*data_len = sizeof(*quant);
quant = (VAQMatrixBufferJPEG*)data;
memset(quant, 0, sizeof(*quant));
quant->load_lum_quantiser_matrix = 1;
for (i = 0; i < 64; i++)
quant->lum_quantiser_matrix[i] = dqt->table[fh->Tq[0]].Q[i];
if (fh->Nf > 1) {
quant->load_chroma_quantiser_matrix = 1;
for (i = 0; i < 64; i++)
quant->chroma_quantiser_matrix[i] =
dqt->table[fh->Tq[1]].Q[i];
}
} else if (index == 1) {
// Write huffman tables.
JPEGRawScanHeader *sh = &priv->scan.header;
JPEGRawHuffmanTableSpecification *dht = &priv->huffman_tables;
VAHuffmanTableBufferJPEGBaseline *huff;
if (*data_len < sizeof(*huff))
return AVERROR(ENOSPC);
*type = VAHuffmanTableBufferType;
*data_len = sizeof(*huff);
huff = (VAHuffmanTableBufferJPEGBaseline*)data;
memset(huff, 0, sizeof(*huff));
for (t = 0; t < 1 + (sh->Ns > 1); t++) {
const JPEGRawHuffmanTable *ht;
huff->load_huffman_table[t] = 1;
ht = &dht->table[2 * t];
for (i = k = 0; i < 16; i++)
k += (huff->huffman_table[t].num_dc_codes[i] = ht->L[i]);
av_assert0(k <= sizeof(huff->huffman_table[t].dc_values));
for (i = 0; i < k; i++)
huff->huffman_table[t].dc_values[i] = ht->V[i];
ht = &dht->table[2 * t + 1];
for (i = k = 0; i < 16; i++)
k += (huff->huffman_table[t].num_ac_codes[i] = ht->L[i]);
av_assert0(k <= sizeof(huff->huffman_table[t].ac_values));
for (i = 0; i < k; i++)
huff->huffman_table[t].ac_values[i] = ht->V[i];
}
} else {
return AVERROR_EOF;
}
return 0;
}
static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
JPEGRawFrameHeader *fh = &priv->frame_header;
JPEGRawScanHeader *sh = &priv->scan.header;
VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
const AVPixFmtDescriptor *desc;
const uint8_t components_rgb[3] = { 'R', 'G', 'B' };
const uint8_t components_yuv[3] = { 1, 2, 3 };
const uint8_t *components;
int t, i, quant_scale, len;
av_assert0(pic->type == PICTURE_TYPE_IDR);
desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);
av_assert0(desc);
if (desc->flags & AV_PIX_FMT_FLAG_RGB)
components = components_rgb;
else
components = components_yuv;
// Frame header.
fh->P = 8;
fh->Y = avctx->height;
fh->X = avctx->width;
fh->Nf = desc->nb_components;
for (i = 0; i < fh->Nf; i++) {
fh->C[i] = components[i];
fh->H[i] = 1 + (i == 0 ? desc->log2_chroma_w : 0);
fh->V[i] = 1 + (i == 0 ? desc->log2_chroma_h : 0);
fh->Tq[i] = !!i;
}
fh->Lf = 8 + 3 * fh->Nf;
// JFIF header.
if (priv->jfif) {
JPEGRawApplicationData *app = &priv->jfif_header;
AVRational sar = pic->input_image->sample_aspect_ratio;
int sar_w, sar_h;
PutByteContext pbc;
bytestream2_init_writer(&pbc, priv->jfif_data,
sizeof(priv->jfif_data));
bytestream2_put_buffer(&pbc, "JFIF", 5);
bytestream2_put_be16(&pbc, 0x0102);
bytestream2_put_byte(&pbc, 0);
av_reduce(&sar_w, &sar_h, sar.num, sar.den, 65535);
if (sar_w && sar_h) {
bytestream2_put_be16(&pbc, sar_w);
bytestream2_put_be16(&pbc, sar_h);
} else {
bytestream2_put_be16(&pbc, 1);
bytestream2_put_be16(&pbc, 1);
}
bytestream2_put_byte(&pbc, 0);
bytestream2_put_byte(&pbc, 0);
av_assert0(bytestream2_get_bytes_left_p(&pbc) == 0);
app->Lp = 2 + sizeof(priv->jfif_data);
app->Ap = priv->jfif_data;
app->Ap_ref = NULL;
}
// Quantisation tables.
if (priv->quality < 50)
quant_scale = 5000 / priv->quality;
else
quant_scale = 200 - 2 * priv->quality;
len = 2;
for (t = 0; t < 1 + (fh->Nf > 1); t++) {
JPEGRawQuantisationTable *quant = &priv->quant_tables.table[t];
const uint8_t *data = t == 0 ?
vaapi_encode_mjpeg_quant_luminance :
vaapi_encode_mjpeg_quant_chrominance;
quant->Pq = 0;
quant->Tq = t;
for (i = 0; i < 64; i++)
quant->Q[i] = av_clip(data[i] * quant_scale / 100, 1, 255);
len += 65;
}
priv->quant_tables.Lq = len;
// Huffman tables.
len = 2;
for (t = 0; t < 2 + 2 * (fh->Nf > 1); t++) {
JPEGRawHuffmanTable *huff = &priv->huffman_tables.table[t];
const uint8_t *lengths, *values;
int k;
switch (t) {
case 0:
lengths = avpriv_mjpeg_bits_dc_luminance + 1;
values = avpriv_mjpeg_val_dc;
break;
case 1:
lengths = avpriv_mjpeg_bits_ac_luminance + 1;
values = avpriv_mjpeg_val_ac_luminance;
break;
case 2:
lengths = avpriv_mjpeg_bits_dc_chrominance + 1;
values = avpriv_mjpeg_val_dc;
break;
case 3:
lengths = avpriv_mjpeg_bits_ac_chrominance + 1;
values = avpriv_mjpeg_val_ac_chrominance;
break;
}
huff->Tc = t % 2;
huff->Th = t / 2;
for (i = k = 0; i < 16; i++)
k += (huff->L[i] = lengths[i]);
for (i = 0; i < k; i++)
huff->V[i] = values[i];
len += 17 + k;
}
priv->huffman_tables.Lh = len;
// Scan header.
sh->Ns = fh->Nf;
for (i = 0; i < fh->Nf; i++) {
sh->Cs[i] = fh->C[i];
sh->Td[i] = i > 0;
sh->Ta[i] = i > 0;
}
sh->Ss = 0;
sh->Se = 63;
sh->Ah = 0;
sh->Al = 0;
sh->Ls = 6 + 2 * sh->Ns;
*vpic = (VAEncPictureParameterBufferJPEG) {
.reconstructed_picture = pic->recon_surface,
.coded_buf = pic->output_buffer,
.picture_width = fh->X,
.picture_height = fh->Y,
.pic_flags.bits = {
.profile = 0,
.progressive = 0,
.huffman = 1,
.interleaved = 0,
.differential = 0,
},
.sample_bit_depth = fh->P,
.num_scan = 1,
.num_components = fh->Nf,
// The driver modifies the provided quantisation tables according
// to this quality value; the middle value of 50 makes that the
// identity so that they are used unchanged.
.quality = 50,
};
for (i = 0; i < fh->Nf; i++) {
vpic->component_id[i] = fh->C[i];
vpic->quantiser_table_selector[i] = fh->Tq[i];
}
pic->nb_slices = 1;
return 0;
}
static int vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
JPEGRawScanHeader *sh = &priv->scan.header;
VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
int i;
*vslice = (VAEncSliceParameterBufferJPEG) {
.restart_interval = 0,
.num_components = sh->Ns,
};
for (i = 0; i < sh->Ns; i++) {
vslice->components[i].component_selector = sh->Cs[i];
vslice->components[i].dc_table_selector = sh->Td[i];
vslice->components[i].ac_table_selector = sh->Ta[i];
}
return 0;
}
static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
int err;
priv->quality = ctx->rc_quality;
if (priv->quality < 1 || priv->quality > 100) {
av_log(avctx, AV_LOG_ERROR, "Invalid quality value %d "
"(must be 1-100).\n", priv->quality);
return AVERROR(EINVAL);
}
// Hack: the implementation calls the JPEG image header (which we
// will use in the same way as a slice header) generic "raw data".
// Therefore, if after the packed header capability check we have
// PACKED_HEADER_RAW_DATA available, rewrite it as
// PACKED_HEADER_SLICE so that the header-writing code can do the
// right thing.
if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_RAW_DATA) {
ctx->va_packed_headers &= ~VA_ENC_PACKED_HEADER_RAW_DATA;
ctx->va_packed_headers |= VA_ENC_PACKED_HEADER_SLICE;
}
err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_MJPEG, avctx);
if (err < 0)
return err;
return 0;
}
static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 1, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 3, 1, 1, VAProfileJPEGBaseline },
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 3, 1, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 3, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_UNKNOWN }
};
static const VAAPIEncodeType vaapi_encode_type_mjpeg = {
.profiles = vaapi_encode_mjpeg_profiles,
.flags = FLAG_CONSTANT_QUALITY_ONLY |
FLAG_INTRA_ONLY,
.configure = &vaapi_encode_mjpeg_configure,
.default_quality = 80,
.picture_params_size = sizeof(VAEncPictureParameterBufferJPEG),
.init_picture_params = &vaapi_encode_mjpeg_init_picture_params,
.slice_params_size = sizeof(VAEncSliceParameterBufferJPEG),
.init_slice_params = &vaapi_encode_mjpeg_init_slice_params,
.slice_header_type = VAEncPackedHeaderRawData,
.write_slice_header = &vaapi_encode_mjpeg_write_image_header,
.write_extra_buffer = &vaapi_encode_mjpeg_write_extra_buffer,
};
static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
ctx->codec = &vaapi_encode_type_mjpeg;
// The JPEG image header - see note above.
ctx->desired_packed_headers =
VA_ENC_PACKED_HEADER_RAW_DATA;
ctx->surface_width = FFALIGN(avctx->width, 8);
ctx->surface_height = FFALIGN(avctx->height, 8);
return ff_vaapi_encode_init(avctx);
}
static av_cold int vaapi_encode_mjpeg_close(AVCodecContext *avctx)
{
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
ff_cbs_fragment_free(priv->cbc, &priv->current_fragment);
ff_cbs_close(&priv->cbc);
return ff_vaapi_encode_close(avctx);
}
#define OFFSET(x) offsetof(VAAPIEncodeMJPEGContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_mjpeg_options[] = {
VAAPI_ENCODE_COMMON_OPTIONS,
{ "jfif", "Include JFIF header",
OFFSET(jfif), AV_OPT_TYPE_BOOL,
{ .i64 = 0 }, 0, 1, FLAGS },
{ "huffman", "Include huffman tables",
OFFSET(huffman), AV_OPT_TYPE_BOOL,
{ .i64 = 1 }, 0, 1, FLAGS },
{ NULL },
};
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "b", "0" },
{ NULL },
};
static const AVClass vaapi_encode_mjpeg_class = {
.class_name = "mjpeg_vaapi",
.item_name = av_default_item_name,
.option = vaapi_encode_mjpeg_options,
.version = LIBAVUTIL_VERSION_INT,
};
AVCodec ff_mjpeg_vaapi_encoder = {
.name = "mjpeg_vaapi",
.long_name = NULL_IF_CONFIG_SMALL("MJPEG (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MJPEG,
.priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
.init = &vaapi_encode_mjpeg_init,
.send_frame = &ff_vaapi_encode_send_frame,
.receive_packet = &ff_vaapi_encode_receive_packet,
.close = &vaapi_encode_mjpeg_close,
.priv_class = &vaapi_encode_mjpeg_class,
.capabilities = AV_CODEC_CAP_HARDWARE |
AV_CODEC_CAP_INTRA_ONLY,
.defaults = vaapi_encode_mjpeg_defaults,
.pix_fmts = (const enum AVPixelFormat[]) {
AV_PIX_FMT_VAAPI,
AV_PIX_FMT_NONE,
},
.wrapper_name = "vaapi",
};