mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-03-28 12:32:17 +02:00
lavc/pngdec: decode textual data (tEXt and zTXt).
Requested in trac ticket #1857.
This commit is contained in:
parent
483c1aa5f1
commit
805b57001f
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
|
#include "libavutil/bprint.h"
|
||||||
#include "libavutil/imgutils.h"
|
#include "libavutil/imgutils.h"
|
||||||
#include "avcodec.h"
|
#include "avcodec.h"
|
||||||
#include "bytestream.h"
|
#include "bytestream.h"
|
||||||
@ -386,6 +387,122 @@ static int png_decode_idat(PNGDecContext *s, int length)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int decode_zbuf(AVBPrint *bp, const uint8_t *data,
|
||||||
|
const uint8_t *data_end)
|
||||||
|
{
|
||||||
|
z_stream zstream;
|
||||||
|
unsigned char *buf;
|
||||||
|
unsigned buf_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
zstream.zalloc = ff_png_zalloc;
|
||||||
|
zstream.zfree = ff_png_zfree;
|
||||||
|
zstream.opaque = NULL;
|
||||||
|
if (inflateInit(&zstream) != Z_OK)
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
zstream.next_in = (unsigned char *)data;
|
||||||
|
zstream.avail_in = data_end - data;
|
||||||
|
av_bprint_init(bp, 0, -1);
|
||||||
|
|
||||||
|
while (zstream.avail_in > 0) {
|
||||||
|
av_bprint_get_buffer(bp, 1, &buf, &buf_size);
|
||||||
|
if (!buf_size) {
|
||||||
|
ret = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
zstream.next_out = buf;
|
||||||
|
zstream.avail_out = buf_size;
|
||||||
|
ret = inflate(&zstream, Z_PARTIAL_FLUSH);
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END) {
|
||||||
|
ret = AVERROR_EXTERNAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
bp->len += zstream.next_out - buf;
|
||||||
|
if (ret == Z_STREAM_END)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inflateEnd(&zstream);
|
||||||
|
bp->str[bp->len] = 0;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
inflateEnd(&zstream);
|
||||||
|
av_bprint_finalize(bp, NULL);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *iso88591_to_utf8(const uint8_t *in, size_t size_in)
|
||||||
|
{
|
||||||
|
size_t extra = 0, i;
|
||||||
|
uint8_t *out, *q;
|
||||||
|
|
||||||
|
for (i = 0; i < size_in; i++)
|
||||||
|
extra += in[i] >= 0x80;
|
||||||
|
if (size_in == SIZE_MAX || extra > SIZE_MAX - size_in - 1)
|
||||||
|
return NULL;
|
||||||
|
q = out = av_malloc(size_in + extra + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < size_in; i++) {
|
||||||
|
if (in[i] >= 0x80) {
|
||||||
|
*(q++) = 0xC0 | (in[i] >> 6);
|
||||||
|
*(q++) = 0x80 | (in[i] & 0x3F);
|
||||||
|
} else {
|
||||||
|
*(q++) = in[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*(q++) = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decode_text_chunk(PNGDecContext *s, uint32_t length, int compressed,
|
||||||
|
AVDictionary **dict)
|
||||||
|
{
|
||||||
|
int ret, method;
|
||||||
|
const uint8_t *data = s->gb.buffer;
|
||||||
|
const uint8_t *data_end = data + length;
|
||||||
|
const uint8_t *keyword = data;
|
||||||
|
const uint8_t *keyword_end = memchr(keyword, 0, data_end - keyword);
|
||||||
|
uint8_t *kw_utf8 = NULL, *text, *txt_utf8 = NULL;
|
||||||
|
unsigned text_len;
|
||||||
|
AVBPrint bp;
|
||||||
|
|
||||||
|
if (!keyword_end)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
data = keyword_end + 1;
|
||||||
|
|
||||||
|
if (compressed) {
|
||||||
|
if (data == data_end)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
method = *(data++);
|
||||||
|
if (method)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
if ((ret = decode_zbuf(&bp, data, data_end)) < 0)
|
||||||
|
return ret;
|
||||||
|
text_len = bp.len;
|
||||||
|
av_bprint_finalize(&bp, (char **)&text);
|
||||||
|
if (!text)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
} else {
|
||||||
|
text = (uint8_t *)data;
|
||||||
|
text_len = data_end - text;
|
||||||
|
}
|
||||||
|
|
||||||
|
kw_utf8 = iso88591_to_utf8(keyword, keyword_end - keyword);
|
||||||
|
txt_utf8 = iso88591_to_utf8(text, text_len);
|
||||||
|
if (text != data)
|
||||||
|
av_free(text);
|
||||||
|
if (!(kw_utf8 && txt_utf8)) {
|
||||||
|
av_free(kw_utf8);
|
||||||
|
av_free(txt_utf8);
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
av_dict_set(dict, kw_utf8, txt_utf8,
|
||||||
|
AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int decode_frame(AVCodecContext *avctx,
|
static int decode_frame(AVCodecContext *avctx,
|
||||||
void *data, int *data_size,
|
void *data, int *data_size,
|
||||||
AVPacket *avpkt)
|
AVPacket *avpkt)
|
||||||
@ -395,6 +512,7 @@ static int decode_frame(AVCodecContext *avctx,
|
|||||||
PNGDecContext * const s = avctx->priv_data;
|
PNGDecContext * const s = avctx->priv_data;
|
||||||
AVFrame *picture = data;
|
AVFrame *picture = data;
|
||||||
AVFrame *p;
|
AVFrame *p;
|
||||||
|
AVDictionary *metadata = NULL;
|
||||||
uint8_t *crow_buf_base = NULL;
|
uint8_t *crow_buf_base = NULL;
|
||||||
uint32_t tag, length;
|
uint32_t tag, length;
|
||||||
int64_t sig;
|
int64_t sig;
|
||||||
@ -617,6 +735,16 @@ static int decode_frame(AVCodecContext *avctx,
|
|||||||
bytestream2_skip(&s->gb, 4); /* crc */
|
bytestream2_skip(&s->gb, 4); /* crc */
|
||||||
}
|
}
|
||||||
break;
|
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'):
|
case MKTAG('I', 'E', 'N', 'D'):
|
||||||
if (!(s->state & PNG_ALLIMAGE))
|
if (!(s->state & PNG_ALLIMAGE))
|
||||||
av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
|
av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
|
||||||
@ -712,6 +840,8 @@ static int decode_frame(AVCodecContext *avctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->current_picture->metadata = metadata;
|
||||||
|
metadata = NULL;
|
||||||
*picture= *s->current_picture;
|
*picture= *s->current_picture;
|
||||||
*data_size = sizeof(AVFrame);
|
*data_size = sizeof(AVFrame);
|
||||||
|
|
||||||
@ -724,6 +854,7 @@ static int decode_frame(AVCodecContext *avctx,
|
|||||||
av_freep(&s->tmp_row);
|
av_freep(&s->tmp_row);
|
||||||
return ret;
|
return ret;
|
||||||
fail:
|
fail:
|
||||||
|
av_dict_free(&metadata);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto the_end;
|
goto the_end;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user