#include "libavcodec/bytestream.h"
#include "libavcodec/flac.h"
#include "libavcodec/mpeg4audio.h"
+#include "libavcodec/packet_internal.h"
#include "avformat.h"
#include "avio_internal.h"
typedef struct MatroskaAttachment {
uint64_t uid;
char *filename;
+ char *description;
char *mime;
EbmlBin bin;
{ MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachment, filename) },
{ MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachment, mime) },
{ MATROSKA_ID_FILEDATA, EBML_BIN, 0, offsetof(MatroskaAttachment, bin) },
- { MATROSKA_ID_FILEDESC, EBML_NONE },
+ { MATROSKA_ID_FILEDESC, EBML_UTF8, 0, offsetof(MatroskaAttachment, description) },
CHILD_OF(matroska_attachments)
};
if (id == MATROSKA_ID_CLUSTER)
return NULL;
- // There can be multiple seekheads.
+ // There can be multiple SeekHeads and Tags.
for (i = 0; i < matroska->num_level1_elems; i++) {
if (matroska->level1_elems[i].id == id) {
if (matroska->level1_elems[i].pos == pos ||
- id != MATROSKA_ID_SEEKHEAD)
+ id != MATROSKA_ID_SEEKHEAD && id != MATROSKA_ID_TAGS)
return &matroska->level1_elems[i];
}
}
}
}
if (!found) {
- av_log(NULL, AV_LOG_WARNING,
+ av_log(s, AV_LOG_WARNING,
"The tags at index %d refer to a "
"non-existent attachment %"PRId64".\n",
i, tags[i].target.attachuid);
}
}
if (!found) {
- av_log(NULL, AV_LOG_WARNING,
+ av_log(s, AV_LOG_WARNING,
"The tags at index %d refer to a non-existent chapter "
"%"PRId64".\n",
i, tags[i].target.chapteruid);
}
}
if (!found) {
- av_log(NULL, AV_LOG_WARNING,
+ av_log(s, AV_LOG_WARNING,
"The tags at index %d refer to a non-existent track "
"%"PRId64".\n",
i, tags[i].target.trackuid);
static int mkv_field_order(MatroskaDemuxContext *matroska, int64_t field_order)
{
- int major, minor, micro, bttb = 0;
+ int minor, micro, bttb = 0;
/* workaround a bug in our Matroska muxer, introduced in version 57.36 alongside
* this function, and fixed in 57.52 */
- if (matroska->muxingapp && sscanf(matroska->muxingapp, "Lavf%d.%d.%d", &major, &minor, µ) == 3)
- bttb = (major == 57 && minor >= 36 && minor <= 51 && micro >= 100);
+ if (matroska->muxingapp && sscanf(matroska->muxingapp, "Lavf57.%d.%d", &minor, µ) == 2)
+ bttb = (minor >= 36 && minor <= 51 && micro >= 100);
switch (field_order) {
case MATROSKA_VIDEO_FIELDORDER_PROGRESSIVE:
return 0;
}
-static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) {
+static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track,
+ void *logctx)
+{
AVSphericalMapping *spherical;
+ const MatroskaTrackVideoProjection *mkv_projection = &track->video.projection;
+ const uint8_t *priv_data = mkv_projection->private.data;
enum AVSphericalProjection projection;
size_t spherical_size;
uint32_t l = 0, t = 0, r = 0, b = 0;
uint32_t padding = 0;
int ret;
- GetByteContext gb;
-
- bytestream2_init(&gb, track->video.projection.private.data,
- track->video.projection.private.size);
- if (bytestream2_get_byte(&gb) != 0) {
- av_log(NULL, AV_LOG_WARNING, "Unknown spherical metadata\n");
+ if (mkv_projection->private.size && priv_data[0] != 0) {
+ av_log(logctx, AV_LOG_WARNING, "Unknown spherical metadata\n");
return 0;
}
- bytestream2_skip(&gb, 3); // flags
-
switch (track->video.projection.type) {
case MATROSKA_VIDEO_PROJECTION_TYPE_EQUIRECTANGULAR:
if (track->video.projection.private.size == 20) {
- t = bytestream2_get_be32(&gb);
- b = bytestream2_get_be32(&gb);
- l = bytestream2_get_be32(&gb);
- r = bytestream2_get_be32(&gb);
+ t = AV_RB32(priv_data + 4);
+ b = AV_RB32(priv_data + 8);
+ l = AV_RB32(priv_data + 12);
+ r = AV_RB32(priv_data + 16);
if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
- av_log(NULL, AV_LOG_ERROR,
+ av_log(logctx, AV_LOG_ERROR,
"Invalid bounding rectangle coordinates "
"%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
l, t, r, b);
return AVERROR_INVALIDDATA;
}
} else if (track->video.projection.private.size != 0) {
- av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n");
+ av_log(logctx, AV_LOG_ERROR, "Unknown spherical metadata\n");
return AVERROR_INVALIDDATA;
}
break;
case MATROSKA_VIDEO_PROJECTION_TYPE_CUBEMAP:
if (track->video.projection.private.size < 4) {
- av_log(NULL, AV_LOG_ERROR, "Missing projection private properties\n");
+ av_log(logctx, AV_LOG_ERROR, "Missing projection private properties\n");
return AVERROR_INVALIDDATA;
} else if (track->video.projection.private.size == 12) {
- uint32_t layout = bytestream2_get_be32(&gb);
+ uint32_t layout = AV_RB32(priv_data + 4);
if (layout) {
- av_log(NULL, AV_LOG_WARNING,
+ av_log(logctx, AV_LOG_WARNING,
"Unknown spherical cubemap layout %"PRIu32"\n", layout);
return 0;
}
projection = AV_SPHERICAL_CUBEMAP;
- padding = bytestream2_get_be32(&gb);
+ padding = AV_RB32(priv_data + 8);
} else {
- av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n");
+ av_log(logctx, AV_LOG_ERROR, "Unknown spherical metadata\n");
return AVERROR_INVALIDDATA;
}
break;
/* No Spherical metadata */
return 0;
default:
- av_log(NULL, AV_LOG_WARNING,
+ av_log(logctx, AV_LOG_WARNING,
"Unknown spherical metadata type %"PRIu64"\n",
track->video.projection.type);
return 0;
if (!track->codec_id)
continue;
+ if ( track->type == MATROSKA_TRACK_TYPE_AUDIO && track->codec_id[0] != 'A'
+ || track->type == MATROSKA_TRACK_TYPE_VIDEO && track->codec_id[0] != 'V'
+ || track->type == MATROSKA_TRACK_TYPE_SUBTITLE && track->codec_id[0] != 'D' && track->codec_id[0] != 'S'
+ || track->type == MATROSKA_TRACK_TYPE_METADATA && track->codec_id[0] != 'D' && track->codec_id[0] != 'S'
+ ) {
+ av_log(matroska->ctx, AV_LOG_INFO, "Inconsistent track type\n");
+ continue;
+ }
+
if (track->audio.samplerate < 0 || track->audio.samplerate > INT_MAX ||
isnan(track->audio.samplerate)) {
av_log(matroska->ctx, AV_LOG_WARNING,
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown/unsupported AVCodecID %s.\n", track->codec_id);
- if (track->time_scale < 0.01)
+ if (track->time_scale < 0.01) {
+ av_log(matroska->ctx, AV_LOG_WARNING,
+ "Track TimestampScale too small %f, assuming 1.0.\n",
+ track->time_scale);
track->time_scale = 1.0;
+ }
avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale,
1000 * 1000 * 1000); /* 64 bit pts in ns */
ret = mkv_parse_video_color(st, track);
if (ret < 0)
return ret;
- ret = mkv_parse_video_projection(st, track);
+ ret = mkv_parse_video_projection(st, track, matroska->ctx);
if (ret < 0)
return ret;
} else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
break;
av_dict_set(&st->metadata, "filename", attachments[j].filename, 0);
av_dict_set(&st->metadata, "mimetype", attachments[j].mime, 0);
+ if (attachments[j].description)
+ av_dict_set(&st->metadata, "title", attachments[j].description, 0);
st->codecpar->codec_id = AV_CODEC_ID_NONE;
for (i = 0; mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) {
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
av_init_packet(pkt);
- pkt->buf = av_buffer_ref(attachments[j].bin.buf);
- if (!pkt->buf)
- return AVERROR(ENOMEM);
+ pkt->buf = attachments[j].bin.buf;
+ attachments[j].bin.buf = NULL;
pkt->data = attachments[j].bin.data;
pkt->size = attachments[j].bin.size;
pkt->stream_index = st->index;
MatroskaTrack *tracks = matroska->tracks.elem;
MatroskaTrack *track;
- ff_packet_list_get(&matroska->queue, &matroska->queue_end, pkt);
+ avpriv_packet_list_get(&matroska->queue, &matroska->queue_end, pkt);
track = &tracks[pkt->stream_index];
if (track->has_palette) {
uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
*/
static void matroska_clear_queue(MatroskaDemuxContext *matroska)
{
- ff_packet_list_free(&matroska->queue, &matroska->queue_end);
+ avpriv_packet_list_free(&matroska->queue, &matroska->queue_end);
}
static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
if (size <= 0)
return AVERROR_INVALIDDATA;
- *laces = *data + 1;
- data += 1;
- size -= 1;
+ *laces = *data + 1;
+ data += 1;
+ size -= 1;
switch (type) {
case 0x1: /* Xiph lacing */
for (n = 0; n < *laces - 1; n++) {
lace_size[n] = 0;
- while (1) {
- if (size <= total) {
+ do {
+ if (size <= total)
return AVERROR_INVALIDDATA;
- }
temp = *data;
total += temp;
lace_size[n] += temp;
data += 1;
size -= 1;
- if (temp != 0xff)
- break;
- }
+ } while (temp == 0xff);
}
- if (size < total) {
+ if (size < total)
return AVERROR_INVALIDDATA;
- }
lace_size[n] = size - total;
break;
}
case 0x2: /* fixed-size lacing */
- if (size % (*laces)) {
+ if (size % (*laces))
return AVERROR_INVALIDDATA;
- }
for (n = 0; n < *laces; n++)
lace_size[n] = size / *laces;
break;
}
data += offset;
size -= offset;
- if (size < total) {
+ if (size < total)
return AVERROR_INVALIDDATA;
- }
+
lace_size[*laces - 1] = size - total;
break;
}
}
- *buf = data;
+ *buf = data;
return 0;
}
track->audio.buf_timecode = AV_NOPTS_VALUE;
pkt->pos = pos;
pkt->stream_index = st->index;
- ret = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0);
+ ret = avpriv_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, NULL, 0);
if (ret < 0) {
av_packet_unref(pkt);
return AVERROR(ENOMEM);
pkt->duration = duration;
pkt->pos = pos;
- err = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0);
+ err = avpriv_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, NULL, 0);
if (err < 0) {
av_packet_unref(pkt);
return AVERROR(ENOMEM);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- res = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0);
+ res = avpriv_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, NULL, 0);
if (res < 0) {
av_packet_unref(pkt);
return AVERROR(ENOMEM);
uint32_t lace_size[256];
int n, flags, laces = 0;
uint64_t num;
- int trust_default_duration = 1;
+ int trust_default_duration;
ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL);
if (st->discard >= AVDISCARD_ALL)
return res;
- av_assert1(block_duration != AV_NOPTS_VALUE);
+ if (block_duration > INT64_MAX)
+ block_duration = INT64_MAX;
block_time = sign_extend(AV_RB16(data), 16);
data += 2;
if (cluster_time != (uint64_t) -1 &&
(block_time >= 0 || cluster_time >= -block_time)) {
- timecode = cluster_time + block_time - track->codec_delay_in_track_tb;
+ uint64_t timecode_cluster_in_track_tb = (double) cluster_time / track->time_scale;
+ timecode = timecode_cluster_in_track_tb + block_time - track->codec_delay_in_track_tb;
if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE &&
timecode < track->end_timecode)
is_keyframe = 0; /* overlapping subtitles are not key frame */
return res;
if (is_keyframe)
matroska->skip_to_keyframe = 0;
- else if (!st->skip_to_keyframe) {
+ else if (!st->internal->skip_to_keyframe) {
av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n");
matroska->skip_to_keyframe = 0;
}
return res;
}
- if (track->audio.samplerate == 8000) {
+ trust_default_duration = track->default_duration != 0;
+ if (track->audio.samplerate == 8000 && trust_default_duration) {
// If this is needed for more codecs, then add them here
if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
if (track->audio.samplerate != st->codecpar->sample_rate || !st->codecpar->frame_size)
matroska_parse_cues(matroska);
}
- if (!st->nb_index_entries)
+ if (!st->internal->nb_index_entries)
goto err;
- timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
+ timestamp = FFMAX(timestamp, st->internal->index_entries[0].timestamp);
- if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
- matroska_reset_status(matroska, 0, st->index_entries[st->nb_index_entries - 1].pos);
- while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
+ if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->internal->nb_index_entries - 1) {
+ matroska_reset_status(matroska, 0, st->internal->index_entries[st->internal->nb_index_entries - 1].pos);
+ while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->internal->nb_index_entries - 1) {
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0)
break;
}
matroska_clear_queue(matroska);
- if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1))
+ if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->internal->nb_index_entries - 1))
goto err;
tracks = matroska->tracks.elem;
}
/* We seek to a level 1 element, so set the appropriate status. */
- matroska_reset_status(matroska, 0, st->index_entries[index].pos);
+ matroska_reset_status(matroska, 0, st->internal->index_entries[index].pos);
if (flags & AVSEEK_FLAG_ANY) {
- st->skip_to_keyframe = 0;
+ st->internal->skip_to_keyframe = 0;
matroska->skip_to_timecode = timestamp;
} else {
- st->skip_to_keyframe = 1;
- matroska->skip_to_timecode = st->index_entries[index].timestamp;
+ st->internal->skip_to_keyframe = 1;
+ matroska->skip_to_timecode = st->internal->index_entries[index].timestamp;
}
matroska->skip_to_keyframe = 1;
matroska->done = 0;
- ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
+ ff_update_cur_dts(s, st, st->internal->index_entries[index].timestamp);
return 0;
err:
// slightly hackish but allows proper fallback to
matroska_reset_status(matroska, 0, -1);
matroska->resync_pos = -1;
matroska_clear_queue(matroska);
- st->skip_to_keyframe =
+ st->internal->skip_to_keyframe =
matroska->skip_to_keyframe = 0;
matroska->done = 0;
return -1;
MatroskaDemuxContext *matroska = s->priv_data;
CueDesc cue_desc;
int i;
- int nb_index_entries = s->streams[0]->nb_index_entries;
- AVIndexEntry *index_entries = s->streams[0]->index_entries;
+ int nb_index_entries = s->streams[0]->internal->nb_index_entries;
+ AVIndexEntry *index_entries = s->streams[0]->internal->index_entries;
if (ts >= matroska->duration * matroska->time_scale) return (CueDesc) {-1, -1, -1, -1};
for (i = 1; i < nb_index_entries; i++) {
if (index_entries[i - 1].timestamp * matroska->time_scale <= ts &&
uint32_t id = matroska->current_id;
int64_t cluster_pos, before_pos;
int index, rv = 1;
- if (s->streams[0]->nb_index_entries <= 0) return 0;
+ if (s->streams[0]->internal->nb_index_entries <= 0) return 0;
// seek to the first cluster using cues.
index = av_index_search_timestamp(s->streams[0], 0, 0);
if (index < 0) return 0;
- cluster_pos = s->streams[0]->index_entries[index].pos;
+ cluster_pos = s->streams[0]->internal->index_entries[index].pos;
before_pos = avio_tell(s->pb);
while (1) {
uint64_t cluster_id, cluster_length;
double bandwidth = 0.0;
int i;
- for (i = 0; i < st->nb_index_entries; i++) {
+ for (i = 0; i < st->internal->nb_index_entries; i++) {
int64_t prebuffer_ns = 1000000000;
- int64_t time_ns = st->index_entries[i].timestamp * matroska->time_scale;
+ int64_t time_ns = st->internal->index_entries[i].timestamp * matroska->time_scale;
double nano_seconds_per_second = 1000000000.0;
int64_t prebuffered_ns = time_ns + prebuffer_ns;
double prebuffer_bytes = 0.0;
// store cue point timestamps as a comma separated list for checking subsegment alignment in
// the muxer. assumes that each timestamp cannot be more than 20 characters long.
- buf = av_malloc_array(s->streams[0]->nb_index_entries, 20);
+ buf = av_malloc_array(s->streams[0]->internal->nb_index_entries, 20);
if (!buf) return -1;
strcpy(buf, "");
- for (i = 0; i < s->streams[0]->nb_index_entries; i++) {
+ for (i = 0; i < s->streams[0]->internal->nb_index_entries; i++) {
int ret = snprintf(buf + end, 20,
- "%" PRId64"%s", s->streams[0]->index_entries[i].timestamp,
- i != s->streams[0]->nb_index_entries - 1 ? "," : "");
- if (ret <= 0 || (ret == 20 && i == s->streams[0]->nb_index_entries - 1)) {
+ "%" PRId64"%s", s->streams[0]->internal->index_entries[i].timestamp,
+ i != s->streams[0]->internal->nb_index_entries - 1 ? "," : "");
+ if (ret <= 0 || (ret == 20 && i == s->streams[0]->internal->nb_index_entries - 1)) {
av_log(s, AV_LOG_ERROR, "timestamp too long.\n");
av_free(buf);
return AVERROR_INVALIDDATA;
av_log(s, AV_LOG_ERROR, "Failed to read file headers\n");
return -1;
}
- if (!s->nb_streams) {
- matroska_read_close(s);
- av_log(s, AV_LOG_ERROR, "No streams found\n");
- return AVERROR_INVALIDDATA;
+ if (!matroska->tracks.nb_elem || !s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "No track found\n");
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
if (!matroska->is_live) {
buf = av_asprintf("%g", matroska->duration);
- if (!buf) return AVERROR(ENOMEM);
+ if (!buf) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
av_dict_set(&s->streams[0]->metadata, DURATION,
buf, AV_DICT_DONT_STRDUP_VAL);
ret = webm_dash_manifest_cues(s, init_range);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Error parsing Cues\n");
- return ret;
+ goto fail;
}
}
matroska->bandwidth, 0);
}
return 0;
+fail:
+ matroska_read_close(s);
+ return ret;
}
static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt)