📄 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; //..} 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 intebml_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 intebml_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 intebml_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 intebml_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_tebml_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 intebml_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 intebml_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 intebml_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 intebml_read_sint (MatroskaDemuxContext *matroska, uint32_t *id, int64_t *num){ ByteIOContext *pb = matroska->ctx->pb; int size, n = 1, negative = 0, 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 sint element size %d at position %"PRId64" (0x%"PRIx64")\n", size, pos, pos); return AVERROR_INVALIDDATA; } if ((*num = get_byte(pb)) & 0x80) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -