mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-06 15:49:50 +08:00
Merge remote-tracking branch 'qatar/master'
* qatar/master: swfdec: Add support for sample_rate_code 0 (5512 Hz) dct-test: factor out some common code and do whas was likely intended doc: library versions need to be bumped in version.h Revert "ffmpeg: get rid of useless AVInputStream.nb_streams." Remove some forgotten AVCodecContext.palctrl usage. lavc/utils: move avcodec_init() higher in the file. lavc: replace some deprecated FF_*_TYPE with AV_PICTURE_TYPE_* ac3dec: actually use drc_scale private option lavc: undeprecate AVPALETTE_SIZE and AVPALETTE_COUNT macros alsa: add missing header msmpeg4: remove leftover unused debug variable declaration Fix assert() calls that need updates after FF_COMMON_FRAME macro elimination. Fix av_dlog invocations with wrong or missing logging context. vf_yadif: add support to yuva420p vf_yadif: correct documentation on the parity parameter vf_yadif: copy buffer properties like aspect for second frame as well oma: support for encrypted files id3v2: add support for non-text and GEOB type tag frames des: add possibility to calculate DES-CBC-MAC with small buffer Conflicts: ffmpeg.c libavcodec/dct-test.c libavformat/mpegts.c Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
@ -66,63 +66,129 @@ static unsigned int get_size(AVIOContext *s, int len)
|
||||
return v;
|
||||
}
|
||||
|
||||
static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
|
||||
/**
|
||||
* Free GEOB type extra metadata.
|
||||
*/
|
||||
static void free_geobtag(ID3v2ExtraMetaGEOB *geob)
|
||||
{
|
||||
char *q, dst[512];
|
||||
const char *val = NULL;
|
||||
int len, dstlen = sizeof(dst) - 1;
|
||||
unsigned genre;
|
||||
av_free(geob->mime_type);
|
||||
av_free(geob->file_name);
|
||||
av_free(geob->description);
|
||||
av_free(geob->data);
|
||||
av_free(geob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode characters to UTF-8 according to encoding type. The decoded buffer is
|
||||
* always null terminated.
|
||||
*
|
||||
* @param dst Pointer where the address of the buffer with the decoded bytes is
|
||||
* stored. Buffer must be freed by caller.
|
||||
* @param dstlen Pointer to an int where the length of the decoded string
|
||||
* is stored (in bytes, incl. null termination)
|
||||
* @param maxread Pointer to maximum number of characters to read from the
|
||||
* AVIOContext. After execution the value is decremented by the number of bytes
|
||||
* actually read.
|
||||
* @seeknull If true, decoding stops after the first U+0000 character found, if
|
||||
* there is any before maxread is reached
|
||||
* @returns 0 if no error occured, dst is uninitialized on error
|
||||
*/
|
||||
static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
|
||||
uint8_t **dst, int *dstlen, int *maxread, const int seeknull)
|
||||
{
|
||||
int len, ret;
|
||||
uint8_t tmp;
|
||||
uint32_t ch = 1;
|
||||
int left = *maxread;
|
||||
unsigned int (*get)(AVIOContext*) = avio_rb16;
|
||||
AVIOContext *dynbuf;
|
||||
|
||||
dst[0] = 0;
|
||||
if (taglen < 1)
|
||||
return;
|
||||
if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) {
|
||||
av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
taglen--; /* account for encoding type byte */
|
||||
|
||||
switch (avio_r8(pb)) { /* encoding type */
|
||||
switch (encoding) {
|
||||
|
||||
case ID3v2_ENCODING_ISO8859:
|
||||
q = dst;
|
||||
while (taglen-- && q - dst < dstlen - 7) {
|
||||
uint8_t tmp;
|
||||
PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
|
||||
while (left && (!seeknull || ch)) {
|
||||
ch = avio_r8(pb);
|
||||
PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
|
||||
left--;
|
||||
}
|
||||
*q = 0;
|
||||
break;
|
||||
|
||||
case ID3v2_ENCODING_UTF16BOM:
|
||||
taglen -= 2;
|
||||
if ((left -= 2) < 0) {
|
||||
av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n");
|
||||
avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
|
||||
av_freep(dst);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
switch (avio_rb16(pb)) {
|
||||
case 0xfffe:
|
||||
get = avio_rl16;
|
||||
case 0xfeff:
|
||||
break;
|
||||
default:
|
||||
av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
|
||||
return;
|
||||
av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
|
||||
avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
|
||||
av_freep(dst);
|
||||
*maxread = left;
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
// fall-through
|
||||
|
||||
case ID3v2_ENCODING_UTF16BE:
|
||||
q = dst;
|
||||
while (taglen > 1 && q - dst < dstlen - 7) {
|
||||
uint32_t ch;
|
||||
uint8_t tmp;
|
||||
|
||||
GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
|
||||
PUT_UTF8(ch, tmp, *q++ = tmp;)
|
||||
while ((left > 1) && (!seeknull || ch)) {
|
||||
GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
|
||||
PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
|
||||
}
|
||||
*q = 0;
|
||||
if (left < 0)
|
||||
left += 2; /* did not read last char from pb */
|
||||
break;
|
||||
|
||||
case ID3v2_ENCODING_UTF8:
|
||||
len = FFMIN(taglen, dstlen);
|
||||
avio_read(pb, dst, len);
|
||||
dst[len] = 0;
|
||||
while (left && (!seeknull || ch)) {
|
||||
ch = avio_r8(pb);
|
||||
avio_w8(dynbuf, ch);
|
||||
left--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
|
||||
av_log(s, AV_LOG_WARNING, "Unknown encoding\n");
|
||||
}
|
||||
|
||||
if (ch)
|
||||
avio_w8(dynbuf, 0);
|
||||
|
||||
len = avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
|
||||
if (dstlen)
|
||||
*dstlen = len;
|
||||
|
||||
*maxread = left;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a text tag.
|
||||
*/
|
||||
static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
|
||||
{
|
||||
uint8_t *dst;
|
||||
const char *val = NULL;
|
||||
int len, dstlen;
|
||||
unsigned genre;
|
||||
|
||||
if (taglen < 1)
|
||||
return;
|
||||
|
||||
taglen--; /* account for encoding type byte */
|
||||
|
||||
if (decode_str(s, pb, avio_r8(pb), &dst, &dstlen, &taglen, 0) < 0) {
|
||||
av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
|
||||
@ -141,6 +207,82 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const cha
|
||||
|
||||
if (val)
|
||||
av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
|
||||
|
||||
av_free(dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
|
||||
*/
|
||||
static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta)
|
||||
{
|
||||
ID3v2ExtraMetaGEOB *geob_data = NULL;
|
||||
ID3v2ExtraMeta *new_extra = NULL;
|
||||
char encoding;
|
||||
unsigned int len;
|
||||
|
||||
if (taglen < 1)
|
||||
return;
|
||||
|
||||
geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
|
||||
if (!geob_data) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMetaGEOB));
|
||||
return;
|
||||
}
|
||||
|
||||
new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
|
||||
if (!new_extra) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMeta));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read encoding type byte */
|
||||
encoding = avio_r8(pb);
|
||||
taglen--;
|
||||
|
||||
/* read MIME type (always ISO-8859) */
|
||||
if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, NULL, &taglen, 1) < 0
|
||||
|| taglen <= 0)
|
||||
goto fail;
|
||||
|
||||
/* read file name */
|
||||
if (decode_str(s, pb, encoding, &geob_data->file_name, NULL, &taglen, 1) < 0
|
||||
|| taglen <= 0)
|
||||
goto fail;
|
||||
|
||||
/* read content description */
|
||||
if (decode_str(s, pb, encoding, &geob_data->description, NULL, &taglen, 1) < 0
|
||||
|| taglen < 0)
|
||||
goto fail;
|
||||
|
||||
if (taglen) {
|
||||
/* save encapsulated binary data */
|
||||
geob_data->data = av_malloc(taglen);
|
||||
if (!geob_data->data) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen);
|
||||
goto fail;
|
||||
}
|
||||
if ((len = avio_read(pb, geob_data->data, taglen)) < taglen)
|
||||
av_log(s, AV_LOG_WARNING, "Error reading GEOB frame, data truncated.\n");
|
||||
geob_data->datasize = len;
|
||||
} else {
|
||||
geob_data->data = NULL;
|
||||
geob_data->datasize = 0;
|
||||
}
|
||||
|
||||
/* add data to the list */
|
||||
new_extra->tag = "GEOB";
|
||||
new_extra->data = geob_data;
|
||||
new_extra->next = *extra_meta;
|
||||
*extra_meta = new_extra;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag);
|
||||
free_geobtag(geob_data);
|
||||
av_free(new_extra);
|
||||
return;
|
||||
}
|
||||
|
||||
static int is_number(const char *str)
|
||||
@ -189,7 +331,27 @@ finish:
|
||||
av_dict_set(m, "date", date, 0);
|
||||
}
|
||||
|
||||
static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
|
||||
/**
|
||||
* Get the corresponding ID3v2EMFunc struct for a tag.
|
||||
* @param isv34 Determines if v2.2 or v2.3/4 strings are used
|
||||
* @return A pointer to the ID3v2EMFunc struct if found, NULL otherwise.
|
||||
*/
|
||||
static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
|
||||
{
|
||||
int i = 0;
|
||||
while (ff_id3v2_extra_meta_funcs[i].tag3) {
|
||||
if (!memcmp(tag,
|
||||
(isv34 ?
|
||||
ff_id3v2_extra_meta_funcs[i].tag4 :
|
||||
ff_id3v2_extra_meta_funcs[i].tag3),
|
||||
(isv34 ? 4 : 3)))
|
||||
return &ff_id3v2_extra_meta_funcs[i];
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta)
|
||||
{
|
||||
int isv34, unsync;
|
||||
unsigned tlen;
|
||||
@ -198,8 +360,10 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t
|
||||
int taghdrlen;
|
||||
const char *reason = NULL;
|
||||
AVIOContext pb;
|
||||
AVIOContext *pbx;
|
||||
unsigned char *buffer = NULL;
|
||||
int buffer_size = 0;
|
||||
void (*extra_func)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta**) = NULL;
|
||||
|
||||
switch (version) {
|
||||
case 2:
|
||||
@ -264,7 +428,8 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t
|
||||
if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
|
||||
av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
|
||||
avio_skip(s->pb, tlen);
|
||||
} else if (tag[0] == 'T') {
|
||||
/* check for text tag or supported special meta tag */
|
||||
} else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)->read))) {
|
||||
if (unsync || tunsync) {
|
||||
int i, j;
|
||||
av_fast_malloc(&buffer, &buffer_size, tlen);
|
||||
@ -280,10 +445,17 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t
|
||||
}
|
||||
}
|
||||
ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
|
||||
read_ttag(s, &pb, j, tag);
|
||||
tlen = j;
|
||||
pbx = &pb; // read from sync buffer
|
||||
} else {
|
||||
read_ttag(s, s->pb, tlen, tag);
|
||||
pbx = s->pb; // read straight from input
|
||||
}
|
||||
if (tag[0] == 'T')
|
||||
/* parse text tag */
|
||||
read_ttag(s, pbx, tlen, tag);
|
||||
else
|
||||
/* parse special meta tag */
|
||||
extra_func(s, pbx, tlen, tag, extra_meta);
|
||||
}
|
||||
else if (!tag[0]) {
|
||||
if (tag[1])
|
||||
@ -307,7 +479,7 @@ seek:
|
||||
return;
|
||||
}
|
||||
|
||||
void ff_id3v2_read(AVFormatContext *s, const char *magic)
|
||||
void ff_id3v2_read_all(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta)
|
||||
{
|
||||
int len, ret;
|
||||
uint8_t buf[ID3v2_HEADER_SIZE];
|
||||
@ -327,7 +499,7 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic)
|
||||
((buf[7] & 0x7f) << 14) |
|
||||
((buf[8] & 0x7f) << 7) |
|
||||
(buf[9] & 0x7f);
|
||||
ff_id3v2_parse(s, len, buf[3], buf[5]);
|
||||
ff_id3v2_parse(s, len, buf[3], buf[5], extra_meta);
|
||||
} else {
|
||||
avio_seek(s->pb, off, SEEK_SET);
|
||||
}
|
||||
@ -338,6 +510,30 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic)
|
||||
merge_date(&s->metadata);
|
||||
}
|
||||
|
||||
void ff_id3v2_read(AVFormatContext *s, const char *magic)
|
||||
{
|
||||
ff_id3v2_read_all(s, magic, NULL);
|
||||
}
|
||||
|
||||
void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
|
||||
{
|
||||
ID3v2ExtraMeta *current = *extra_meta, *next;
|
||||
void (*free_func)(ID3v2ExtraMeta*);
|
||||
|
||||
while (current) {
|
||||
if ((free_func = get_extra_meta_func(current->tag, 1)->free))
|
||||
free_func(current->data);
|
||||
next = current->next;
|
||||
av_freep(¤t);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
const ID3v2EMFunc ff_id3v2_extra_meta_funcs[] = {
|
||||
{ "GEO", "GEOB", read_geobtag, free_geobtag },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
|
||||
{ "TALB", "album"},
|
||||
{ "TCOM", "composer"},
|
||||
|
Reference in New Issue
Block a user