get_be16(pb); // unknown
snprintf(buf, sizeof(buf), "%d", get_be16(pb));
- av_metadata_set(&c->fc->metadata, "track", buf);
+ av_metadata_set2(&c->fc->metadata, "track", buf, 0);
get_be16(pb); // total tracks
switch (atom.type) {
case MKTAG(0xa9,'n','a','m'): key = "title"; break;
case MKTAG(0xa9,'a','u','t'):
- case MKTAG(0xa9,'A','R','T'): key = "author"; break;
+ case MKTAG(0xa9,'A','R','T'): key = "artist"; break;
case MKTAG(0xa9,'w','r','t'): key = "composer"; break;
case MKTAG( 'c','p','r','t'):
case MKTAG(0xa9,'c','p','y'): key = "copyright"; break;
get_buffer(pb, str, str_size);
str[str_size] = 0;
}
- av_metadata_set(&c->fc->metadata, key, str);
+ av_metadata_set2(&c->fc->metadata, key, str, 0);
if (*language && strcmp(language, "und")) {
snprintf(key2, sizeof(key2), "%s-%s", key, language);
- av_metadata_set(&c->fc->metadata, key2, str);
+ av_metadata_set2(&c->fc->metadata, key2, str, 0);
}
}
#ifdef DEBUG_METADATA
return 0;
}
+static int mov_read_chpl(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
+{
+ int64_t start;
+ int i, nb_chapters, str_len;
+ char str[256+1];
+
+ if ((atom.size -= 5) < 0)
+ return 0;
+
+ get_be32(pb); // version + flags
+ get_be32(pb); // ???
+ nb_chapters = get_byte(pb);
+
+ for (i = 0; i < nb_chapters; i++) {
+ if (atom.size < 9)
+ return 0;
+
+ start = get_be64(pb);
+ str_len = get_byte(pb);
+
+ if ((atom.size -= 9+str_len) < 0)
+ return 0;
+
+ get_buffer(pb, str, str_len);
+ str[str_len] = 0;
+ ff_new_chapter(c->fc, i, (AVRational){1,10000000}, start, AV_NOPTS_VALUE, str);
+ }
+ return 0;
+}
+
static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
{
int64_t total_size = 0;
if (strcmp(type, "qt "))
c->isom = 1;
av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type);
- av_metadata_set(&c->fc->metadata, "major_brand", type);
+ av_metadata_set2(&c->fc->metadata, "major_brand", type, 0);
minor_ver = get_be32(pb); /* minor version */
snprintf(minor_ver_str, sizeof(minor_ver_str), "%d", minor_ver);
- av_metadata_set(&c->fc->metadata, "minor_version", minor_ver_str);
+ av_metadata_set2(&c->fc->metadata, "minor_version", minor_ver_str, 0);
comp_brand_size = atom.size - 8;
if (comp_brand_size < 0)
return AVERROR(ENOMEM);
get_buffer(pb, comp_brands_str, comp_brand_size);
comp_brands_str[comp_brand_size] = 0;
- av_metadata_set(&c->fc->metadata, "compatible_brands", comp_brands_str);
+ av_metadata_set2(&c->fc->metadata, "compatible_brands", comp_brands_str, 0);
av_freep(&comp_brands_str);
return 0;
lang = get_be16(pb); /* language */
if (ff_mov_lang_to_iso639(lang, language))
- av_metadata_set(&st->metadata, "language", language);
+ av_metadata_set2(&st->metadata, "language", language, 0);
get_be16(pb); /* quality */
return 0;
return 0;
}
+/**
+ * An strf atom is a BITMAPINFOHEADER struct. This struct is 40 bytes itself,
+ * but can have extradata appended at the end after the 40 bytes belonging
+ * to the struct.
+ */
+static int mov_read_strf(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ if (atom.size <= 40)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ if((uint64_t)atom.size > (1<<30))
+ return -1;
+
+ av_free(st->codec->extradata);
+ st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
+ st->codec->extradata_size = atom.size - 40;
+ url_fskip(pb, 40);
+ get_buffer(pb, st->codec->extradata, atom.size - 40);
+ return 0;
+}
+
static int mov_read_stco(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
{
AVStream *st;
return 0;
}
+static int mov_read_chap(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
+{
+ c->chapter_track = get_be32(pb);
+ return 0;
+}
+
static int mov_read_trex(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
{
MOVTrackExt *trex;
static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('a','v','s','s'), mov_read_extradata },
+{ MKTAG('c','h','p','l'), mov_read_chpl },
{ MKTAG('c','o','6','4'), mov_read_stco },
{ MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */
{ MKTAG('d','i','n','f'), mov_read_default },
{ MKTAG('s','t','b','l'), mov_read_default },
{ MKTAG('s','t','c','o'), mov_read_stco },
{ MKTAG('s','t','p','s'), mov_read_stps },
+{ MKTAG('s','t','r','f'), mov_read_strf },
{ MKTAG('s','t','s','c'), mov_read_stsc },
{ MKTAG('s','t','s','d'), mov_read_stsd }, /* sample description */
{ MKTAG('s','t','s','s'), mov_read_stss }, /* sync sample */
{ MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */
{ MKTAG('t','r','a','k'), mov_read_trak },
{ MKTAG('t','r','a','f'), mov_read_default },
+{ MKTAG('t','r','e','f'), mov_read_default },
+{ MKTAG('c','h','a','p'), mov_read_chap },
{ MKTAG('t','r','e','x'), mov_read_trex },
{ MKTAG('t','r','u','n'), mov_read_trun },
{ MKTAG('u','d','t','a'), mov_read_default },
return score;
}
+// must be done after parsing all trak because there's no order requirement
+static void mov_read_chapters(AVFormatContext *s)
+{
+ MOVContext *mov = s->priv_data;
+ AVStream *st = NULL;
+ MOVStreamContext *sc;
+ int64_t cur_pos;
+ uint8_t *title = NULL;
+ int i, len, i8, i16;
+
+ for (i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->id == mov->chapter_track) {
+ st = s->streams[i];
+ break;
+ }
+ if (!st) {
+ av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n");
+ return;
+ }
+
+ st->discard = AVDISCARD_ALL;
+ sc = st->priv_data;
+ cur_pos = url_ftell(sc->pb);
+
+ for (i = 0; i < st->nb_index_entries; i++) {
+ AVIndexEntry *sample = &st->index_entries[i];
+ int64_t end = i+1 < st->nb_index_entries ? st->index_entries[i+1].timestamp : st->duration;
+
+ if (url_fseek(sc->pb, sample->pos, SEEK_SET) != sample->pos) {
+ av_log(s, AV_LOG_ERROR, "Chapter %d not found in file\n", i);
+ goto finish;
+ }
+
+ title = av_malloc(sample->size+2);
+ get_buffer(sc->pb, title, sample->size);
+
+ // the first two bytes are the length of the title
+ len = AV_RB16(title);
+ if (len > sample->size-2)
+ continue;
+
+ // The samples could theoretically be in any encoding if there's an encd
+ // atom following, but in practice are only utf-8 or utf-16, distinguished
+ // instead by the presence of a BOM
+ if (AV_RB16(title+2) == 0xfeff) {
+ uint8_t *utf8 = av_malloc(2*len+3);
+
+ i8 = i16 = 0;
+ while (i16 < len) {
+ uint32_t ch;
+ uint8_t tmp;
+ GET_UTF16(ch, i16 < len ? AV_RB16(title + (i16+=2)) : 0, break;)
+ PUT_UTF8(ch, tmp, if (i8 < 2*len) utf8[2+i8++] = tmp;)
+ }
+ utf8[2+i8] = 0;
+ av_freep(&title);
+ title = utf8;
+ }
+
+ ff_new_chapter(s, i, st->time_base, sample->timestamp, end, title+2);
+ av_freep(&title);
+ }
+finish:
+ av_free(title);
+ url_fseek(sc->pb, cur_pos, SEEK_SET);
+}
+
static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
{
MOVContext *mov = s->priv_data;
}
dprintf(mov->fc, "on_parse_exit_offset=%lld\n", url_ftell(pb));
+ if (!url_is_streamed(pb) && mov->chapter_track > 0)
+ mov_read_chapters(s);
+
return 0;
}
}
if (st->discard == AVDISCARD_ALL)
goto retry;
- pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? PKT_FLAG_KEY : 0;
+ pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
pkt->pos = sample->pos;
dprintf(s, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %d\n",
pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration);