Message ID | 20180607234331.32139-25-sw@jkqxz.net |
---|---|
State | Superseded |
Headers | show |
On Fri, Jun 8, 2018 at 7:46 AM Mark Thompson <sw@jkqxz.net> wrote: > > --- > configure | 2 + > libavcodec/Makefile | 1 + > libavcodec/cbs.c | 6 + > libavcodec/cbs_internal.h | 1 + > libavcodec/cbs_jpeg.c | 513 ++++++++++++++++++++++++++++++++++ > libavcodec/cbs_jpeg.h | 128 +++++++++ > libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++ > 7 files changed, 842 insertions(+) > create mode 100644 libavcodec/cbs_jpeg.c > create mode 100644 libavcodec/cbs_jpeg.h > create mode 100644 libavcodec/cbs_jpeg_syntax_template.c > > diff --git a/configure b/configure > index 790f55be14..d908283954 100755 > --- a/configure > +++ b/configure > @@ -2244,6 +2244,7 @@ CONFIG_EXTRA=" > cbs > cbs_h264 > cbs_h265 > + cbs_jpeg > cbs_mpeg2 > cbs_vp9 > dirac_parse > @@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST" > # subsystems > cbs_h264_select="cbs golomb" > cbs_h265_select="cbs golomb" > +cbs_jpeg_select="cbs" > cbs_mpeg2_select="cbs" > cbs_vp9_select="cbs" > dct_select="rdft" > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index 3ab071a039..2a1e0de110 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o > OBJS-$(CONFIG_CBS) += cbs.o > OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o > OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o > +OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o > OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o > OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o > OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c > index be6c043b58..bb3ce95971 100644 > --- a/libavcodec/cbs.c > +++ b/libavcodec/cbs.c > @@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = { > #if CONFIG_CBS_H265 > &ff_cbs_type_h265, > #endif > +#if CONFIG_CBS_JPEG > + &ff_cbs_type_jpeg, > +#endif > #if CONFIG_CBS_MPEG2 > &ff_cbs_type_mpeg2, > #endif > @@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = { > #if CONFIG_CBS_H265 > AV_CODEC_ID_H265, > #endif > +#if CONFIG_CBS_JPEG > + AV_CODEC_ID_MJPEG, > +#endif > #if CONFIG_CBS_MPEG2 > AV_CODEC_ID_MPEG2VIDEO, > #endif > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h > index 172b8a2515..e0e912e28e 100644 > --- a/libavcodec/cbs_internal.h > +++ b/libavcodec/cbs_internal.h > @@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, > > extern const CodedBitstreamType ff_cbs_type_h264; > extern const CodedBitstreamType ff_cbs_type_h265; > +extern const CodedBitstreamType ff_cbs_type_jpeg; > extern const CodedBitstreamType ff_cbs_type_mpeg2; > extern const CodedBitstreamType ff_cbs_type_vp9; > > diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c > new file mode 100644 > index 0000000000..365db73394 > --- /dev/null > +++ b/libavcodec/cbs_jpeg.c > @@ -0,0 +1,513 @@ > +/* > + * 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 "cbs.h" > +#include "cbs_internal.h" > +#include "cbs_jpeg.h" > + > + > +#define HEADER(name) do { \ > + ff_cbs_trace_header(ctx, name); \ > + } while (0) > + > +#define CHECK(call) do { \ > + err = (call); \ > + if (err < 0) \ > + return err; \ > + } while (0) > + > +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) > + > +#define u(width, name, range_min, range_max) \ > + xu(width, name, range_min, range_max, 0) > +#define us(width, name, sub, range_min, range_max) \ > + xu(width, name, range_min, range_max, 1, sub) > + > + > +#define READ > +#define READWRITE read > +#define RWContext GetBitContext > +#define FUNC(name) cbs_jpeg_read_ ## name > + > +#define xu(width, name, range_min, range_max, subs, ...) do { \ > + uint32_t value = range_min; \ > + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ > + SUBSCRIPTS(subs, __VA_ARGS__), \ > + &value, range_min, range_max)); \ > + current->name = value; \ > + } while (0) > + > +#include "cbs_jpeg_syntax_template.c" > + > +#undef READ > +#undef READWRITE > +#undef RWContext > +#undef FUNC > +#undef xu > + > +#define WRITE > +#define READWRITE write > +#define RWContext PutBitContext > +#define FUNC(name) cbs_jpeg_write_ ## name > + > +#define xu(width, name, range_min, range_max, subs, ...) do { \ > + uint32_t value = current->name; \ > + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ > + SUBSCRIPTS(subs, __VA_ARGS__), \ > + value, range_min, range_max)); \ > + } while (0) > + > + > +#include "cbs_jpeg_syntax_template.c" > + > +#undef READ > +#undef READWRITE > +#undef RWContext > +#undef FUNC > +#undef xu > + > + > +static void cbs_jpeg_free_application_data(void *unit, uint8_t *content) > +{ > + JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content; > + av_buffer_unref(&ad->Ap_ref); > + av_freep(&content); > +} > + > +static void cbs_jpeg_free_scan(void *unit, uint8_t *content) > +{ > + JPEGRawScan *scan = (JPEGRawScan*)content; > + av_buffer_unref(&scan->data_ref); > + av_freep(&content); > +} > + > +static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, > + CodedBitstreamFragment *frag, > + int header) > +{ > + AVBufferRef *data_ref; > + uint8_t *data; > + size_t data_size; > + int unit, start, end, marker, next_start, next_marker; > + int err, i, j, length; > + > + if (frag->data_size < 4) { > + // Definitely too short to be meaningful. > + return AVERROR_INVALIDDATA; > + } > + > + for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++); > + if (i > 0) { > + av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at " > + "beginning of image.\n", i); > + } > + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size && frag->data[i]) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "no SOI marker found.\n"); > + return AVERROR_INVALIDDATA; > + } > + marker = frag->data[i]; > + if (marker != JPEG_MARKER_SOI) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first " > + "marker is %02x, should be SOI.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "no image content found.\n"); > + return AVERROR_INVALIDDATA; > + } > + marker = frag->data[i]; > + start = i + 1; > + > + for (unit = 0;; unit++) { > + if (marker == JPEG_MARKER_EOI) { > + break; > + } else if (marker == JPEG_MARKER_SOS) { > + for (i = start; i + 1 < frag->data_size; i++) { > + if (frag->data[i] != 0xff) > + continue; > + end = i; > + for (++i; i + 1 < frag->data_size && > + frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + next_marker = -1; > + } else { > + if (frag->data[i] == 0x00) > + continue; > + next_marker = frag->data[i]; > + next_start = i + 1; > + } > + break; > + } > + } else { > + i = start; > + if (i + 2 > frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "truncated at %02x marker.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + length = AV_RB16(frag->data + i); > + if (i + length > frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "truncated at %02x marker segment.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + end = start + length; > + > + i = end; > + if (frag->data[i] != 0xff) { > + next_marker = -1; > + } else { > + for (++i; i + 1 < frag->data_size && > + frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + next_marker = -1; > + } else { > + next_marker = frag->data[i]; > + next_start = i + 1; > + } > + } > + } > + > + if (marker == JPEG_MARKER_SOS) { > + length = AV_RB16(frag->data + start); > + > + data_ref = NULL; > + data = av_malloc(end - start + > + AV_INPUT_BUFFER_PADDING_SIZE); > + if (!data) > + return AVERROR(ENOMEM); > + > + memcpy(data, frag->data + start, length); > + for (i = start + length, j = length; i < end; i++, j++) { > + if (frag->data[i] == 0xff) { > + while (frag->data[i] == 0xff) > + ++i; > + data[j] = 0xff; > + } else { > + data[j] = frag->data[i]; > + } > + } > + data_size = j; > + > + memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); > + > + } else { > + data = frag->data + start; > + data_size = end - start; > + data_ref = frag->data_ref; > + } > + > + err = ff_cbs_insert_unit_data(ctx, frag, unit, marker, > + data, data_size, data_ref); > + if (err < 0) { > + if (!data_ref) > + av_freep(&data); > + return err; > + } > + > + if (next_marker == -1) > + break; > + marker = next_marker; > + start = next_start; > + } > + > + return 0; > +} > + > +static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit) > +{ > + GetBitContext gbc; > + int err; > + > + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); > + if (err < 0) > + return err; > + > + if (unit->type >= JPEG_MARKER_SOF0 && > + unit->type <= JPEG_MARKER_SOF3) { > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawFrameHeader), > + NULL); > + if (err < 0) > + return err; > + > + err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content); > + if (err < 0) > + return err; > + > + } else if (unit->type >= JPEG_MARKER_APPN && > + unit->type <= JPEG_MARKER_APPN + 15) { > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawApplicationData), > + &cbs_jpeg_free_application_data); > + if (err < 0) > + return err; > + > + err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content); > + if (err < 0) > + return err; > + > + } else if (unit->type == JPEG_MARKER_SOS) { > + JPEGRawScan *scan; > + int pos; > + > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawScan), > + &cbs_jpeg_free_scan); > + if (err < 0) > + return err; > + scan = unit->content; > + > + err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header); > + if (err < 0) > + return err; > + > + pos = get_bits_count(&gbc); > + av_assert0(pos % 8 == 0); > + if (pos > 0) { > + scan->data_size = unit->data_size - pos / 8; > + scan->data_ref = av_buffer_ref(unit->data_ref); > + if (!scan->data_ref) > + return AVERROR(ENOMEM); > + scan->data = unit->data + pos / 8; > + } > + > + } else { > + switch (unit->type) { > +#define SEGMENT(marker, type, func) \ > + case JPEG_MARKER_ ## marker: \ > + { \ > + err = ff_cbs_alloc_unit_content(ctx, unit, \ > + sizeof(type), NULL); \ > + if (err < 0) \ > + return err; \ > + err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \ > + if (err < 0) \ > + return err; \ > + } \ > + break > + SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt); > + SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht); > + SEGMENT(COM, JPEGRawComment, comment); > +#undef SEGMENT > + default: > + return AVERROR(ENOSYS); > + } > + } > + > + return 0; > +} > + > +static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit, > + PutBitContext *pbc) > +{ > + JPEGRawScan *scan = unit->content; > + int i, err; > + > + err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header); > + if (err < 0) > + return err; > + > + if (scan->data) { > + if (scan->data_size * 8 > put_bits_left(pbc)) > + return AVERROR(ENOSPC); > + > + for (i = 0; i < scan->data_size; i++) > + put_bits(pbc, 8, scan->data[i]); > + } > + > + return 0; > +} > + > +static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit, > + PutBitContext *pbc) > +{ > + int err; > + > + if (unit->type >= JPEG_MARKER_SOF0 && > + unit->type <= JPEG_MARKER_SOF3) { > + err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content); > + } else if (unit->type >= JPEG_MARKER_APPN && > + unit->type <= JPEG_MARKER_APPN + 15) { > + err = cbs_jpeg_write_application_data(ctx, pbc, unit->content); > + } else { > + switch (unit->type) { > +#define SEGMENT(marker, func) \ > + case JPEG_MARKER_ ## marker: \ > + err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \ > + break; > + SEGMENT(DQT, dqt); > + SEGMENT(DHT, dht); > + SEGMENT(COM, comment); > + default: > + return AVERROR_PATCHWELCOME; > + } > + } > + > + return err; > +} > + > +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit) > +{ > + CodedBitstreamJPEGContext *priv = ctx->priv_data; > + PutBitContext pbc; > + int err; > + > + if (!priv->write_buffer) { > + // Initial write buffer size is 1MB. > + priv->write_buffer_size = 1024 * 1024; > + > + reallocate_and_try_again: > + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); > + if (err < 0) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " > + "sufficiently large write buffer (last attempt " > + "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); > + return err; > + } > + } > + > + init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); > + > + if (unit->type == JPEG_MARKER_SOS) > + err = cbs_jpeg_write_scan(ctx, unit, &pbc); > + else > + err = cbs_jpeg_write_segment(ctx, unit, &pbc); > + > + if (err == AVERROR(ENOSPC)) { > + // Overflow. > + priv->write_buffer_size *= 2; > + goto reallocate_and_try_again; > + } > + if (err < 0) { > + // Write failed for some other reason. > + return err; > + } > + > + if (put_bits_count(&pbc) % 8) > + unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; > + else > + unit->data_bit_padding = 0; > + > + unit->data_size = (put_bits_count(&pbc) + 7) / 8; > + flush_put_bits(&pbc); > + > + err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); > + if (err < 0) > + return err; > + > + memcpy(unit->data, priv->write_buffer, unit->data_size); > + > + return 0; > +} > + > +static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, > + CodedBitstreamFragment *frag) > +{ > + const CodedBitstreamUnit *unit; > + uint8_t *data; > + size_t size, dp, sp; > + int i; > + > + size = 4; // SOI + EOI. > + for (i = 0; i < frag->nb_units; i++) { > + unit = &frag->units[i]; > + size += 2 + unit->data_size; > + if (unit->type == JPEG_MARKER_SOS) { > + for (sp = 0; sp < unit->data_size; sp++) { > + if (unit->data[sp] == 0xff) > + ++size; > + } > + } > + } > + > + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); > + if (!frag->data_ref) > + return AVERROR(ENOMEM); > + data = frag->data_ref->data; > + > + dp = 0; > + > + data[dp++] = 0xff; > + data[dp++] = JPEG_MARKER_SOI; > + > + for (i = 0; i < frag->nb_units; i++) { > + unit = &frag->units[i]; > + > + data[dp++] = 0xff; > + data[dp++] = unit->type; > + > + if (unit->type != JPEG_MARKER_SOS) { > + memcpy(data + dp, unit->data, unit->data_size); > + dp += unit->data_size; > + } else { > + sp = AV_RB16(unit->data); > + av_assert0(sp <= unit->data_size); > + memcpy(data + dp, unit->data, sp); > + dp += sp; > + > + for (; sp < unit->data_size; sp++) { > + if (unit->data[sp] == 0xff) { > + data[dp++] = 0xff; > + data[dp++] = 0x00; > + } else { > + data[dp++] = unit->data[sp]; > + } > + } > + } > + } > + > + data[dp++] = 0xff; > + data[dp++] = JPEG_MARKER_EOI; > + > + av_assert0(dp == size); > + > + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); > + frag->data = data; > + frag->data_size = size; > + > + return 0; > +} > + > +static void cbs_jpeg_close(CodedBitstreamContext *ctx) > +{ > + CodedBitstreamJPEGContext *priv = ctx->priv_data; > + > + av_freep(&priv->write_buffer); > +} > + > +const CodedBitstreamType ff_cbs_type_jpeg = { > + .codec_id = AV_CODEC_ID_MJPEG, > + > + .priv_data_size = sizeof(CodedBitstreamJPEGContext), > + > + .split_fragment = &cbs_jpeg_split_fragment, > + .read_unit = &cbs_jpeg_read_unit, > + .write_unit = &cbs_jpeg_write_unit, > + .assemble_fragment = &cbs_jpeg_assemble_fragment, > + > + .close = &cbs_jpeg_close, > +}; > diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h > new file mode 100644 > index 0000000000..f427c4c616 > --- /dev/null > +++ b/libavcodec/cbs_jpeg.h > @@ -0,0 +1,128 @@ > +/* > + * 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 > + */ > + > +#ifndef AVCODEC_CBS_JPEG_H > +#define AVCODEC_CBS_JPEG_H > + > +#include <stddef.h> > +#include <stdint.h> > + > + missing "libavutil/buffer.h" for AVBufferRef *, use make checkheaders can reproduce the error > +enum { > + JPEG_MARKER_SOF0 = 0xc0, > + JPEG_MARKER_SOF1 = 0xc1, > + JPEG_MARKER_SOF2 = 0xc2, > + JPEG_MARKER_SOF3 = 0xc3, > + > + JPEG_MARKER_DHT = 0xc4, > + JPEG_MARKER_SOI = 0xd8, > + JPEG_MARKER_EOI = 0xd9, > + JPEG_MARKER_SOS = 0xda, > + JPEG_MARKER_DQT = 0xdb, > + > + JPEG_MARKER_APPN = 0xe0, > + JPEG_MARKER_JPGN = 0xf0, > + JPEG_MARKER_COM = 0xfe, > +}; > + > +enum { > + JPEG_MAX_COMPONENTS = 255, > + > + JPEG_MAX_HEIGHT = 65535, > + JPEG_MAX_WIDTH = 65535, > +}; > + > + > +typedef struct JPEGRawFrameHeader { > + uint16_t Lf; > + uint8_t P; > + uint16_t Y; > + uint16_t X; > + uint16_t Nf; > + > + uint8_t C [JPEG_MAX_COMPONENTS]; > + uint8_t H [JPEG_MAX_COMPONENTS]; > + uint8_t V [JPEG_MAX_COMPONENTS]; > + uint8_t Tq[JPEG_MAX_COMPONENTS]; > +} JPEGRawFrameHeader; > + > +typedef struct JPEGRawScanHeader { > + uint16_t Ls; > + uint8_t Ns; > + > + uint8_t Cs[JPEG_MAX_COMPONENTS]; > + uint8_t Td[JPEG_MAX_COMPONENTS]; > + uint8_t Ta[JPEG_MAX_COMPONENTS]; > + > + uint8_t Ss; > + uint8_t Se; > + uint8_t Ah; > + uint8_t Al; > +} JPEGRawScanHeader; > + > +typedef struct JPEGRawScan { > + JPEGRawScanHeader header; > + uint8_t *data; > + size_t data_size; > + AVBufferRef *data_ref; > +} JPEGRawScan; > + > +typedef struct JPEGRawQuantisationTable { > + uint8_t Pq; > + uint8_t Tq; > + uint16_t Q[64]; > +} JPEGRawQuantisationTable; > + > +typedef struct JPEGRawQuantisationTableSpecification { > + uint16_t Lq; > + JPEGRawQuantisationTable table[4]; > +} JPEGRawQuantisationTableSpecification; > + > +typedef struct JPEGRawHuffmanTable { > + uint8_t Tc; > + uint8_t Th; > + uint8_t L[16]; > + uint8_t V[224]; > +} JPEGRawHuffmanTable; > + > +typedef struct JPEGRawHuffmanTableSpecification { > + uint16_t Lh; > + JPEGRawHuffmanTable table[8]; > +} JPEGRawHuffmanTableSpecification; > + > +typedef struct JPEGRawApplicationData { > + uint16_t Lp; > + uint8_t *Ap; > + AVBufferRef *Ap_ref; > +} JPEGRawApplicationData; > + > +typedef struct JPEGRawComment { > + uint16_t Lc; > + uint8_t *Cm; > + AVBufferRef *Cm_ref; > +} JPEGRawComment; > + > + > +typedef struct CodedBitstreamJPEGContext { > + // Write buffer. > + uint8_t *write_buffer; > + size_t write_buffer_size; > +} CodedBitstreamJPEGContext; > + > + > +#endif /* AVCODEC_CBS_JPEG_H */ > diff --git a/libavcodec/cbs_jpeg_syntax_template.c b/libavcodec/cbs_jpeg_syntax_template.c > new file mode 100644 > index 0000000000..d3cd9ff62e > --- /dev/null > +++ b/libavcodec/cbs_jpeg_syntax_template.c > @@ -0,0 +1,191 @@ > +/* > + * 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 > + */ > + > +static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawFrameHeader *current) > +{ > + int err, i; > + > + HEADER("Frame Header"); > + > + u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS); > + > + u(8, P, 2, 16); > + u(16, Y, 0, JPEG_MAX_HEIGHT); > + u(16, X, 1, JPEG_MAX_WIDTH); > + u(8, Nf, 1, JPEG_MAX_COMPONENTS); > + > + for (i = 0; i < current->Nf; i++) { > + us(8, C[i], i, 0, JPEG_MAX_COMPONENTS); > + us(4, H[i], i, 1, 4); > + us(4, V[i], i, 1, 4); > + us(8, Tq[i], i, 0, 3); > + } > + > + return 0; > +} > + > +static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawQuantisationTable *current) > +{ > + int err, i; > + > + u(4, Pq, 0, 1); > + u(4, Tq, 0, 3); > + > + if (current->Pq) { > + for (i = 0; i < 64; i++) > + us(16, Q[i], i, 1, 255); > + } else { > + for (i = 0; i < 64; i++) > + us(8, Q[i], i, 1, 255); > + } > + > + return 0; > +} > + > +static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawQuantisationTableSpecification *current) > +{ > + int err, i, n; > + > + HEADER("Quantisation Tables"); > + > + u(16, Lq, 2, 2 + 4 * 65); > + n = current->Lq / 65; > + > + for (i = 0; i < n; i++) > + CHECK(FUNC(quantisation_table)(ctx, rw, ¤t->table[i])); > + > + return 0; > +} > + > +static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawHuffmanTable *current) > +{ > + int err, i, j, ij; > + > + u(4, Tc, 0, 1); > + u(4, Th, 0, 3); > + > + for (i = 0; i < 16; i++) > + us(8, L[i], i, 0, 224); > + > + ij = 0; > + for (i = 0; i < 16; i++) { > + for (j = 0; j < current->L[i]; j++) { > + us(8, V[ij], ij, 0, 255); > + ++ij; > + } > + } > + > + return 0; > +} > + > +static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawHuffmanTableSpecification *current) > +{ > + int err, i, j, n; > + > + HEADER("Huffman Tables"); > + > + u(16, Lh, 2, 2 + 8 * (1 + 16 + 256)); > + > + n = 2; > + for (i = 0; n < current->Lh; i++) { > + CHECK(FUNC(huffman_table)(ctx, rw, ¤t->table[i])); > + > + ++n; > + for (j = 0; j < 16; j++) > + n += 1 + current->table[i].L[j]; > + } > + > + return 0; > +} > + > +static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawScanHeader *current) > +{ > + int err, j; > + > + HEADER("Scan"); > + > + u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS); > + > + u(8, Ns, 1, 4); > + for (j = 0; j < current->Ns; j++) { > + us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS); > + us(4, Td[j], j, 0, 3); > + us(4, Ta[j], j, 0, 3); > + } > + > + u(8, Ss, 0, 63); > + u(8, Se, 0, 63); > + u(4, Ah, 0, 13); > + u(4, Al, 0, 15); > + > + return 0; > +} > + > +static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawApplicationData *current) > +{ > + int err, i; > + > + HEADER("Application Data"); > + > + u(16, Lp, 2, 65535); > + > + if (current->Lp > 2) { > +#ifdef READ > + current->Ap_ref = av_buffer_alloc(current->Lp - 2); > + if (!current->Ap_ref) > + return AVERROR(ENOMEM); > + current->Ap = current->Ap_ref->data; > +#endif > + > + for (i = 0; i < current->Lp - 2; i++) > + us(8, Ap[i], i, 0, 255); > + } > + > + return 0; > +} > + > +static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawComment *current) > +{ > + int err, i; > + > + HEADER("Comment"); > + > + u(16, Lc, 2, 65535); > + > + if (current->Lc > 2) { > +#ifdef READ > + current->Cm_ref = av_buffer_alloc(current->Lc - 2); > + if (!current->Cm_ref) > + return AVERROR(ENOMEM); > + current->Cm = current->Cm_ref->data; > +#endif > + > + for (i = 0; i < current->Lc - 2; i++) > + us(8, Cm[i], i, 0, 255); > + } > + > + return 0; > +} > -- > 2.16.3 > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
On 6/7/2018 8:43 PM, Mark Thompson wrote: > +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit) > +{ > + CodedBitstreamJPEGContext *priv = ctx->priv_data; > + PutBitContext pbc; > + int err; > + > + if (!priv->write_buffer) { > + // Initial write buffer size is 1MB. > + priv->write_buffer_size = 1024 * 1024; > + > + reallocate_and_try_again: > + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); You could use av_fast_malloc() instead, since you don't care about the previous contents of the buffer. But it will make no real difference admittedly since at most it will be reallocated twice or thrice for really big files and that's it. I sent a patch implementing this change for the other modules some weeks, which i guess you didn't see and i forgot about :p > + if (err < 0) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " > + "sufficiently large write buffer (last attempt " > + "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); > + return err; > + } > + }
On 08/06/18 02:16, mypopy@gmail.com wrote: > On Fri, Jun 8, 2018 at 7:46 AM Mark Thompson <sw@jkqxz.net> wrote: >> >> --- >> configure | 2 + >> libavcodec/Makefile | 1 + >> libavcodec/cbs.c | 6 + >> libavcodec/cbs_internal.h | 1 + >> libavcodec/cbs_jpeg.c | 513 ++++++++++++++++++++++++++++++++++ >> libavcodec/cbs_jpeg.h | 128 +++++++++ >> libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++ >> 7 files changed, 842 insertions(+) >> create mode 100644 libavcodec/cbs_jpeg.c >> create mode 100644 libavcodec/cbs_jpeg.h >> create mode 100644 libavcodec/cbs_jpeg_syntax_template.c >> >> ... >> diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h >> new file mode 100644 >> index 0000000000..f427c4c616 >> --- /dev/null >> +++ b/libavcodec/cbs_jpeg.h >> @@ -0,0 +1,128 @@ >> +/* >> + * 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 >> + */ >> + >> +#ifndef AVCODEC_CBS_JPEG_H >> +#define AVCODEC_CBS_JPEG_H >> + >> +#include <stddef.h> >> +#include <stdint.h> >> + >> + > > missing "libavutil/buffer.h" for AVBufferRef *, use make > checkheaders can reproduce the error Yep. Fixed, thank you! - Mark >> +enum { >> + JPEG_MARKER_SOF0 = 0xc0, >> + JPEG_MARKER_SOF1 = 0xc1, >> + JPEG_MARKER_SOF2 = 0xc2, >> + JPEG_MARKER_SOF3 = 0xc3, >> + >> + JPEG_MARKER_DHT = 0xc4, >> + JPEG_MARKER_SOI = 0xd8, >> + JPEG_MARKER_EOI = 0xd9, >> + JPEG_MARKER_SOS = 0xda, >> + JPEG_MARKER_DQT = 0xdb, >> + >> + JPEG_MARKER_APPN = 0xe0, >> + JPEG_MARKER_JPGN = 0xf0, >> + JPEG_MARKER_COM = 0xfe, >> +}; >> + >> +enum { >> + JPEG_MAX_COMPONENTS = 255, >> + >> + JPEG_MAX_HEIGHT = 65535, >> + JPEG_MAX_WIDTH = 65535, >> +}; >> + >> + >> +typedef struct JPEGRawFrameHeader { >> + uint16_t Lf; >> + uint8_t P; >> + uint16_t Y; >> + uint16_t X; >> + uint16_t Nf; >> + >> + uint8_t C [JPEG_MAX_COMPONENTS]; >> + uint8_t H [JPEG_MAX_COMPONENTS]; >> + uint8_t V [JPEG_MAX_COMPONENTS]; >> + uint8_t Tq[JPEG_MAX_COMPONENTS]; >> +} JPEGRawFrameHeader; >> + >> +typedef struct JPEGRawScanHeader { >> + uint16_t Ls; >> + uint8_t Ns; >> + >> + uint8_t Cs[JPEG_MAX_COMPONENTS]; >> + uint8_t Td[JPEG_MAX_COMPONENTS]; >> + uint8_t Ta[JPEG_MAX_COMPONENTS]; >> + >> + uint8_t Ss; >> + uint8_t Se; >> + uint8_t Ah; >> + uint8_t Al; >> +} JPEGRawScanHeader; >> + >> +typedef struct JPEGRawScan { >> + JPEGRawScanHeader header; >> + uint8_t *data; >> + size_t data_size; >> + AVBufferRef *data_ref; >> +} JPEGRawScan; >> + >> +typedef struct JPEGRawQuantisationTable { >> + uint8_t Pq; >> + uint8_t Tq; >> + uint16_t Q[64]; >> +} JPEGRawQuantisationTable; >> + >> +typedef struct JPEGRawQuantisationTableSpecification { >> + uint16_t Lq; >> + JPEGRawQuantisationTable table[4]; >> +} JPEGRawQuantisationTableSpecification; >> + >> +typedef struct JPEGRawHuffmanTable { >> + uint8_t Tc; >> + uint8_t Th; >> + uint8_t L[16]; >> + uint8_t V[224]; >> +} JPEGRawHuffmanTable; >> + >> +typedef struct JPEGRawHuffmanTableSpecification { >> + uint16_t Lh; >> + JPEGRawHuffmanTable table[8]; >> +} JPEGRawHuffmanTableSpecification; >> + >> +typedef struct JPEGRawApplicationData { >> + uint16_t Lp; >> + uint8_t *Ap; >> + AVBufferRef *Ap_ref; >> +} JPEGRawApplicationData; >> + >> +typedef struct JPEGRawComment { >> + uint16_t Lc; >> + uint8_t *Cm; >> + AVBufferRef *Cm_ref; >> +} JPEGRawComment; >> + >> + >> +typedef struct CodedBitstreamJPEGContext { >> + // Write buffer. >> + uint8_t *write_buffer; >> + size_t write_buffer_size; >> +} CodedBitstreamJPEGContext; >> + >> + >> +#endif /* AVCODEC_CBS_JPEG_H */
On 08/06/18 05:18, James Almer wrote: > On 6/7/2018 8:43 PM, Mark Thompson wrote: >> +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, >> + CodedBitstreamUnit *unit) >> +{ >> + CodedBitstreamJPEGContext *priv = ctx->priv_data; >> + PutBitContext pbc; >> + int err; >> + >> + if (!priv->write_buffer) { >> + // Initial write buffer size is 1MB. >> + priv->write_buffer_size = 1024 * 1024; >> + >> + reallocate_and_try_again: >> + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); > > You could use av_fast_malloc() instead, since you don't care about the > previous contents of the buffer. But it will make no real difference > admittedly since at most it will be reallocated twice or thrice for > really big files and that's it. > > I sent a patch implementing this change for the other modules some > weeks, which i guess you didn't see and i forgot about :p That seems reasonable, but we should probably split this whole write-buffer stuff into a separate function so it doesn't keep being repeated in new places. I'll leave it identical to the existing ones for now - if you'd like to pursue the fast_malloc patch (which I think looks fine) then I'll change it to match, and likewise if we can make a suitable new function. Thanks, - Mark
On 6/13/2018 6:28 PM, Mark Thompson wrote: > On 08/06/18 05:18, James Almer wrote: >> On 6/7/2018 8:43 PM, Mark Thompson wrote: >>> +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, >>> + CodedBitstreamUnit *unit) >>> +{ >>> + CodedBitstreamJPEGContext *priv = ctx->priv_data; >>> + PutBitContext pbc; >>> + int err; >>> + >>> + if (!priv->write_buffer) { >>> + // Initial write buffer size is 1MB. >>> + priv->write_buffer_size = 1024 * 1024; >>> + >>> + reallocate_and_try_again: >>> + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); >> >> You could use av_fast_malloc() instead, since you don't care about the >> previous contents of the buffer. But it will make no real difference >> admittedly since at most it will be reallocated twice or thrice for >> really big files and that's it. >> >> I sent a patch implementing this change for the other modules some >> weeks, which i guess you didn't see and i forgot about :p > > That seems reasonable, but we should probably split this whole write-buffer stuff into a separate function so it doesn't keep being repeated in new places. > > I'll leave it identical to the existing ones for now - if you'd like to pursue the fast_malloc patch (which I think looks fine) then I'll change it to match, and likewise if we can make a suitable new function. Push as is. If we change it we can do it to all modules at the same time later. > > Thanks, > > - Mark > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel >
On Fri, 2018-06-08 at 00:43 +0100, Mark Thompson wrote: > --- > configure | 2 + > libavcodec/Makefile | 1 + > libavcodec/cbs.c | 6 + > libavcodec/cbs_internal.h | 1 + > libavcodec/cbs_jpeg.c | 513 > ++++++++++++++++++++++++++++++++++ > libavcodec/cbs_jpeg.h | 128 +++++++++ > libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++ > 7 files changed, 842 insertions(+) > create mode 100644 libavcodec/cbs_jpeg.c > create mode 100644 libavcodec/cbs_jpeg.h > create mode 100644 libavcodec/cbs_jpeg_syntax_template.c > > diff --git a/configure b/configure > index 790f55be14..d908283954 100755 > --- a/configure > +++ b/configure > @@ -2244,6 +2244,7 @@ CONFIG_EXTRA=" > cbs > cbs_h264 > cbs_h265 > + cbs_jpeg > cbs_mpeg2 > cbs_vp9 > dirac_parse > @@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST" > # subsystems > cbs_h264_select="cbs golomb" > cbs_h265_select="cbs golomb" > +cbs_jpeg_select="cbs" > cbs_mpeg2_select="cbs" > cbs_vp9_select="cbs" > dct_select="rdft" > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index 3ab071a039..2a1e0de110 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o > OBJS-$(CONFIG_CBS) += cbs.o > OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o > OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o > +OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o > OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o > OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o > OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c > index be6c043b58..bb3ce95971 100644 > --- a/libavcodec/cbs.c > +++ b/libavcodec/cbs.c > @@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = { > #if CONFIG_CBS_H265 > &ff_cbs_type_h265, > #endif > +#if CONFIG_CBS_JPEG > + &ff_cbs_type_jpeg, > +#endif > #if CONFIG_CBS_MPEG2 > &ff_cbs_type_mpeg2, > #endif > @@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = { > #if CONFIG_CBS_H265 > AV_CODEC_ID_H265, > #endif > +#if CONFIG_CBS_JPEG > + AV_CODEC_ID_MJPEG, > +#endif > #if CONFIG_CBS_MPEG2 > AV_CODEC_ID_MPEG2VIDEO, > #endif > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h > index 172b8a2515..e0e912e28e 100644 > --- a/libavcodec/cbs_internal.h > +++ b/libavcodec/cbs_internal.h > @@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, > PutBitContext *pbc, > > extern const CodedBitstreamType ff_cbs_type_h264; > extern const CodedBitstreamType ff_cbs_type_h265; > +extern const CodedBitstreamType ff_cbs_type_jpeg; > extern const CodedBitstreamType ff_cbs_type_mpeg2; > extern const CodedBitstreamType ff_cbs_type_vp9; > > diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c > new file mode 100644 > index 0000000000..365db73394 > --- /dev/null > +++ b/libavcodec/cbs_jpeg.c > @@ -0,0 +1,513 @@ > +/* > + * 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 "cbs.h" > +#include "cbs_internal.h" > +#include "cbs_jpeg.h" > + > + > +#define HEADER(name) do { \ > + ff_cbs_trace_header(ctx, name); \ > + } while (0) > + > +#define CHECK(call) do { \ > + err = (call); \ > + if (err < 0) \ > + return err; \ > + } while (0) > + > +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ > }) : NULL) > + > +#define u(width, name, range_min, range_max) \ > + xu(width, name, range_min, range_max, 0) > +#define us(width, name, sub, range_min, range_max) \ > + xu(width, name, range_min, range_max, 1, sub) > + > + > +#define READ > +#define READWRITE read > +#define RWContext GetBitContext > +#define FUNC(name) cbs_jpeg_read_ ## name > + > +#define xu(width, name, range_min, range_max, subs, ...) do { \ > + uint32_t value = range_min; \ > + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ > + SUBSCRIPTS(subs, __VA_ARGS__), \ > + &value, range_min, range_max)); \ > + current->name = value; \ > + } while (0) > + > +#include "cbs_jpeg_syntax_template.c" > + > +#undef READ > +#undef READWRITE > +#undef RWContext > +#undef FUNC > +#undef xu > + > +#define WRITE > +#define READWRITE write > +#define RWContext PutBitContext > +#define FUNC(name) cbs_jpeg_write_ ## name > + > +#define xu(width, name, range_min, range_max, subs, ...) do { \ > + uint32_t value = current->name; \ > + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ > + SUBSCRIPTS(subs, __VA_ARGS__), \ > + value, range_min, range_max)); \ > + } while (0) > + > + > +#include "cbs_jpeg_syntax_template.c" > + > +#undef READ > +#undef READWRITE > +#undef RWContext > +#undef FUNC > +#undef xu > + > + > +static void cbs_jpeg_free_application_data(void *unit, uint8_t *content) > +{ > + JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content; > + av_buffer_unref(&ad->Ap_ref); > + av_freep(&content); > +} > + > +static void cbs_jpeg_free_scan(void *unit, uint8_t *content) > +{ > + JPEGRawScan *scan = (JPEGRawScan*)content; > + av_buffer_unref(&scan->data_ref); > + av_freep(&content); > +} > + > +static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, > + CodedBitstreamFragment *frag, > + int header) > +{ > + AVBufferRef *data_ref; > + uint8_t *data; > + size_t data_size; > + int unit, start, end, marker, next_start, next_marker; > + int err, i, j, length; > + > + if (frag->data_size < 4) { > + // Definitely too short to be meaningful. > + return AVERROR_INVALIDDATA; > + } > + > + for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++); > + if (i > 0) { > + av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at " > + "beginning of image.\n", i); > + } > + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size && frag->data[i]) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "no SOI marker found.\n"); > + return AVERROR_INVALIDDATA; > + } > + marker = frag->data[i]; > + if (marker != JPEG_MARKER_SOI) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first " > + "marker is %02x, should be SOI.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "no image content found.\n"); > + return AVERROR_INVALIDDATA; > + } > + marker = frag->data[i]; > + start = i + 1; > + > + for (unit = 0;; unit++) { > + if (marker == JPEG_MARKER_EOI) { > + break; > + } else if (marker == JPEG_MARKER_SOS) { > + for (i = start; i + 1 < frag->data_size; i++) { > + if (frag->data[i] != 0xff) > + continue; > + end = i; > + for (++i; i + 1 < frag->data_size && > + frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + next_marker = -1; > + } else { > + if (frag->data[i] == 0x00) > + continue; > + next_marker = frag->data[i]; > + next_start = i + 1; > + } > + break; > + } > + } else { > + i = start; > + if (i + 2 > frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "truncated at %02x marker.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + length = AV_RB16(frag->data + i); > + if (i + length > frag->data_size) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " > + "truncated at %02x marker segment.\n", marker); > + return AVERROR_INVALIDDATA; > + } > + end = start + length; > + > + i = end; > + if (frag->data[i] != 0xff) { > + next_marker = -1; > + } else { > + for (++i; i + 1 < frag->data_size && > + frag->data[i] == 0xff; i++); > + if (i + 1 >= frag->data_size) { > + next_marker = -1; > + } else { > + next_marker = frag->data[i]; > + next_start = i + 1; > + } > + } > + } > + > + if (marker == JPEG_MARKER_SOS) { > + length = AV_RB16(frag->data + start); > + > + data_ref = NULL; > + data = av_malloc(end - start + > + AV_INPUT_BUFFER_PADDING_SIZE); > + if (!data) > + return AVERROR(ENOMEM); > + > + memcpy(data, frag->data + start, length); > + for (i = start + length, j = length; i < end; i++, j++) { > + if (frag->data[i] == 0xff) { > + while (frag->data[i] == 0xff) > + ++i; > + data[j] = 0xff; > + } else { > + data[j] = frag->data[i]; > + } > + } > + data_size = j; > + > + memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); > + > + } else { > + data = frag->data + start; > + data_size = end - start; > + data_ref = frag->data_ref; > + } > + > + err = ff_cbs_insert_unit_data(ctx, frag, unit, marker, > + data, data_size, data_ref); > + if (err < 0) { > + if (!data_ref) > + av_freep(&data); > + return err; > + } > + > + if (next_marker == -1) > + break; > + marker = next_marker; > + start = next_start; > + } > + > + return 0; > +} > + > +static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit) > +{ > + GetBitContext gbc; > + int err; > + > + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); > + if (err < 0) > + return err; > + > + if (unit->type >= JPEG_MARKER_SOF0 && > + unit->type <= JPEG_MARKER_SOF3) { > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawFrameHeader), > + NULL); > + if (err < 0) > + return err; > + > + err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content); > + if (err < 0) > + return err; > + > + } else if (unit->type >= JPEG_MARKER_APPN && > + unit->type <= JPEG_MARKER_APPN + 15) { > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawApplicationData), > + &cbs_jpeg_free_application_data); > + if (err < 0) > + return err; > + > + err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content); > + if (err < 0) > + return err; > + > + } else if (unit->type == JPEG_MARKER_SOS) { > + JPEGRawScan *scan; > + int pos; > + > + err = ff_cbs_alloc_unit_content(ctx, unit, > + sizeof(JPEGRawScan), > + &cbs_jpeg_free_scan); > + if (err < 0) > + return err; > + scan = unit->content; > + > + err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header); > + if (err < 0) > + return err; > + > + pos = get_bits_count(&gbc); > + av_assert0(pos % 8 == 0); > + if (pos > 0) { > + scan->data_size = unit->data_size - pos / 8; > + scan->data_ref = av_buffer_ref(unit->data_ref); > + if (!scan->data_ref) > + return AVERROR(ENOMEM); > + scan->data = unit->data + pos / 8; > + } > + > + } else { > + switch (unit->type) { > +#define SEGMENT(marker, type, func) \ > + case JPEG_MARKER_ ## marker: \ > + { \ > + err = ff_cbs_alloc_unit_content(ctx, unit, \ > + sizeof(type), NULL); \ > + if (err < 0) \ > + return err; \ > + err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \ > + if (err < 0) \ > + return err; \ > + } \ > + break > + SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt); > + SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht); > + SEGMENT(COM, JPEGRawComment, comment); A free function should be provided for JPEGRawComment as JPEGRawComment includes a AVBufferRef pointer. > +#undef SEGMENT > + default: > + return AVERROR(ENOSYS); > + } > + } > + > + return 0; > +} > + > +static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit, > + PutBitContext *pbc) > +{ > + JPEGRawScan *scan = unit->content; > + int i, err; > + > + err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header); > + if (err < 0) > + return err; > + > + if (scan->data) { > + if (scan->data_size * 8 > put_bits_left(pbc)) > + return AVERROR(ENOSPC); > + > + for (i = 0; i < scan->data_size; i++) > + put_bits(pbc, 8, scan->data[i]); > + } > + > + return 0; > +} > + > +static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit, > + PutBitContext *pbc) > +{ > + int err; > + > + if (unit->type >= JPEG_MARKER_SOF0 && > + unit->type <= JPEG_MARKER_SOF3) { > + err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content); > + } else if (unit->type >= JPEG_MARKER_APPN && > + unit->type <= JPEG_MARKER_APPN + 15) { > + err = cbs_jpeg_write_application_data(ctx, pbc, unit->content); > + } else { > + switch (unit->type) { > +#define SEGMENT(marker, func) \ > + case JPEG_MARKER_ ## marker: \ > + err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \ > + break; > + SEGMENT(DQT, dqt); > + SEGMENT(DHT, dht); > + SEGMENT(COM, comment); > + default: > + return AVERROR_PATCHWELCOME; > + } > + } > + > + return err; > +} > + > +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, > + CodedBitstreamUnit *unit) > +{ > + CodedBitstreamJPEGContext *priv = ctx->priv_data; > + PutBitContext pbc; > + int err; > + > + if (!priv->write_buffer) { > + // Initial write buffer size is 1MB. > + priv->write_buffer_size = 1024 * 1024; > + > + reallocate_and_try_again: > + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); > + if (err < 0) { > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " > + "sufficiently large write buffer (last attempt " > + "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); > + return err; > + } > + } > + > + init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); > + > + if (unit->type == JPEG_MARKER_SOS) > + err = cbs_jpeg_write_scan(ctx, unit, &pbc); > + else > + err = cbs_jpeg_write_segment(ctx, unit, &pbc); > + > + if (err == AVERROR(ENOSPC)) { > + // Overflow. > + priv->write_buffer_size *= 2; > + goto reallocate_and_try_again; > + } > + if (err < 0) { > + // Write failed for some other reason. > + return err; > + } > + > + if (put_bits_count(&pbc) % 8) > + unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; > + else > + unit->data_bit_padding = 0; > + > + unit->data_size = (put_bits_count(&pbc) + 7) / 8; > + flush_put_bits(&pbc); > + > + err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); > + if (err < 0) > + return err; > + > + memcpy(unit->data, priv->write_buffer, unit->data_size); > + > + return 0; > +} > + > +static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, > + CodedBitstreamFragment *frag) > +{ > + const CodedBitstreamUnit *unit; > + uint8_t *data; > + size_t size, dp, sp; > + int i; > + > + size = 4; // SOI + EOI. > + for (i = 0; i < frag->nb_units; i++) { > + unit = &frag->units[i]; > + size += 2 + unit->data_size; > + if (unit->type == JPEG_MARKER_SOS) { > + for (sp = 0; sp < unit->data_size; sp++) { > + if (unit->data[sp] == 0xff) > + ++size; > + } > + } > + } > + > + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); > + if (!frag->data_ref) > + return AVERROR(ENOMEM); > + data = frag->data_ref->data; > + > + dp = 0; > + > + data[dp++] = 0xff; > + data[dp++] = JPEG_MARKER_SOI; > + > + for (i = 0; i < frag->nb_units; i++) { > + unit = &frag->units[i]; > + > + data[dp++] = 0xff; > + data[dp++] = unit->type; > + > + if (unit->type != JPEG_MARKER_SOS) { > + memcpy(data + dp, unit->data, unit->data_size); > + dp += unit->data_size; > + } else { > + sp = AV_RB16(unit->data); > + av_assert0(sp <= unit->data_size); > + memcpy(data + dp, unit->data, sp); > + dp += sp; > + > + for (; sp < unit->data_size; sp++) { > + if (unit->data[sp] == 0xff) { > + data[dp++] = 0xff; > + data[dp++] = 0x00; > + } else { > + data[dp++] = unit->data[sp]; > + } > + } > + } > + } > + > + data[dp++] = 0xff; > + data[dp++] = JPEG_MARKER_EOI; > + > + av_assert0(dp == size); > + > + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); > + frag->data = data; > + frag->data_size = size; > + > + return 0; > +} > + > +static void cbs_jpeg_close(CodedBitstreamContext *ctx) > +{ > + CodedBitstreamJPEGContext *priv = ctx->priv_data; > + > + av_freep(&priv->write_buffer); > +} > + > +const CodedBitstreamType ff_cbs_type_jpeg = { > + .codec_id = AV_CODEC_ID_MJPEG, > + > + .priv_data_size = sizeof(CodedBitstreamJPEGContext), > + > + .split_fragment = &cbs_jpeg_split_fragment, > + .read_unit = &cbs_jpeg_read_unit, > + .write_unit = &cbs_jpeg_write_unit, > + .assemble_fragment = &cbs_jpeg_assemble_fragment, > + > + .close = &cbs_jpeg_close, > +}; > diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h > new file mode 100644 > index 0000000000..f427c4c616 > --- /dev/null > +++ b/libavcodec/cbs_jpeg.h > @@ -0,0 +1,128 @@ > +/* > + * 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 > + */ > + > +#ifndef AVCODEC_CBS_JPEG_H > +#define AVCODEC_CBS_JPEG_H > + > +#include <stddef.h> > +#include <stdint.h> > + > + > +enum { > + JPEG_MARKER_SOF0 = 0xc0, > + JPEG_MARKER_SOF1 = 0xc1, > + JPEG_MARKER_SOF2 = 0xc2, > + JPEG_MARKER_SOF3 = 0xc3, > + > + JPEG_MARKER_DHT = 0xc4, > + JPEG_MARKER_SOI = 0xd8, > + JPEG_MARKER_EOI = 0xd9, > + JPEG_MARKER_SOS = 0xda, > + JPEG_MARKER_DQT = 0xdb, > + > + JPEG_MARKER_APPN = 0xe0, > + JPEG_MARKER_JPGN = 0xf0, > + JPEG_MARKER_COM = 0xfe, > +}; > + > +enum { > + JPEG_MAX_COMPONENTS = 255, > + > + JPEG_MAX_HEIGHT = 65535, > + JPEG_MAX_WIDTH = 65535, > +}; > + > + > +typedef struct JPEGRawFrameHeader { > + uint16_t Lf; > + uint8_t P; > + uint16_t Y; > + uint16_t X; > + uint16_t Nf; > + > + uint8_t C [JPEG_MAX_COMPONENTS]; > + uint8_t H [JPEG_MAX_COMPONENTS]; > + uint8_t V [JPEG_MAX_COMPONENTS]; > + uint8_t Tq[JPEG_MAX_COMPONENTS]; > +} JPEGRawFrameHeader; > + > +typedef struct JPEGRawScanHeader { > + uint16_t Ls; > + uint8_t Ns; > + > + uint8_t Cs[JPEG_MAX_COMPONENTS]; > + uint8_t Td[JPEG_MAX_COMPONENTS]; > + uint8_t Ta[JPEG_MAX_COMPONENTS]; > + > + uint8_t Ss; > + uint8_t Se; > + uint8_t Ah; > + uint8_t Al; > +} JPEGRawScanHeader; > + > +typedef struct JPEGRawScan { > + JPEGRawScanHeader header; > + uint8_t *data; > + size_t data_size; > + AVBufferRef *data_ref; > +} JPEGRawScan; > + > +typedef struct JPEGRawQuantisationTable { > + uint8_t Pq; > + uint8_t Tq; > + uint16_t Q[64]; > +} JPEGRawQuantisationTable; > + > +typedef struct JPEGRawQuantisationTableSpecification { > + uint16_t Lq; > + JPEGRawQuantisationTable table[4]; > +} JPEGRawQuantisationTableSpecification; > + > +typedef struct JPEGRawHuffmanTable { > + uint8_t Tc; > + uint8_t Th; > + uint8_t L[16]; > + uint8_t V[224]; > +} JPEGRawHuffmanTable; > + > +typedef struct JPEGRawHuffmanTableSpecification { > + uint16_t Lh; > + JPEGRawHuffmanTable table[8]; > +} JPEGRawHuffmanTableSpecification; > + > +typedef struct JPEGRawApplicationData { > + uint16_t Lp; > + uint8_t *Ap; > + AVBufferRef *Ap_ref; > +} JPEGRawApplicationData; > + > +typedef struct JPEGRawComment { > + uint16_t Lc; > + uint8_t *Cm; > + AVBufferRef *Cm_ref; > +} JPEGRawComment; > + > + > +typedef struct CodedBitstreamJPEGContext { > + // Write buffer. > + uint8_t *write_buffer; > + size_t write_buffer_size; > +} CodedBitstreamJPEGContext; > + > + > +#endif /* AVCODEC_CBS_JPEG_H */ > diff --git a/libavcodec/cbs_jpeg_syntax_template.c > b/libavcodec/cbs_jpeg_syntax_template.c > new file mode 100644 > index 0000000000..d3cd9ff62e > --- /dev/null > +++ b/libavcodec/cbs_jpeg_syntax_template.c > @@ -0,0 +1,191 @@ > +/* > + * 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 > + */ > + > +static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawFrameHeader *current) > +{ > + int err, i; > + > + HEADER("Frame Header"); > + > + u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS); > + > + u(8, P, 2, 16); > + u(16, Y, 0, JPEG_MAX_HEIGHT); > + u(16, X, 1, JPEG_MAX_WIDTH); > + u(8, Nf, 1, JPEG_MAX_COMPONENTS); > + > + for (i = 0; i < current->Nf; i++) { > + us(8, C[i], i, 0, JPEG_MAX_COMPONENTS); > + us(4, H[i], i, 1, 4); > + us(4, V[i], i, 1, 4); > + us(8, Tq[i], i, 0, 3); > + } > + > + return 0; > +} > + > +static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext > *rw, > + JPEGRawQuantisationTable *current) > +{ > + int err, i; > + > + u(4, Pq, 0, 1); > + u(4, Tq, 0, 3); > + > + if (current->Pq) { > + for (i = 0; i < 64; i++) > + us(16, Q[i], i, 1, 255); > + } else { > + for (i = 0; i < 64; i++) > + us(8, Q[i], i, 1, 255); > + } > + > + return 0; > +} > + > +static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawQuantisationTableSpecification *current) > +{ > + int err, i, n; > + > + HEADER("Quantisation Tables"); > + > + u(16, Lq, 2, 2 + 4 * 65); > + n = current->Lq / 65; > + > + for (i = 0; i < n; i++) > + CHECK(FUNC(quantisation_table)(ctx, rw, ¤t->table[i])); > + > + return 0; > +} > + > +static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawHuffmanTable *current) > +{ > + int err, i, j, ij; > + > + u(4, Tc, 0, 1); > + u(4, Th, 0, 3); > + > + for (i = 0; i < 16; i++) > + us(8, L[i], i, 0, 224); > + > + ij = 0; > + for (i = 0; i < 16; i++) { > + for (j = 0; j < current->L[i]; j++) { > + us(8, V[ij], ij, 0, 255); > + ++ij; > + } > + } > + > + return 0; > +} > + > +static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawHuffmanTableSpecification *current) > +{ > + int err, i, j, n; > + > + HEADER("Huffman Tables"); > + > + u(16, Lh, 2, 2 + 8 * (1 + 16 + 256)); > + > + n = 2; > + for (i = 0; n < current->Lh; i++) { > + CHECK(FUNC(huffman_table)(ctx, rw, ¤t->table[i])); > + > + ++n; > + for (j = 0; j < 16; j++) > + n += 1 + current->table[i].L[j]; > + } > + > + return 0; > +} > + > +static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawScanHeader *current) > +{ > + int err, j; > + > + HEADER("Scan"); > + > + u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS); > + > + u(8, Ns, 1, 4); > + for (j = 0; j < current->Ns; j++) { > + us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS); > + us(4, Td[j], j, 0, 3); > + us(4, Ta[j], j, 0, 3); > + } > + > + u(8, Ss, 0, 63); > + u(8, Se, 0, 63); > + u(4, Ah, 0, 13); > + u(4, Al, 0, 15); > + > + return 0; > +} > + > +static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawApplicationData *current) > +{ > + int err, i; > + > + HEADER("Application Data"); > + > + u(16, Lp, 2, 65535); > + > + if (current->Lp > 2) { > +#ifdef READ > + current->Ap_ref = av_buffer_alloc(current->Lp - 2); > + if (!current->Ap_ref) > + return AVERROR(ENOMEM); > + current->Ap = current->Ap_ref->data; > +#endif > + > + for (i = 0; i < current->Lp - 2; i++) > + us(8, Ap[i], i, 0, 255); > + } > + > + return 0; > +} > + > +static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw, > + JPEGRawComment *current) > +{ > + int err, i; > + > + HEADER("Comment"); > + > + u(16, Lc, 2, 65535); > + > + if (current->Lc > 2) { > +#ifdef READ > + current->Cm_ref = av_buffer_alloc(current->Lc - 2); > + if (!current->Cm_ref) > + return AVERROR(ENOMEM); > + current->Cm = current->Cm_ref->data; > +#endif > + > + for (i = 0; i < current->Lc - 2; i++) > + us(8, Cm[i], i, 0, 255); > + } > + > + return 0; > +}
On 15/06/18 01:52, Xiang, Haihao wrote: > On Fri, 2018-06-08 at 00:43 +0100, Mark Thompson wrote: >> --- >> configure | 2 + >> libavcodec/Makefile | 1 + >> libavcodec/cbs.c | 6 + >> libavcodec/cbs_internal.h | 1 + >> libavcodec/cbs_jpeg.c | 513 >> ++++++++++++++++++++++++++++++++++ >> libavcodec/cbs_jpeg.h | 128 +++++++++ >> libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++ >> 7 files changed, 842 insertions(+) >> create mode 100644 libavcodec/cbs_jpeg.c >> create mode 100644 libavcodec/cbs_jpeg.h >> create mode 100644 libavcodec/cbs_jpeg_syntax_template.c >> >> ... >> diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c >> new file mode 100644 >> index 0000000000..365db73394 >> --- /dev/null >> +++ b/libavcodec/cbs_jpeg.c >> ... >> +static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx, >> + CodedBitstreamUnit *unit) >> +{ >> + GetBitContext gbc; >> + int err; >> + >> + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); >> + if (err < 0) >> + return err; >> + >> + if (unit->type >= JPEG_MARKER_SOF0 && >> + unit->type <= JPEG_MARKER_SOF3) { >> + err = ff_cbs_alloc_unit_content(ctx, unit, >> + sizeof(JPEGRawFrameHeader), >> + NULL); >> + if (err < 0) >> + return err; >> + >> + err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content); >> + if (err < 0) >> + return err; >> + >> + } else if (unit->type >= JPEG_MARKER_APPN && >> + unit->type <= JPEG_MARKER_APPN + 15) { >> + err = ff_cbs_alloc_unit_content(ctx, unit, >> + sizeof(JPEGRawApplicationData), >> + &cbs_jpeg_free_application_data); >> + if (err < 0) >> + return err; >> + >> + err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content); >> + if (err < 0) >> + return err; >> + >> + } else if (unit->type == JPEG_MARKER_SOS) { >> + JPEGRawScan *scan; >> + int pos; >> + >> + err = ff_cbs_alloc_unit_content(ctx, unit, >> + sizeof(JPEGRawScan), >> + &cbs_jpeg_free_scan); >> + if (err < 0) >> + return err; >> + scan = unit->content; >> + >> + err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header); >> + if (err < 0) >> + return err; >> + >> + pos = get_bits_count(&gbc); >> + av_assert0(pos % 8 == 0); >> + if (pos > 0) { >> + scan->data_size = unit->data_size - pos / 8; >> + scan->data_ref = av_buffer_ref(unit->data_ref); >> + if (!scan->data_ref) >> + return AVERROR(ENOMEM); >> + scan->data = unit->data + pos / 8; >> + } >> + >> + } else { >> + switch (unit->type) { >> +#define SEGMENT(marker, type, func) \ >> + case JPEG_MARKER_ ## marker: \ >> + { \ >> + err = ff_cbs_alloc_unit_content(ctx, unit, \ >> + sizeof(type), NULL); \ >> + if (err < 0) \ >> + return err; \ >> + err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \ >> + if (err < 0) \ >> + return err; \ >> + } \ >> + break >> + SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt); >> + SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht); >> + SEGMENT(COM, JPEGRawComment, comment); > > A free function should be provided for JPEGRawComment as JPEGRawComment includes > a AVBufferRef pointer. Yep, fixed. >> +#undef SEGMENT >> + default: >> + return AVERROR(ENOSYS); >> + } >> + } >> + >> + return 0; >> +} >> ... Thanks, - Mark
diff --git a/configure b/configure index 790f55be14..d908283954 100755 --- a/configure +++ b/configure @@ -2244,6 +2244,7 @@ CONFIG_EXTRA=" cbs cbs_h264 cbs_h265 + cbs_jpeg cbs_mpeg2 cbs_vp9 dirac_parse @@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST" # subsystems cbs_h264_select="cbs golomb" cbs_h265_select="cbs golomb" +cbs_jpeg_select="cbs" cbs_mpeg2_select="cbs" cbs_vp9_select="cbs" dct_select="rdft" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3ab071a039..2a1e0de110 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o OBJS-$(CONFIG_CBS) += cbs.o OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o +OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index be6c043b58..bb3ce95971 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = { #if CONFIG_CBS_H265 &ff_cbs_type_h265, #endif +#if CONFIG_CBS_JPEG + &ff_cbs_type_jpeg, +#endif #if CONFIG_CBS_MPEG2 &ff_cbs_type_mpeg2, #endif @@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = { #if CONFIG_CBS_H265 AV_CODEC_ID_H265, #endif +#if CONFIG_CBS_JPEG + AV_CODEC_ID_MJPEG, +#endif #if CONFIG_CBS_MPEG2 AV_CODEC_ID_MPEG2VIDEO, #endif diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index 172b8a2515..e0e912e28e 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, extern const CodedBitstreamType ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265; +extern const CodedBitstreamType ff_cbs_type_jpeg; extern const CodedBitstreamType ff_cbs_type_mpeg2; extern const CodedBitstreamType ff_cbs_type_vp9; diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c new file mode 100644 index 0000000000..365db73394 --- /dev/null +++ b/libavcodec/cbs_jpeg.c @@ -0,0 +1,513 @@ +/* + * 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 "cbs.h" +#include "cbs_internal.h" +#include "cbs_jpeg.h" + + +#define HEADER(name) do { \ + ff_cbs_trace_header(ctx, name); \ + } while (0) + +#define CHECK(call) do { \ + err = (call); \ + if (err < 0) \ + return err; \ + } while (0) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) + +#define u(width, name, range_min, range_max) \ + xu(width, name, range_min, range_max, 0) +#define us(width, name, sub, range_min, range_max) \ + xu(width, name, range_min, range_max, 1, sub) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext +#define FUNC(name) cbs_jpeg_read_ ## name + +#define xu(width, name, range_min, range_max, subs, ...) do { \ + uint32_t value = range_min; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + current->name = value; \ + } while (0) + +#include "cbs_jpeg_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef FUNC +#undef xu + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext +#define FUNC(name) cbs_jpeg_write_ ## name + +#define xu(width, name, range_min, range_max, subs, ...) do { \ + uint32_t value = current->name; \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while (0) + + +#include "cbs_jpeg_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef FUNC +#undef xu + + +static void cbs_jpeg_free_application_data(void *unit, uint8_t *content) +{ + JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content; + av_buffer_unref(&ad->Ap_ref); + av_freep(&content); +} + +static void cbs_jpeg_free_scan(void *unit, uint8_t *content) +{ + JPEGRawScan *scan = (JPEGRawScan*)content; + av_buffer_unref(&scan->data_ref); + av_freep(&content); +} + +static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) +{ + AVBufferRef *data_ref; + uint8_t *data; + size_t data_size; + int unit, start, end, marker, next_start, next_marker; + int err, i, j, length; + + if (frag->data_size < 4) { + // Definitely too short to be meaningful. + return AVERROR_INVALIDDATA; + } + + for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++); + if (i > 0) { + av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at " + "beginning of image.\n", i); + } + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); + if (i + 1 >= frag->data_size && frag->data[i]) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "no SOI marker found.\n"); + return AVERROR_INVALIDDATA; + } + marker = frag->data[i]; + if (marker != JPEG_MARKER_SOI) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first " + "marker is %02x, should be SOI.\n", marker); + return AVERROR_INVALIDDATA; + } + for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); + if (i + 1 >= frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "no image content found.\n"); + return AVERROR_INVALIDDATA; + } + marker = frag->data[i]; + start = i + 1; + + for (unit = 0;; unit++) { + if (marker == JPEG_MARKER_EOI) { + break; + } else if (marker == JPEG_MARKER_SOS) { + for (i = start; i + 1 < frag->data_size; i++) { + if (frag->data[i] != 0xff) + continue; + end = i; + for (++i; i + 1 < frag->data_size && + frag->data[i] == 0xff; i++); + if (i + 1 >= frag->data_size) { + next_marker = -1; + } else { + if (frag->data[i] == 0x00) + continue; + next_marker = frag->data[i]; + next_start = i + 1; + } + break; + } + } else { + i = start; + if (i + 2 > frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "truncated at %02x marker.\n", marker); + return AVERROR_INVALIDDATA; + } + length = AV_RB16(frag->data + i); + if (i + length > frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "truncated at %02x marker segment.\n", marker); + return AVERROR_INVALIDDATA; + } + end = start + length; + + i = end; + if (frag->data[i] != 0xff) { + next_marker = -1; + } else { + for (++i; i + 1 < frag->data_size && + frag->data[i] == 0xff; i++); + if (i + 1 >= frag->data_size) { + next_marker = -1; + } else { + next_marker = frag->data[i]; + next_start = i + 1; + } + } + } + + if (marker == JPEG_MARKER_SOS) { + length = AV_RB16(frag->data + start); + + data_ref = NULL; + data = av_malloc(end - start + + AV_INPUT_BUFFER_PADDING_SIZE); + if (!data) + return AVERROR(ENOMEM); + + memcpy(data, frag->data + start, length); + for (i = start + length, j = length; i < end; i++, j++) { + if (frag->data[i] == 0xff) { + while (frag->data[i] == 0xff) + ++i; + data[j] = 0xff; + } else { + data[j] = frag->data[i]; + } + } + data_size = j; + + memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + } else { + data = frag->data + start; + data_size = end - start; + data_ref = frag->data_ref; + } + + err = ff_cbs_insert_unit_data(ctx, frag, unit, marker, + data, data_size, data_ref); + if (err < 0) { + if (!data_ref) + av_freep(&data); + return err; + } + + if (next_marker == -1) + break; + marker = next_marker; + start = next_start; + } + + return 0; +} + +static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + GetBitContext gbc; + int err; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if (err < 0) + return err; + + if (unit->type >= JPEG_MARKER_SOF0 && + unit->type <= JPEG_MARKER_SOF3) { + err = ff_cbs_alloc_unit_content(ctx, unit, + sizeof(JPEGRawFrameHeader), + NULL); + if (err < 0) + return err; + + err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content); + if (err < 0) + return err; + + } else if (unit->type >= JPEG_MARKER_APPN && + unit->type <= JPEG_MARKER_APPN + 15) { + err = ff_cbs_alloc_unit_content(ctx, unit, + sizeof(JPEGRawApplicationData), + &cbs_jpeg_free_application_data); + if (err < 0) + return err; + + err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content); + if (err < 0) + return err; + + } else if (unit->type == JPEG_MARKER_SOS) { + JPEGRawScan *scan; + int pos; + + err = ff_cbs_alloc_unit_content(ctx, unit, + sizeof(JPEGRawScan), + &cbs_jpeg_free_scan); + if (err < 0) + return err; + scan = unit->content; + + err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header); + if (err < 0) + return err; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0); + if (pos > 0) { + scan->data_size = unit->data_size - pos / 8; + scan->data_ref = av_buffer_ref(unit->data_ref); + if (!scan->data_ref) + return AVERROR(ENOMEM); + scan->data = unit->data + pos / 8; + } + + } else { + switch (unit->type) { +#define SEGMENT(marker, type, func) \ + case JPEG_MARKER_ ## marker: \ + { \ + err = ff_cbs_alloc_unit_content(ctx, unit, \ + sizeof(type), NULL); \ + if (err < 0) \ + return err; \ + err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \ + if (err < 0) \ + return err; \ + } \ + break + SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt); + SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht); + SEGMENT(COM, JPEGRawComment, comment); +#undef SEGMENT + default: + return AVERROR(ENOSYS); + } + } + + return 0; +} + +static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + JPEGRawScan *scan = unit->content; + int i, err; + + err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header); + if (err < 0) + return err; + + if (scan->data) { + if (scan->data_size * 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + for (i = 0; i < scan->data_size; i++) + put_bits(pbc, 8, scan->data[i]); + } + + return 0; +} + +static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + int err; + + if (unit->type >= JPEG_MARKER_SOF0 && + unit->type <= JPEG_MARKER_SOF3) { + err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content); + } else if (unit->type >= JPEG_MARKER_APPN && + unit->type <= JPEG_MARKER_APPN + 15) { + err = cbs_jpeg_write_application_data(ctx, pbc, unit->content); + } else { + switch (unit->type) { +#define SEGMENT(marker, func) \ + case JPEG_MARKER_ ## marker: \ + err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \ + break; + SEGMENT(DQT, dqt); + SEGMENT(DHT, dht); + SEGMENT(COM, comment); + default: + return AVERROR_PATCHWELCOME; + } + } + + return err; +} + +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + CodedBitstreamJPEGContext *priv = ctx->priv_data; + PutBitContext pbc; + int err; + + if (!priv->write_buffer) { + // Initial write buffer size is 1MB. + priv->write_buffer_size = 1024 * 1024; + + reallocate_and_try_again: + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " + "sufficiently large write buffer (last attempt " + "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); + return err; + } + } + + init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); + + if (unit->type == JPEG_MARKER_SOS) + err = cbs_jpeg_write_scan(ctx, unit, &pbc); + else + err = cbs_jpeg_write_segment(ctx, unit, &pbc); + + if (err == AVERROR(ENOSPC)) { + // Overflow. + priv->write_buffer_size *= 2; + goto reallocate_and_try_again; + } + if (err < 0) { + // Write failed for some other reason. + return err; + } + + if (put_bits_count(&pbc) % 8) + unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; + else + unit->data_bit_padding = 0; + + unit->data_size = (put_bits_count(&pbc) + 7) / 8; + flush_put_bits(&pbc); + + err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); + if (err < 0) + return err; + + memcpy(unit->data, priv->write_buffer, unit->data_size); + + return 0; +} + +static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + const CodedBitstreamUnit *unit; + uint8_t *data; + size_t size, dp, sp; + int i; + + size = 4; // SOI + EOI. + for (i = 0; i < frag->nb_units; i++) { + unit = &frag->units[i]; + size += 2 + unit->data_size; + if (unit->type == JPEG_MARKER_SOS) { + for (sp = 0; sp < unit->data_size; sp++) { + if (unit->data[sp] == 0xff) + ++size; + } + } + } + + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!frag->data_ref) + return AVERROR(ENOMEM); + data = frag->data_ref->data; + + dp = 0; + + data[dp++] = 0xff; + data[dp++] = JPEG_MARKER_SOI; + + for (i = 0; i < frag->nb_units; i++) { + unit = &frag->units[i]; + + data[dp++] = 0xff; + data[dp++] = unit->type; + + if (unit->type != JPEG_MARKER_SOS) { + memcpy(data + dp, unit->data, unit->data_size); + dp += unit->data_size; + } else { + sp = AV_RB16(unit->data); + av_assert0(sp <= unit->data_size); + memcpy(data + dp, unit->data, sp); + dp += sp; + + for (; sp < unit->data_size; sp++) { + if (unit->data[sp] == 0xff) { + data[dp++] = 0xff; + data[dp++] = 0x00; + } else { + data[dp++] = unit->data[sp]; + } + } + } + } + + data[dp++] = 0xff; + data[dp++] = JPEG_MARKER_EOI; + + av_assert0(dp == size); + + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + frag->data = data; + frag->data_size = size; + + return 0; +} + +static void cbs_jpeg_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamJPEGContext *priv = ctx->priv_data; + + av_freep(&priv->write_buffer); +} + +const CodedBitstreamType ff_cbs_type_jpeg = { + .codec_id = AV_CODEC_ID_MJPEG, + + .priv_data_size = sizeof(CodedBitstreamJPEGContext), + + .split_fragment = &cbs_jpeg_split_fragment, + .read_unit = &cbs_jpeg_read_unit, + .write_unit = &cbs_jpeg_write_unit, + .assemble_fragment = &cbs_jpeg_assemble_fragment, + + .close = &cbs_jpeg_close, +}; diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h new file mode 100644 index 0000000000..f427c4c616 --- /dev/null +++ b/libavcodec/cbs_jpeg.h @@ -0,0 +1,128 @@ +/* + * 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 + */ + +#ifndef AVCODEC_CBS_JPEG_H +#define AVCODEC_CBS_JPEG_H + +#include <stddef.h> +#include <stdint.h> + + +enum { + JPEG_MARKER_SOF0 = 0xc0, + JPEG_MARKER_SOF1 = 0xc1, + JPEG_MARKER_SOF2 = 0xc2, + JPEG_MARKER_SOF3 = 0xc3, + + JPEG_MARKER_DHT = 0xc4, + JPEG_MARKER_SOI = 0xd8, + JPEG_MARKER_EOI = 0xd9, + JPEG_MARKER_SOS = 0xda, + JPEG_MARKER_DQT = 0xdb, + + JPEG_MARKER_APPN = 0xe0, + JPEG_MARKER_JPGN = 0xf0, + JPEG_MARKER_COM = 0xfe, +}; + +enum { + JPEG_MAX_COMPONENTS = 255, + + JPEG_MAX_HEIGHT = 65535, + JPEG_MAX_WIDTH = 65535, +}; + + +typedef struct JPEGRawFrameHeader { + uint16_t Lf; + uint8_t P; + uint16_t Y; + uint16_t X; + uint16_t Nf; + + uint8_t C [JPEG_MAX_COMPONENTS]; + uint8_t H [JPEG_MAX_COMPONENTS]; + uint8_t V [JPEG_MAX_COMPONENTS]; + uint8_t Tq[JPEG_MAX_COMPONENTS]; +} JPEGRawFrameHeader; + +typedef struct JPEGRawScanHeader { + uint16_t Ls; + uint8_t Ns; + + uint8_t Cs[JPEG_MAX_COMPONENTS]; + uint8_t Td[JPEG_MAX_COMPONENTS]; + uint8_t Ta[JPEG_MAX_COMPONENTS]; + + uint8_t Ss; + uint8_t Se; + uint8_t Ah; + uint8_t Al; +} JPEGRawScanHeader; + +typedef struct JPEGRawScan { + JPEGRawScanHeader header; + uint8_t *data; + size_t data_size; + AVBufferRef *data_ref; +} JPEGRawScan; + +typedef struct JPEGRawQuantisationTable { + uint8_t Pq; + uint8_t Tq; + uint16_t Q[64]; +} JPEGRawQuantisationTable; + +typedef struct JPEGRawQuantisationTableSpecification { + uint16_t Lq; + JPEGRawQuantisationTable table[4]; +} JPEGRawQuantisationTableSpecification; + +typedef struct JPEGRawHuffmanTable { + uint8_t Tc; + uint8_t Th; + uint8_t L[16]; + uint8_t V[224]; +} JPEGRawHuffmanTable; + +typedef struct JPEGRawHuffmanTableSpecification { + uint16_t Lh; + JPEGRawHuffmanTable table[8]; +} JPEGRawHuffmanTableSpecification; + +typedef struct JPEGRawApplicationData { + uint16_t Lp; + uint8_t *Ap; + AVBufferRef *Ap_ref; +} JPEGRawApplicationData; + +typedef struct JPEGRawComment { + uint16_t Lc; + uint8_t *Cm; + AVBufferRef *Cm_ref; +} JPEGRawComment; + + +typedef struct CodedBitstreamJPEGContext { + // Write buffer. + uint8_t *write_buffer; + size_t write_buffer_size; +} CodedBitstreamJPEGContext; + + +#endif /* AVCODEC_CBS_JPEG_H */ diff --git a/libavcodec/cbs_jpeg_syntax_template.c b/libavcodec/cbs_jpeg_syntax_template.c new file mode 100644 index 0000000000..d3cd9ff62e --- /dev/null +++ b/libavcodec/cbs_jpeg_syntax_template.c @@ -0,0 +1,191 @@ +/* + * 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 + */ + +static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawFrameHeader *current) +{ + int err, i; + + HEADER("Frame Header"); + + u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS); + + u(8, P, 2, 16); + u(16, Y, 0, JPEG_MAX_HEIGHT); + u(16, X, 1, JPEG_MAX_WIDTH); + u(8, Nf, 1, JPEG_MAX_COMPONENTS); + + for (i = 0; i < current->Nf; i++) { + us(8, C[i], i, 0, JPEG_MAX_COMPONENTS); + us(4, H[i], i, 1, 4); + us(4, V[i], i, 1, 4); + us(8, Tq[i], i, 0, 3); + } + + return 0; +} + +static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawQuantisationTable *current) +{ + int err, i; + + u(4, Pq, 0, 1); + u(4, Tq, 0, 3); + + if (current->Pq) { + for (i = 0; i < 64; i++) + us(16, Q[i], i, 1, 255); + } else { + for (i = 0; i < 64; i++) + us(8, Q[i], i, 1, 255); + } + + return 0; +} + +static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawQuantisationTableSpecification *current) +{ + int err, i, n; + + HEADER("Quantisation Tables"); + + u(16, Lq, 2, 2 + 4 * 65); + n = current->Lq / 65; + + for (i = 0; i < n; i++) + CHECK(FUNC(quantisation_table)(ctx, rw, ¤t->table[i])); + + return 0; +} + +static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawHuffmanTable *current) +{ + int err, i, j, ij; + + u(4, Tc, 0, 1); + u(4, Th, 0, 3); + + for (i = 0; i < 16; i++) + us(8, L[i], i, 0, 224); + + ij = 0; + for (i = 0; i < 16; i++) { + for (j = 0; j < current->L[i]; j++) { + us(8, V[ij], ij, 0, 255); + ++ij; + } + } + + return 0; +} + +static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawHuffmanTableSpecification *current) +{ + int err, i, j, n; + + HEADER("Huffman Tables"); + + u(16, Lh, 2, 2 + 8 * (1 + 16 + 256)); + + n = 2; + for (i = 0; n < current->Lh; i++) { + CHECK(FUNC(huffman_table)(ctx, rw, ¤t->table[i])); + + ++n; + for (j = 0; j < 16; j++) + n += 1 + current->table[i].L[j]; + } + + return 0; +} + +static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawScanHeader *current) +{ + int err, j; + + HEADER("Scan"); + + u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS); + + u(8, Ns, 1, 4); + for (j = 0; j < current->Ns; j++) { + us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS); + us(4, Td[j], j, 0, 3); + us(4, Ta[j], j, 0, 3); + } + + u(8, Ss, 0, 63); + u(8, Se, 0, 63); + u(4, Ah, 0, 13); + u(4, Al, 0, 15); + + return 0; +} + +static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawApplicationData *current) +{ + int err, i; + + HEADER("Application Data"); + + u(16, Lp, 2, 65535); + + if (current->Lp > 2) { +#ifdef READ + current->Ap_ref = av_buffer_alloc(current->Lp - 2); + if (!current->Ap_ref) + return AVERROR(ENOMEM); + current->Ap = current->Ap_ref->data; +#endif + + for (i = 0; i < current->Lp - 2; i++) + us(8, Ap[i], i, 0, 255); + } + + return 0; +} + +static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawComment *current) +{ + int err, i; + + HEADER("Comment"); + + u(16, Lc, 2, 65535); + + if (current->Lc > 2) { +#ifdef READ + current->Cm_ref = av_buffer_alloc(current->Lc - 2); + if (!current->Cm_ref) + return AVERROR(ENOMEM); + current->Cm = current->Cm_ref->data; +#endif + + for (i = 0; i < current->Lc - 2; i++) + us(8, Cm[i], i, 0, 255); + } + + return 0; +}