📄 matroskadec.c
字号:
/*
* Matroska file demuxer (no muxer yet)
* Copyright (c) 2003-2004 The ffmpeg Project
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file matroskadec.c
* Matroska file demuxer
* by Ronald Bultje <rbultje@ronald.bitfreak.net>
* with a little help from Moritz Bunkus <moritz@bunkus.org>
* Specs available on the matroska project page:
* http://www.matroska.org/.
*/
#include "avformat.h"
/* For codec_get_id(). */
#include "riff.h"
#include "intfloat_readwrite.h"
#include "matroska.h"
typedef struct Track {
MatroskaTrackType type;
/* Unique track number and track ID. stream_index is the index that
* the calling app uses for this track. */
uint32_t num;
uint32_t uid;
int stream_index;
char *name;
char language[4];
char *codec_id;
char *codec_name;
unsigned char *codec_priv;
int codec_priv_size;
uint64_t default_duration;
MatroskaTrackFlags flags;
} MatroskaTrack;
typedef struct MatroskaVideoTrack {
MatroskaTrack track;
int pixel_width;
int pixel_height;
int display_width;
int display_height;
uint32_t fourcc;
MatroskaAspectRatioMode ar_mode;
MatroskaEyeMode eye_mode;
//..
} MatroskaVideoTrack;
typedef struct MatroskaAudioTrack {
MatroskaTrack track;
int channels;
int bitdepth;
int internal_samplerate;
int samplerate;
int block_align;
/* real audio header */
int coded_framesize;
int sub_packet_h;
int frame_size;
int sub_packet_size;
int sub_packet_cnt;
int pkt_cnt;
uint8_t *buf;
//..
} MatroskaAudioTrack;
typedef struct MatroskaSubtitleTrack {
MatroskaTrack track;
int ass;
//..
} MatroskaSubtitleTrack;
#define MAX_TRACK_SIZE (FFMAX(FFMAX(sizeof(MatroskaVideoTrack), \
sizeof(MatroskaAudioTrack)), \
sizeof(MatroskaSubtitleTrack)))
typedef struct MatroskaLevel {
uint64_t start;
uint64_t length;
} MatroskaLevel;
typedef struct MatroskaDemuxIndex {
uint64_t pos; /* of the corresponding *cluster*! */
uint16_t track; /* reference to 'num' */
uint64_t time; /* in nanoseconds */
} MatroskaDemuxIndex;
typedef struct MatroskaDemuxContext {
AVFormatContext *ctx;
/* ebml stuff */
int num_levels;
MatroskaLevel levels[EBML_MAX_DEPTH];
int level_up;
/* matroska stuff */
char *writing_app;
char *muxing_app;
int64_t created;
/* timescale in the file */
int64_t time_scale;
/* num_streams is the number of streams that av_new_stream() was called
* for ( = that are available to the calling program). */
int num_tracks;
int num_streams;
MatroskaTrack *tracks[MAX_STREAMS];
/* cache for ID peeking */
uint32_t peek_id;
/* byte position of the segment inside the stream */
offset_t segment_start;
/* The packet queue. */
AVPacket **packets;
int num_packets;
/* have we already parse metadata/cues/clusters? */
int metadata_parsed;
int index_parsed;
int done;
/* The index for seeking. */
int num_indexes;
MatroskaDemuxIndex *index;
/* What to skip before effectively reading a packet. */
int skip_to_keyframe;
AVStream *skip_to_stream;
} MatroskaDemuxContext;
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
* EBML file.
*/
/*
* Return: the amount of levels in the hierarchy that the
* current element lies higher than the previous one.
* The opposite isn't done - that's auto-done using master
* element reading.
*/
static int
ebml_read_element_level_up (MatroskaDemuxContext *matroska)
{
ByteIOContext *pb = &matroska->ctx->pb;
offset_t pos = url_ftell(pb);
int num = 0;
while (matroska->num_levels > 0) {
MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1];
if (pos >= level->start + level->length) {
matroska->num_levels--;
num++;
} else {
break;
}
}
return num;
}
/*
* Read: an "EBML number", which is defined as a variable-length
* array of bytes. The first byte indicates the length by giving a
* number of 0-bits followed by a one. The position of the first
* "one" bit inside the first byte indicates the length of this
* number.
* Returns: num. of bytes read. < 0 on error.
*/
static int
ebml_read_num (MatroskaDemuxContext *matroska,
int max_size,
uint64_t *number)
{
ByteIOContext *pb = &matroska->ctx->pb;
int len_mask = 0x80, read = 1, n = 1;
int64_t total = 0;
/* the first byte tells us the length in bytes - get_byte() can normally
* return 0, but since that's not a valid first ebmlID byte, we can
* use it safely here to catch EOS. */
if (!(total = get_byte(pb))) {
/* we might encounter EOS here */
if (!url_feof(pb)) {
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(EIO); /* EOS or actual I/O error */
}
/* get the length of the EBML number */
while (read <= max_size && !(total & len_mask)) {
read++;
len_mask >>= 1;
}
if (read > max_size) {
offset_t pos = url_ftell(pb) - 1;
av_log(matroska->ctx, AV_LOG_ERROR,
"Invalid EBML number size tag 0x%02x at pos %"PRIu64" (0x%"PRIx64")\n",
(uint8_t) total, pos, pos);
return AVERROR_INVALIDDATA;
}
/* read out length */
total &= ~len_mask;
while (n++ < read)
total = (total << 8) | get_byte(pb);
*number = total;
return read;
}
/*
* Read: the element content data ID.
* Return: the number of bytes read or < 0 on error.
*/
static int
ebml_read_element_id (MatroskaDemuxContext *matroska,
uint32_t *id,
int *level_up)
{
int read;
uint64_t total;
/* if we re-call this, use our cached ID */
if (matroska->peek_id != 0) {
if (level_up)
*level_up = 0;
*id = matroska->peek_id;
return 0;
}
/* read out the "EBML number", include tag in ID */
if ((read = ebml_read_num(matroska, 4, &total)) < 0)
return read;
*id = matroska->peek_id = total | (1 << (read * 7));
/* level tracking */
if (level_up)
*level_up = ebml_read_element_level_up(matroska);
return read;
}
/*
* Read: element content length.
* Return: the number of bytes read or < 0 on error.
*/
static int
ebml_read_element_length (MatroskaDemuxContext *matroska,
uint64_t *length)
{
/* clear cache since we're now beyond that data point */
matroska->peek_id = 0;
/* read out the "EBML number", include tag in ID */
return ebml_read_num(matroska, 8, length);
}
/*
* Return: the ID of the next element, or 0 on error.
* Level_up contains the amount of levels that this
* next element lies higher than the previous one.
*/
static uint32_t
ebml_peek_id (MatroskaDemuxContext *matroska,
int *level_up)
{
uint32_t id;
if (ebml_read_element_id(matroska, &id, level_up) < 0)
return 0;
return id;
}
/*
* Seek to a given offset.
* 0 is success, -1 is failure.
*/
static int
ebml_read_seek (MatroskaDemuxContext *matroska,
offset_t offset)
{
ByteIOContext *pb = &matroska->ctx->pb;
/* clear ID cache, if any */
matroska->peek_id = 0;
return (url_fseek(pb, offset, SEEK_SET) == offset) ? 0 : -1;
}
/*
* Skip the next element.
* 0 is success, -1 is failure.
*/
static int
ebml_read_skip (MatroskaDemuxContext *matroska)
{
ByteIOContext *pb = &matroska->ctx->pb;
uint32_t id;
uint64_t length;
int res;
if ((res = ebml_read_element_id(matroska, &id, NULL)) < 0 ||
(res = ebml_read_element_length(matroska, &length)) < 0)
return res;
url_fskip(pb, length);
return 0;
}
/*
* Read the next element as an unsigned int.
* 0 is success, < 0 is failure.
*/
static int
ebml_read_uint (MatroskaDemuxContext *matroska,
uint32_t *id,
uint64_t *num)
{
ByteIOContext *pb = &matroska->ctx->pb;
int n = 0, size, res;
uint64_t rlength;
if ((res = ebml_read_element_id(matroska, id, NULL)) < 0 ||
(res = ebml_read_element_length(matroska, &rlength)) < 0)
return res;
size = rlength;
if (size < 1 || size > 8) {
offset_t pos = url_ftell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Invalid uint element size %d at position %"PRId64" (0x%"PRIx64")\n",
size, pos, pos);
return AVERROR_INVALIDDATA;
}
/* big-endian ordening; build up number */
*num = 0;
while (n++ < size)
*num = (*num << 8) | get_byte(pb);
return 0;
}
/*
* Read the next element as a signed int.
* 0 is success, < 0 is failure.
*/
static int
ebml_read_sint (MatroskaDemuxContext *matroska,
uint32_t *id,
int64_t *num)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -