*/
/**
- * @file matroska.c
+ * @file matroskadec.c
* Matroska file demuxer
* by Ronald Bultje <rbultje@ronald.bitfreak.net>
* with a little help from Moritz Bunkus <moritz@bunkus.org>
/* Unique track number and track ID. stream_index is the index that
* the calling app uses for this track. */
- uint32_t num,
- uid,
- stream_index;
+ uint32_t num;
+ uint32_t uid;
+ int stream_index;
- char *name,
- *language;
+ char *name;
+ char language[4];
- char *codec_id,
- *codec_name;
+ char *codec_id;
+ char *codec_name;
unsigned char *codec_priv;
int codec_priv_size;
typedef struct MatroskaVideoTrack {
MatroskaTrack track;
- int pixel_width,
- pixel_height,
- display_width,
- display_height;
+ int pixel_width;
+ int pixel_height;
+ int display_width;
+ int display_height;
uint32_t fourcc;
typedef struct MatroskaAudioTrack {
MatroskaTrack track;
- int channels,
- bitdepth,
- internal_samplerate,
- samplerate;
+ int channels;
+ int bitdepth;
+ int internal_samplerate;
+ int samplerate;
int block_align;
/* real audio header */
typedef struct MatroskaSubtitleTrack {
MatroskaTrack track;
+ int ass;
//..
} MatroskaSubtitleTrack;
sizeof(MatroskaSubtitleTrack)))
typedef struct MatroskaLevel {
- uint64_t start, length;
+ uint64_t start;
+ uint64_t length;
} MatroskaLevel;
typedef struct MatroskaDemuxIndex {
int level_up;
/* matroska stuff */
- char *writing_app,
- *muxing_app;
+ char *writing_app;
+ char *muxing_app;
int64_t created;
/* timescale in the file */
/* num_streams is the number of streams that av_new_stream() was called
* for ( = that are available to the calling program). */
- int num_tracks, num_streams;
+ int num_tracks;
+ int num_streams;
MatroskaTrack *tracks[MAX_STREAMS];
/* cache for ID peeking */
int num_packets;
/* have we already parse metadata/cues/clusters? */
- int metadata_parsed,
- index_parsed,
- done;
+ int metadata_parsed;
+ int index_parsed;
+ int done;
/* The index for seeking. */
int num_indexes;
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
pos, pos);
}
- return AVERROR_IO; /* EOS or actual I/O error */
+ return AVERROR(EIO); /* EOS or actual I/O error */
}
/* get the length of the EBML number */
{
uint32_t id;
- assert(level_up != NULL);
-
if (ebml_read_element_id(matroska, &id, level_up) < 0)
return 0;
* byte more, read the string and NULL-terminate it ourselves. */
if (size < 0 || !(*str = av_malloc(size + 1))) {
av_log(matroska->ctx, AV_LOG_ERROR, "Memory allocation failed\n");
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
}
if (get_buffer(pb, (uint8_t *) *str, size) != size) {
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
- return AVERROR_IO;
+ return AVERROR(EIO);
}
(*str)[size] = '\0';
if (matroska->num_levels >= EBML_MAX_DEPTH) {
av_log(matroska->ctx, AV_LOG_ERROR,
"File moves beyond max. allowed depth (%d)\n", EBML_MAX_DEPTH);
- return AVERROR_NOTSUPP;
+ return AVERROR(ENOSYS);
}
/* remember level */
if (!(*binary = av_malloc(*size))) {
av_log(matroska->ctx, AV_LOG_ERROR,
"Memory allocation error\n");
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
}
if (get_buffer(pb, *binary, *size) != *size) {
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
- return AVERROR_IO;
+ return AVERROR(EIO);
}
return 0;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &level_up)))
- return AVERROR_IO;
+ return AVERROR(EIO);
/* end-of-header */
if (level_up)
uint8_t probe_data[] = { 'm', 'a', 't', 'r', 'o', 's', 'k', 'a' };
/* ebml header? */
- if ((p->buf[0] << 24 | p->buf[1] << 16 |
- p->buf[2] << 8 | p->buf[3]) != EBML_ID_HEADER)
+ if (AV_RB32(p->buf) != EBML_ID_HEADER)
return 0;
/* length of header */
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
/* Allocate a generic track. As soon as we know its type we'll realloc. */
track = av_mallocz(MAX_TRACK_SIZE);
matroska->num_tracks++;
+ strcpy(track->language, "eng");
/* start with the master */
if ((res = ebml_read_master(matroska, &id)) < 0)
/* try reading the trackentry headers */
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
if ((res = ebml_read_uint (matroska, &id,
&num)) < 0)
break;
- track->default_duration = num/matroska->time_scale;
+ track->default_duration = num;
break;
}
if ((res = ebml_read_float(matroska, &id,
&num)) < 0)
break;
- track->default_duration = 1000000000/(matroska->time_scale*num);
+ if (!track->default_duration)
+ track->default_duration = 1000000000/num;
break;
}
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up > 0) {
matroska->level_up--;
/* language (matters for audio/subtitles, mostly) */
case MATROSKA_ID_TRACKLANGUAGE: {
- char *text;
+ char *text, *end;
if ((res = ebml_read_utf8(matroska, &id, &text)) < 0)
break;
- track->language = text;
+ if ((end = strchr(text, '-')))
+ *end = '\0';
+ if (strlen(text) == 3)
+ strcpy(track->language, text);
+ av_free(text);
break;
}
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
- track->default_duration = num / matroska->time_scale;
+ track->default_duration = num;
break;
}
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id (matroska,
&matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
/* The next thing is a segment. */
while (1) {
if (!(id = ebml_peek_id(matroska, &last_level)))
- return AVERROR_IO;
+ return AVERROR(EIO);
if (id == MATROSKA_ID_SEGMENT)
break;
/* we've found our segment, start reading the different contents in here */
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
int extradata_size = 0;
int extradata_offset = 0;
track = matroska->tracks[i];
+ track->stream_index = -1;
- /* libavformat does not really support subtitles.
- * Also apply some sanity checks. */
- if ((track->type == MATROSKA_TRACK_TYPE_SUBTITLE) ||
- (track->codec_id == NULL))
+ /* Apply some sanity checks. */
+ if (track->codec_id == NULL)
continue;
for(j=0; ff_mkv_codec_tags[j].str; j++){
MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC) &&
(track->codec_priv_size >= 40) &&
(track->codec_priv != NULL)) {
- unsigned char *p;
+ MatroskaVideoTrack *vtrack = (MatroskaVideoTrack *) track;
/* Offset of biCompression. Stored in LE. */
- p = (unsigned char *)track->codec_priv + 16;
- ((MatroskaVideoTrack *)track)->fourcc = (p[3] << 24) |
- (p[2] << 16) | (p[1] << 8) | p[0];
- codec_id = codec_get_id(codec_bmp_tags, ((MatroskaVideoTrack *)track)->fourcc);
+ vtrack->fourcc = AV_RL32(track->codec_priv + 16);
+ codec_id = codec_get_id(codec_bmp_tags, vtrack->fourcc);
}
MATROSKA_CODEC_ID_AUDIO_ACM) &&
(track->codec_priv_size >= 18) &&
(track->codec_priv != NULL)) {
- unsigned char *p;
uint16_t tag;
/* Offset of wFormatTag. Stored in LE. */
- p = (unsigned char *)track->codec_priv;
- tag = (p[1] << 8) | p[0];
+ tag = AV_RL16(track->codec_priv);
codec_id = codec_get_id(codec_wav_tags, tag);
}
int sri = matroska_aac_sri(audiotrack->internal_samplerate);
extradata = av_malloc(5);
if (extradata == NULL)
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
extradata[0] = (profile << 3) | ((sri&0x0E) >> 1);
extradata[1] = ((sri&0x01) << 7) | (audiotrack->channels<<3);
if (strstr(track->codec_id, "SBR")) {
} else {
extradata_size = 2;
}
- track->default_duration = 1024*1000 / audiotrack->internal_samplerate;
}
else if (codec_id == CODEC_ID_TTA) {
extradata_size = 30;
extradata = av_mallocz(extradata_size);
if (extradata == NULL)
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
init_put_byte(&b, extradata, extradata_size, 1,
NULL, NULL, NULL, NULL);
put_buffer(&b, (uint8_t *) "TTA1", 4);
}
}
+ else if (codec_id == CODEC_ID_TEXT) {
+ MatroskaSubtitleTrack *subtrack=(MatroskaSubtitleTrack *)track;
+ if (!strcmp(track->codec_id, "S_TEXT/ASS") ||
+ !strcmp(track->codec_id, "S_TEXT/SSA") ||
+ !strcmp(track->codec_id, "S_ASS") ||
+ !strcmp(track->codec_id, "S_SSA"))
+ subtrack->ass = 1;
+ }
+
if (codec_id == CODEC_ID_NONE) {
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown/unsupported CodecID %s.\n",
matroska->num_streams++;
st = av_new_stream(s, track->stream_index);
if (st == NULL)
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
av_set_pts_info(st, 64, matroska->time_scale, 1000*1000*1000); /* 64 bit pts in ns */
st->codec->codec_id = codec_id;
st->start_time = 0;
+ if (strcmp(track->language, "und"))
+ strcpy(st->language, track->language);
if (track->default_duration)
av_reduce(&st->codec->time_base.num, &st->codec->time_base.den,
- track->default_duration, 1000, 30000);
+ track->default_duration, 1000000000, 30000);
if(extradata){
st->codec->extradata = extradata;
} else if(track->codec_priv && track->codec_priv_size > 0){
st->codec->extradata = av_malloc(track->codec_priv_size);
if(st->codec->extradata == NULL)
- return AVERROR_NOMEM;
+ return AVERROR(ENOMEM);
st->codec->extradata_size = track->codec_priv_size;
memcpy(st->codec->extradata,track->codec_priv+extradata_offset,
track->codec_priv_size);
MatroskaDemuxIndex *idx = &matroska->index[i];
track = matroska_find_track_by_num(matroska, idx->track);
stream = matroska->tracks[track]->stream_index;
- av_add_index_entry(matroska->ctx->streams[stream],
- idx->pos, idx->time/matroska->time_scale,
- 0, 0, AVINDEX_KEYFRAME);
+ if (stream >= 0)
+ av_add_index_entry(matroska->ctx->streams[stream],
+ idx->pos, idx->time/matroska->time_scale,
+ 0, 0, AVINDEX_KEYFRAME);
}
}
av_free(origdata);
return res;
}
+ if (matroska->tracks[track]->stream_index < 0)
+ return res;
st = matroska->ctx->streams[matroska->tracks[track]->stream_index];
if (st->discard >= AVDISCARD_ALL) {
av_free(origdata);
return res;
}
if (duration == AV_NOPTS_VALUE)
- duration = matroska->tracks[track]->default_duration;
+ duration = matroska->tracks[track]->default_duration / matroska->time_scale;
/* block_time (relative to cluster time) */
- block_time = (data[0] << 8) | data[1];
+ block_time = AV_RB16(data);
data += 2;
- size -= 2;
- flags = *data;
- data += 1;
- size -= 1;
+ flags = *data++;
+ size -= 3;
if (is_keyframe == -1)
- is_keyframe = flags & 1 ? PKT_FLAG_KEY : 0;
+ is_keyframe = flags & 0x80 ? PKT_FLAG_KEY : 0;
if (matroska->skip_to_keyframe) {
if (!is_keyframe || st != matroska->skip_to_stream)
int real_v = matroska->tracks[track]->flags & MATROSKA_TRACK_REAL_V;
uint64_t timecode = AV_NOPTS_VALUE;
- if (cluster_time != (uint64_t)-1 && cluster_time + block_time >= 0)
+ if (cluster_time != (uint64_t)-1
+ && (block_time >= 0 || cluster_time >= -block_time))
timecode = cluster_time + block_time;
for (n = 0; n < laces; n++) {
matroska_queue_packet(matroska, pkt);
}
} else {
- pkt = av_mallocz(sizeof(AVPacket));
- /* XXX: prevent data copy... */
- if (av_new_packet(pkt, slice_size) < 0) {
- res = AVERROR_NOMEM;
- n = laces-1;
- break;
- }
- memcpy (pkt->data, data+slice_offset, slice_size);
+ int offset = 0;
+
+ if (st->codec->codec_id == CODEC_ID_TEXT
+ && ((MatroskaSubtitleTrack *)(matroska->tracks[track]))->ass) {
+ int i;
+ for (i=0; i<8 && data[slice_offset+offset]; offset++)
+ if (data[slice_offset+offset] == ',')
+ i++;
+ }
+
+ pkt = av_mallocz(sizeof(AVPacket));
+ /* XXX: prevent data copy... */
+ if (av_new_packet(pkt, slice_size-offset) < 0) {
+ res = AVERROR(ENOMEM);
+ n = laces-1;
+ break;
+ }
+ memcpy (pkt->data, data+slice_offset+offset, slice_size-offset);
- if (n == 0)
- pkt->flags = is_keyframe;
- pkt->stream_index = matroska->tracks[track]->stream_index;
+ if (n == 0)
+ pkt->flags = is_keyframe;
+ pkt->stream_index = matroska->tracks[track]->stream_index;
- pkt->pts = timecode;
- pkt->pos = pos;
- pkt->duration = duration;
+ pkt->pts = timecode;
+ pkt->pos = pos;
+ pkt->duration = duration;
- matroska_queue_packet(matroska, pkt);
+ matroska_queue_packet(matroska, pkt);
}
if (timecode != AV_NOPTS_VALUE)
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
case MATROSKA_ID_BLOCKDURATION: {
if ((res = ebml_read_uint(matroska, &id, &duration)) < 0)
break;
- duration /= matroska->time_scale;
break;
}
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR_IO;
+ res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
/* Have we already reached the end? */
if (matroska->done)
- return AVERROR_IO;
+ return AVERROR(EIO);
res = 0;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- return AVERROR_IO;
+ return AVERROR(EIO);
} else if (matroska->level_up) {
matroska->level_up--;
break;
av_free(track->codec_name);
av_free(track->codec_priv);
av_free(track->name);
- av_free(track->language);
if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track;