return 0;
}
-static int decode_frame(AVCodecContext *avctx,
- void *data, int *got_frame,
- AVPacket *avpkt)
+static int decode_ihdr_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length)
{
- PNGDecContext *const s = avctx->priv_data;
- const uint8_t *buf = avpkt->data;
- int buf_size = avpkt->size;
- AVFrame *p;
- AVDictionary *metadata = NULL;
- uint32_t tag, length;
- int64_t sig;
- int ret;
-
- ff_thread_release_buffer(avctx, &s->last_picture);
- FFSWAP(ThreadFrame, s->picture, s->last_picture);
- p = s->picture.f;
+ if (length != 13)
+ return AVERROR_INVALIDDATA;
+ s->width = bytestream2_get_be32(&s->gb);
+ s->height = bytestream2_get_be32(&s->gb);
+ if (av_image_check_size(s->width, s->height, 0, avctx)) {
+ s->width = s->height = 0;
+ av_log(avctx, AV_LOG_ERROR, "Invalid image size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ s->bit_depth = bytestream2_get_byte(&s->gb);
+ s->color_type = bytestream2_get_byte(&s->gb);
+ s->compression_type = bytestream2_get_byte(&s->gb);
+ s->filter_type = bytestream2_get_byte(&s->gb);
+ s->interlace_type = bytestream2_get_byte(&s->gb);
+ bytestream2_skip(&s->gb, 4); /* crc */
+ s->state |= PNG_IHDR;
+ if (avctx->debug & FF_DEBUG_PICT_INFO)
+ av_log(avctx, AV_LOG_DEBUG, "width=%d height=%d depth=%d color_type=%d "
+ "compression_type=%d filter_type=%d interlace_type=%d\n",
+ s->width, s->height, s->bit_depth, s->color_type,
+ s->compression_type, s->filter_type, s->interlace_type);
- bytestream2_init(&s->gb, buf, buf_size);
+ return 0;
+}
- /* check signature */
- sig = bytestream2_get_be64(&s->gb);
- if (sig != PNGSIG &&
- sig != MNGSIG) {
- av_log(avctx, AV_LOG_ERROR, "Missing png signature\n");
+static int decode_phys_chunk(AVCodecContext *avctx, PNGDecContext *s)
+{
+ if (s->state & PNG_IDAT) {
+ av_log(avctx, AV_LOG_ERROR, "pHYs after IDAT\n");
return AVERROR_INVALIDDATA;
}
+ avctx->sample_aspect_ratio.num = bytestream2_get_be32(&s->gb);
+ avctx->sample_aspect_ratio.den = bytestream2_get_be32(&s->gb);
+ if (avctx->sample_aspect_ratio.num < 0 || avctx->sample_aspect_ratio.den < 0)
+ avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
+ bytestream2_skip(&s->gb, 1); /* unit specifier */
+ bytestream2_skip(&s->gb, 4); /* crc */
- s->y = s->state = 0;
+ return 0;
+}
- /* init the zlib */
- s->zstream.zalloc = ff_png_zalloc;
- s->zstream.zfree = ff_png_zfree;
- s->zstream.opaque = NULL;
- ret = inflateInit(&s->zstream);
- if (ret != Z_OK) {
- av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
- return AVERROR_EXTERNAL;
+static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length, AVFrame *p)
+{
+ int ret;
+
+ if (!(s->state & PNG_IHDR)) {
+ av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n");
+ return AVERROR_INVALIDDATA;
}
- for (;;) {
- if (bytestream2_get_bytes_left(&s->gb) <= 0) {
- av_log(avctx, AV_LOG_ERROR, "%d bytes left\n", bytestream2_get_bytes_left(&s->gb));
- if ( s->state & PNG_ALLIMAGE
- && avctx->strict_std_compliance <= FF_COMPLIANCE_NORMAL)
- goto exit_loop;
- goto fail;
+ if (!(s->state & PNG_IDAT)) {
+ /* init image info */
+ avctx->width = s->width;
+ avctx->height = s->height;
+
+ s->channels = ff_png_get_nb_channels(s->color_type);
+ s->bits_per_pixel = s->bit_depth * s->channels;
+ s->bpp = (s->bits_per_pixel + 7) >> 3;
+ s->row_size = (avctx->width * s->bits_per_pixel + 7) >> 3;
+
+ if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
+ s->color_type == PNG_COLOR_TYPE_RGB) {
+ avctx->pix_fmt = AV_PIX_FMT_RGB24;
+ } else if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
+ s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ avctx->pix_fmt = AV_PIX_FMT_RGBA;
+ } else if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
+ s->color_type == PNG_COLOR_TYPE_GRAY) {
+ avctx->pix_fmt = AV_PIX_FMT_GRAY8;
+ } else if (s->bit_depth == 16 &&
+ s->color_type == PNG_COLOR_TYPE_GRAY) {
+ avctx->pix_fmt = AV_PIX_FMT_GRAY16BE;
+ } else if (s->bit_depth == 16 &&
+ s->color_type == PNG_COLOR_TYPE_RGB) {
+ avctx->pix_fmt = AV_PIX_FMT_RGB48BE;
+ } else if (s->bit_depth == 16 &&
+ s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ avctx->pix_fmt = AV_PIX_FMT_RGBA64BE;
+ } else if ((s->bits_per_pixel == 1 || s->bits_per_pixel == 2 || s->bits_per_pixel == 4 || s->bits_per_pixel == 8) &&
+ s->color_type == PNG_COLOR_TYPE_PALETTE) {
+ avctx->pix_fmt = AV_PIX_FMT_PAL8;
+ } else if (s->bit_depth == 1 && s->bits_per_pixel == 1) {
+ avctx->pix_fmt = AV_PIX_FMT_MONOBLACK;
+ } else if (s->bit_depth == 8 &&
+ s->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ avctx->pix_fmt = AV_PIX_FMT_YA8;
+ } else if (s->bit_depth == 16 &&
+ s->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ avctx->pix_fmt = AV_PIX_FMT_YA16BE;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "unsupported bit depth %d "
+ "and color type %d\n",
+ s->bit_depth, s->color_type);
+ return AVERROR_INVALIDDATA;
}
- length = bytestream2_get_be32(&s->gb);
- if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb)) {
- av_log(avctx, AV_LOG_ERROR, "chunk too big\n");
- goto fail;
+ if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0)
+ return ret;
+ ff_thread_finish_setup(avctx);
+
+ p->pict_type = AV_PICTURE_TYPE_I;
+ p->key_frame = 1;
+ p->interlaced_frame = !!s->interlace_type;
+
+ /* compute the compressed row size */
+ if (!s->interlace_type) {
+ s->crow_size = s->row_size + 1;
+ } else {
+ s->pass = 0;
+ s->pass_row_size = ff_png_pass_row_size(s->pass,
+ s->bits_per_pixel,
+ s->width);
+ s->crow_size = s->pass_row_size + 1;
}
- tag = bytestream2_get_le32(&s->gb);
- if (avctx->debug & FF_DEBUG_STARTCODE)
- av_log(avctx, AV_LOG_DEBUG, "png: tag=%c%c%c%c length=%u\n",
- (tag & 0xff),
- ((tag >> 8) & 0xff),
- ((tag >> 16) & 0xff),
- ((tag >> 24) & 0xff), length);
- switch (tag) {
- case MKTAG('I', 'H', 'D', 'R'):
- if (length != 13)
- goto fail;
- s->width = bytestream2_get_be32(&s->gb);
- s->height = bytestream2_get_be32(&s->gb);
- if (av_image_check_size(s->width, s->height, 0, avctx)) {
- s->width = s->height = 0;
- av_log(avctx, AV_LOG_ERROR, "Invalid image size\n");
- goto fail;
- }
- s->bit_depth = bytestream2_get_byte(&s->gb);
- s->color_type = bytestream2_get_byte(&s->gb);
- s->compression_type = bytestream2_get_byte(&s->gb);
- s->filter_type = bytestream2_get_byte(&s->gb);
- s->interlace_type = bytestream2_get_byte(&s->gb);
- bytestream2_skip(&s->gb, 4); /* crc */
- s->state |= PNG_IHDR;
- if (avctx->debug & FF_DEBUG_PICT_INFO)
- av_log(avctx, AV_LOG_DEBUG, "width=%d height=%d depth=%d color_type=%d "
- "compression_type=%d filter_type=%d interlace_type=%d\n",
- s->width, s->height, s->bit_depth, s->color_type,
- s->compression_type, s->filter_type, s->interlace_type);
- break;
- case MKTAG('p', 'H', 'Y', 's'):
- if (s->state & PNG_IDAT) {
- av_log(avctx, AV_LOG_ERROR, "pHYs after IDAT\n");
- goto fail;
- }
- avctx->sample_aspect_ratio.num = bytestream2_get_be32(&s->gb);
- avctx->sample_aspect_ratio.den = bytestream2_get_be32(&s->gb);
- if (avctx->sample_aspect_ratio.num < 0 || avctx->sample_aspect_ratio.den < 0)
- avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
- bytestream2_skip(&s->gb, 1); /* unit specifier */
- bytestream2_skip(&s->gb, 4); /* crc */
- break;
- case MKTAG('I', 'D', 'A', 'T'):
- if (!(s->state & PNG_IHDR)) {
- av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n");
- goto fail;
- }
- if (!(s->state & PNG_IDAT)) {
- /* init image info */
- avctx->width = s->width;
- avctx->height = s->height;
-
- s->channels = ff_png_get_nb_channels(s->color_type);
- s->bits_per_pixel = s->bit_depth * s->channels;
- s->bpp = (s->bits_per_pixel + 7) >> 3;
- s->row_size = (avctx->width * s->bits_per_pixel + 7) >> 3;
-
- if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
- s->color_type == PNG_COLOR_TYPE_RGB) {
- avctx->pix_fmt = AV_PIX_FMT_RGB24;
- } else if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
- s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- avctx->pix_fmt = AV_PIX_FMT_RGBA;
- } else if ((s->bit_depth == 2 || s->bit_depth == 4 || s->bit_depth == 8) &&
- s->color_type == PNG_COLOR_TYPE_GRAY) {
- avctx->pix_fmt = AV_PIX_FMT_GRAY8;
- } else if (s->bit_depth == 16 &&
- s->color_type == PNG_COLOR_TYPE_GRAY) {
- avctx->pix_fmt = AV_PIX_FMT_GRAY16BE;
- } else if (s->bit_depth == 16 &&
- s->color_type == PNG_COLOR_TYPE_RGB) {
- avctx->pix_fmt = AV_PIX_FMT_RGB48BE;
- } else if (s->bit_depth == 16 &&
- s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- avctx->pix_fmt = AV_PIX_FMT_RGBA64BE;
- } else if ((s->bits_per_pixel == 1 || s->bits_per_pixel == 2 || s->bits_per_pixel == 4 || s->bits_per_pixel == 8) &&
- s->color_type == PNG_COLOR_TYPE_PALETTE) {
- avctx->pix_fmt = AV_PIX_FMT_PAL8;
- } else if (s->bit_depth == 1 && s->bits_per_pixel == 1) {
- avctx->pix_fmt = AV_PIX_FMT_MONOBLACK;
- } else if (s->bit_depth == 8 &&
- s->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
- avctx->pix_fmt = AV_PIX_FMT_YA8;
- } else if (s->bit_depth == 16 &&
- s->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
- avctx->pix_fmt = AV_PIX_FMT_YA16BE;
- } else {
- av_log(avctx, AV_LOG_ERROR, "unsupported bit depth %d "
- "and color type %d\n",
- s->bit_depth, s->color_type);
- goto fail;
- }
+ av_dlog(avctx, "row_size=%d crow_size =%d\n",
+ s->row_size, s->crow_size);
+ s->image_buf = p->data[0];
+ s->image_linesize = p->linesize[0];
+ /* copy the palette if needed */
+ if (avctx->pix_fmt == AV_PIX_FMT_PAL8)
+ memcpy(p->data[1], s->palette, 256 * sizeof(uint32_t));
+ /* empty row is used if differencing to the first row */
+ av_fast_padded_mallocz(&s->last_row, &s->last_row_size, s->row_size);
+ if (!s->last_row)
+ return AVERROR_INVALIDDATA;
+ if (s->interlace_type ||
+ s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ av_fast_padded_malloc(&s->tmp_row, &s->tmp_row_size, s->row_size);
+ if (!s->tmp_row)
+ return AVERROR_INVALIDDATA;
+ }
+ /* compressed row */
+ av_fast_padded_malloc(&s->buffer, &s->buffer_size, s->row_size + 16);
+ if (!s->buffer)
+ return AVERROR(ENOMEM);
- if (ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF) < 0)
- goto fail;
- ff_thread_finish_setup(avctx);
+ /* we want crow_buf+1 to be 16-byte aligned */
+ s->crow_buf = s->buffer + 15;
+ s->zstream.avail_out = s->crow_size;
+ s->zstream.next_out = s->crow_buf;
+ }
+ s->state |= PNG_IDAT;
+ if ((ret = png_decode_idat(s, length)) < 0)
+ return ret;
+ bytestream2_skip(&s->gb, 4); /* crc */
- p->pict_type = AV_PICTURE_TYPE_I;
- p->key_frame = 1;
- p->interlaced_frame = !!s->interlace_type;
+ return 0;
+}
- /* compute the compressed row size */
- if (!s->interlace_type) {
- s->crow_size = s->row_size + 1;
- } else {
- s->pass = 0;
- s->pass_row_size = ff_png_pass_row_size(s->pass,
- s->bits_per_pixel,
- s->width);
- s->crow_size = s->pass_row_size + 1;
- }
- av_dlog(avctx, "row_size=%d crow_size =%d\n",
- s->row_size, s->crow_size);
- s->image_buf = p->data[0];
- s->image_linesize = p->linesize[0];
- /* copy the palette if needed */
- if (avctx->pix_fmt == AV_PIX_FMT_PAL8)
- memcpy(p->data[1], s->palette, 256 * sizeof(uint32_t));
- /* empty row is used if differencing to the first row */
- av_fast_padded_mallocz(&s->last_row, &s->last_row_size, s->row_size);
- if (!s->last_row)
- goto fail;
- if (s->interlace_type ||
- s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- av_fast_padded_malloc(&s->tmp_row, &s->tmp_row_size, s->row_size);
- if (!s->tmp_row)
- goto fail;
- }
- /* compressed row */
- av_fast_padded_malloc(&s->buffer, &s->buffer_size, s->row_size + 16);
- if (!s->buffer)
- goto fail;
-
- /* we want crow_buf+1 to be 16-byte aligned */
- s->crow_buf = s->buffer + 15;
- s->zstream.avail_out = s->crow_size;
- s->zstream.next_out = s->crow_buf;
- }
- s->state |= PNG_IDAT;
- if (png_decode_idat(s, length) < 0)
- goto fail;
- bytestream2_skip(&s->gb, 4); /* crc */
- break;
- case MKTAG('P', 'L', 'T', 'E'):
- {
- int n, i, r, g, b;
+static int decode_plte_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length)
+{
+ int n, i, r, g, b;
- if ((length % 3) != 0 || length > 256 * 3)
- goto skip_tag;
- /* read the palette */
- n = length / 3;
- for (i = 0; i < n; i++) {
- r = bytestream2_get_byte(&s->gb);
- g = bytestream2_get_byte(&s->gb);
- b = bytestream2_get_byte(&s->gb);
- s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b;
- }
- for (; i < 256; i++)
- s->palette[i] = (0xFFU << 24);
- s->state |= PNG_PLTE;
- bytestream2_skip(&s->gb, 4); /* crc */
- }
- break;
- case MKTAG('t', 'R', 'N', 'S'):
- {
- int v, i;
+ if ((length % 3) != 0 || length > 256 * 3)
+ return AVERROR_INVALIDDATA;
+ /* read the palette */
+ n = length / 3;
+ for (i = 0; i < n; i++) {
+ r = bytestream2_get_byte(&s->gb);
+ g = bytestream2_get_byte(&s->gb);
+ b = bytestream2_get_byte(&s->gb);
+ s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b;
+ }
+ for (; i < 256; i++)
+ s->palette[i] = (0xFFU << 24);
+ s->state |= PNG_PLTE;
+ bytestream2_skip(&s->gb, 4); /* crc */
- /* read the transparency. XXX: Only palette mode supported */
- if (s->color_type != PNG_COLOR_TYPE_PALETTE ||
- length > 256 ||
- !(s->state & PNG_PLTE))
- goto skip_tag;
- for (i = 0; i < length; i++) {
- v = bytestream2_get_byte(&s->gb);
- s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
- }
- bytestream2_skip(&s->gb, 4); /* crc */
- }
- break;
- case MKTAG('t', 'E', 'X', 't'):
- if (decode_text_chunk(s, length, 0, &metadata) < 0)
- av_log(avctx, AV_LOG_WARNING, "Broken tEXt chunk\n");
- bytestream2_skip(&s->gb, length + 4);
- break;
- case MKTAG('z', 'T', 'X', 't'):
- if (decode_text_chunk(s, length, 1, &metadata) < 0)
- av_log(avctx, AV_LOG_WARNING, "Broken zTXt chunk\n");
- bytestream2_skip(&s->gb, length + 4);
- break;
- case MKTAG('I', 'E', 'N', 'D'):
- if (!(s->state & PNG_ALLIMAGE))
- av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
- if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
- goto fail;
- }
- bytestream2_skip(&s->gb, 4); /* crc */
- goto exit_loop;
- default:
- /* skip tag */
-skip_tag:
- bytestream2_skip(&s->gb, length + 4);
- break;
- }
+ return 0;
+}
+
+static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length)
+{
+ int v, i;
+
+ /* read the transparency. XXX: Only palette mode supported */
+ if (s->color_type != PNG_COLOR_TYPE_PALETTE ||
+ length > 256 ||
+ !(s->state & PNG_PLTE))
+ return AVERROR_INVALIDDATA;
+ for (i = 0; i < length; i++) {
+ v = bytestream2_get_byte(&s->gb);
+ s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
}
-exit_loop:
+ bytestream2_skip(&s->gb, 4); /* crc */
- if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE){
+ return 0;
+}
+
+static void handle_small_bpp(PNGDecContext *s, AVFrame *p)
+{
+ if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) {
int i, j, k;
uint8_t *pd = p->data[0];
for (j = 0; j < s->height; j++) {
}
pd += s->image_linesize;
}
- }
- if (s->bits_per_pixel == 2){
+ } else if (s->bits_per_pixel == 2) {
int i, j;
uint8_t *pd = p->data[0];
for (j = 0; j < s->height; j++) {
i = s->width / 4;
- if (s->color_type == PNG_COLOR_TYPE_PALETTE){
+ if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
if ((s->width&3) >= 3) pd[4*i + 2]= (pd[i] >> 2) & 3;
if ((s->width&3) >= 2) pd[4*i + 1]= (pd[i] >> 4) & 3;
if ((s->width&3) >= 1) pd[4*i + 0]= pd[i] >> 6;
}
pd += s->image_linesize;
}
- }
- if (s->bits_per_pixel == 4){
+ } else if (s->bits_per_pixel == 4) {
int i, j;
uint8_t *pd = p->data[0];
for (j = 0; j < s->height; j++) {
i = s->width/2;
- if (s->color_type == PNG_COLOR_TYPE_PALETTE){
+ if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
if (s->width&1) pd[2*i+0]= pd[i]>>4;
for (i--; i >= 0; i--) {
- pd[2*i + 1] = pd[i] & 15;
- pd[2*i + 0] = pd[i] >> 4;
- }
+ pd[2*i + 1] = pd[i] & 15;
+ pd[2*i + 0] = pd[i] >> 4;
+ }
} else {
if (s->width & 1) pd[2*i + 0]= (pd[i] >> 4) * 0x11;
for (i--; i >= 0; i--) {
pd += s->image_linesize;
}
}
+}
+
+static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length)
+{
+ uint32_t sequence_number, width, height, x_offset, y_offset;
+
+ if (length != 26)
+ return AVERROR_INVALIDDATA;
+
+ sequence_number = bytestream2_get_be32(&s->gb);
+ width = bytestream2_get_be32(&s->gb);
+ height = bytestream2_get_be32(&s->gb);
+ x_offset = bytestream2_get_be32(&s->gb);
+ y_offset = bytestream2_get_be32(&s->gb);
+ bytestream2_skip(&s->gb, 10); /* delay_num (2)
+ * delay_den (2)
+ * dispose_op (1)
+ * blend_op (1)
+ * crc (4)
+ */
+
+ if (width != s->width || height != s->height ||
+ x_offset != 0 || y_offset != 0) {
+ if (sequence_number == 0)
+ return AVERROR_INVALIDDATA;
+ avpriv_request_sample(avctx, "non key frames");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ return 0;
+}
+
+static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
+ AVFrame *p, AVPacket *avpkt)
+{
+ AVDictionary *metadata = NULL;
+ uint32_t tag, length;
+ int decode_next_dat = 0;
+ int ret = AVERROR_INVALIDDATA;
+
+ for (;;) {
+ length = bytestream2_get_bytes_left(&s->gb);
+ if (length <= 0) {
+ if (avctx->codec_id == AV_CODEC_ID_APNG && length == 0) {
+ if (!(s->state & PNG_IDAT))
+ return 0;
+ else
+ goto exit_loop;
+ }
+ av_log(avctx, AV_LOG_ERROR, "%d bytes left\n", length);
+ if ( s->state & PNG_ALLIMAGE
+ && avctx->strict_std_compliance <= FF_COMPLIANCE_NORMAL)
+ goto exit_loop;
+ goto fail;
+ }
+
+ length = bytestream2_get_be32(&s->gb);
+ if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb)) {
+ av_log(avctx, AV_LOG_ERROR, "chunk too big\n");
+ goto fail;
+ }
+ tag = bytestream2_get_le32(&s->gb);
+ if (avctx->debug & FF_DEBUG_STARTCODE)
+ av_log(avctx, AV_LOG_DEBUG, "png: tag=%c%c%c%c length=%u\n",
+ (tag & 0xff),
+ ((tag >> 8) & 0xff),
+ ((tag >> 16) & 0xff),
+ ((tag >> 24) & 0xff), length);
+ switch (tag) {
+ case MKTAG('I', 'H', 'D', 'R'):
+ if (decode_ihdr_chunk(avctx, s, length) < 0)
+ goto fail;
+ break;
+ case MKTAG('p', 'H', 'Y', 's'):
+ if (decode_phys_chunk(avctx, s) < 0)
+ goto fail;
+ break;
+ case MKTAG('f', 'c', 'T', 'L'):
+ if (avctx->codec_id != AV_CODEC_ID_APNG)
+ goto skip_tag;
+ if ((ret = decode_fctl_chunk(avctx, s, length)) < 0)
+ goto fail;
+ decode_next_dat = 1;
+ break;
+ case MKTAG('f', 'd', 'A', 'T'):
+ if (avctx->codec_id != AV_CODEC_ID_APNG)
+ goto skip_tag;
+ if (!decode_next_dat)
+ goto fail;
+ bytestream2_get_be32(&s->gb);
+ length -= 4;
+ /* fallthrough */
+ case MKTAG('I', 'D', 'A', 'T'):
+ if (avctx->codec_id == AV_CODEC_ID_APNG && !decode_next_dat)
+ goto skip_tag;
+ if (decode_idat_chunk(avctx, s, length, p) < 0)
+ goto fail;
+ break;
+ case MKTAG('P', 'L', 'T', 'E'):
+ if (decode_plte_chunk(avctx, s, length) < 0)
+ goto skip_tag;
+ break;
+ case MKTAG('t', 'R', 'N', 'S'):
+ if (decode_trns_chunk(avctx, s, length) < 0)
+ goto skip_tag;
+ break;
+ case MKTAG('t', 'E', 'X', 't'):
+ if (decode_text_chunk(s, length, 0, &metadata) < 0)
+ av_log(avctx, AV_LOG_WARNING, "Broken tEXt chunk\n");
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ case MKTAG('z', 'T', 'X', 't'):
+ if (decode_text_chunk(s, length, 1, &metadata) < 0)
+ av_log(avctx, AV_LOG_WARNING, "Broken zTXt chunk\n");
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ case MKTAG('I', 'E', 'N', 'D'):
+ if (!(s->state & PNG_ALLIMAGE))
+ av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
+ if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
+ goto fail;
+ }
+ bytestream2_skip(&s->gb, 4); /* crc */
+ goto exit_loop;
+ default:
+ /* skip tag */
+skip_tag:
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ }
+ }
+exit_loop:
+
+ if (s->bits_per_pixel <= 4)
+ handle_small_bpp(s, p);
/* handle p-frames only if a predecessor frame is available */
if (s->last_picture.f->data[0]) {
av_frame_set_metadata(p, metadata);
metadata = NULL;
+ return 0;
+
+fail:
+ av_dict_free(&metadata);
+ ff_thread_report_progress(&s->picture, INT_MAX, 0);
+ return ret;
+}
+
+#if CONFIG_PNG_DECODER
+static int decode_frame_png(AVCodecContext *avctx,
+ void *data, int *got_frame,
+ AVPacket *avpkt)
+{
+ PNGDecContext *const s = avctx->priv_data;
+ const uint8_t *buf = avpkt->data;
+ int buf_size = avpkt->size;
+ AVFrame *p;
+ int64_t sig;
+ int ret;
+
+ ff_thread_release_buffer(avctx, &s->last_picture);
+ FFSWAP(ThreadFrame, s->picture, s->last_picture);
+ p = s->picture.f;
+
+ bytestream2_init(&s->gb, buf, buf_size);
+
+ /* check signature */
+ sig = bytestream2_get_be64(&s->gb);
+ if (sig != PNGSIG &&
+ sig != MNGSIG) {
+ av_log(avctx, AV_LOG_ERROR, "Missing png signature\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ s->y = s->state = 0;
+
+ /* init the zlib */
+ s->zstream.zalloc = ff_png_zalloc;
+ s->zstream.zfree = ff_png_zfree;
+ s->zstream.opaque = NULL;
+ ret = inflateInit(&s->zstream);
+ if (ret != Z_OK) {
+ av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
+ return AVERROR_EXTERNAL;
+ }
+
+ if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+ goto the_end;
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
return ret;
inflateEnd(&s->zstream);
s->crow_buf = NULL;
return ret;
-fail:
- av_dict_free(&metadata);
- ff_thread_report_progress(&s->picture, INT_MAX, 0);
- ret = AVERROR_INVALIDDATA;
- goto the_end;
}
+#endif
+
+#if CONFIG_APNG_DECODER
+static int decode_frame_apng(AVCodecContext *avctx,
+ void *data, int *got_frame,
+ AVPacket *avpkt)
+{
+ PNGDecContext *const s = avctx->priv_data;
+ int ret;
+ AVFrame *p;
+
+ ff_thread_release_buffer(avctx, &s->last_picture);
+ FFSWAP(ThreadFrame, s->picture, s->last_picture);
+ p = s->picture.f;
+
+ if (!(s->state & PNG_IHDR)) {
+ if (!avctx->extradata_size)
+ return AVERROR_INVALIDDATA;
+
+ /* only init fields, there is no zlib use in extradata */
+ s->zstream.zalloc = ff_png_zalloc;
+ s->zstream.zfree = ff_png_zfree;
+
+ bytestream2_init(&s->gb, avctx->extradata, avctx->extradata_size);
+ if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+ goto end;
+ }
+
+ /* reset state for a new frame */
+ if ((ret = inflateInit(&s->zstream)) != Z_OK) {
+ av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
+ ret = AVERROR_EXTERNAL;
+ goto end;
+ }
+ s->y = 0;
+ s->state &= ~(PNG_IDAT | PNG_ALLIMAGE);
+ bytestream2_init(&s->gb, avpkt->data, avpkt->size);
+ if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+ goto end;
+
+ if (!(s->state & PNG_ALLIMAGE))
+ av_log(avctx, AV_LOG_WARNING, "Frame did not contain a complete image\n");
+ if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ if ((ret = av_frame_ref(data, s->picture.f)) < 0)
+ goto end;
+
+ *got_frame = 1;
+ ret = bytestream2_tell(&s->gb);
+
+end:
+ inflateEnd(&s->zstream);
+ return ret;
+}
+#endif
static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
{
return 0;
}
+#if CONFIG_APNG_DECODER
+AVCodec ff_apng_decoder = {
+ .name = "apng",
+ .long_name = NULL_IF_CONFIG_SMALL("APNG (Animated Portable Network Graphics) image"),
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_APNG,
+ .priv_data_size = sizeof(PNGDecContext),
+ .init = png_dec_init,
+ .close = png_dec_end,
+ .decode = decode_frame_apng,
+ .init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init),
+ .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context),
+ .capabilities = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
+};
+#endif
+
+#if CONFIG_PNG_DECODER
AVCodec ff_png_decoder = {
.name = "png",
.long_name = NULL_IF_CONFIG_SMALL("PNG (Portable Network Graphics) image"),
.priv_data_size = sizeof(PNGDecContext),
.init = png_dec_init,
.close = png_dec_end,
- .decode = decode_frame,
+ .decode = decode_frame_png,
.init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init),
.update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context),
.capabilities = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
};
+#endif