[FFmpeg-devel] [PATCH] Matroska Muxer
David Conrad
umovimus
Mon Aug 20 22:30:32 CEST 2007
On Aug 14, 2007, at 3:13 PM, Michael Niedermayer wrote:
> Hi
>
> On Mon, Aug 13, 2007 at 08:38:25PM -0400, David Conrad wrote:
>
>> Hi,
>>
>> I feel that my GSoC is ready for SVN. It does have a couple of known
>> limitations:
>>
>>
> [...]
>
>> +// 2 bytes * 3 for EBML IDs, 3 1-byte EBML lengths, 8 bytes for
>> 64 bit offset, 4 bytes for target EBML ID
>> +#define MAX_SEEKENTRY_SIZE 21
>>
>
> not doxygen compatible comment
Fixed, I think.
>> +
>> +// per-cuepoint-track - 3 1-byte EBML IDs, 3 1-byte EBML sizes, 2
>> 8-byte uint max
>> +#define MAX_CUETRACKPOS_SIZE 22
>> +
>> +// per-cuepoint - 2 1-byte EBML IDs, 2 1-byte EBML sizes, 8-byte
>> uint max
>> +#define MAX_CUEPOINT_SIZE(num_tracks) 12 +
>> MAX_CUETRACKPOS_SIZE*num_tracks
>> +
>> +
>> +static int ebml_id_size(unsigned int id)
>> +{
>> + return (av_log2(id+1)-1)/7+1;
>> +}
>> +
>> +static void put_ebml_id(ByteIOContext *pb, unsigned int id)
>> +{
>> + int i = ebml_id_size(id);
>> + while (i--)
>> + put_byte(pb, id >> (i*8));
>> +}
>> +
>> +/**
>> + * Write an EBML size meaning "unknown size"
>> + *
>> + * @param bytes The number of bytes the size should occupy.
>> Maximum of 8.
>> + */
>> +static void put_ebml_size_unknown(ByteIOContext *pb, int bytes)
>> +{
>> + uint64_t value = 0;
>> + int i;
>> +
>> + bytes = FFMIN(bytes, 8);
>>
>
> what is this good for? >8 is not supported yes but it wont work any
> better
> with such checks if its over 8
True, changed to an assert.
>> + for (i = 0; i < bytes*7 + 1; i++)
>> + value |= 1ULL << i;
>> + for (i = bytes-1; i >= 0; i--)
>> + put_byte(pb, value >> i*8);
>>
>
> put_byte(pb, 0x1FF>>bytes);
> while(--bytes)
> put_byte(pb, 0xFF);
Fixed.
>> +}
>> +
>> +/**
>> + * Calculate how many bytes are needed to represent a given size
>> in EBML
>> + */
>> +static int ebml_size_bytes(uint64_t size)
>> +{
>> + int bytes = 1;
>> + while ((size+1) >> bytes*7) bytes++;
>> + return bytes;
>> +}
>>
>
> isnt ebml_size_bytes and ebml_id_size the same if the IDs would be
> stored
> properly?
> i mean currently the #define *_ID_* is in encoded form while size
> of course
> cannot be, so if we would change the #defines to be in normal form
> then i
> think this could allow some simplifications, though i might be
> wrong ...
I think this is possible, the only caveat is that if IDs with all
ones are ever used (they're currently reserved) then this won't work
for them. I'll send a patch changing the demuxer, then change the muxer.
>> +
>> +/**
>> + * Write a size in EBML variable length format.
>> + *
>> + * @param bytes The number of bytes that need to be used to write
>> the size.
>> + * If zero, any number of bytes can be used.
>> + */
>> +static void put_ebml_size(ByteIOContext *pb, uint64_t size, int
>> bytes)
>> +{
>> + int i, needed_bytes = ebml_size_bytes(size);
>> +
>>
>
>
>> + // sizes larger than this are currently undefined in EBML
>> + // so write "unknown" size
>> + if (size >= (1ULL<<56)-1) {
>> + put_ebml_size_unknown(pb, 1);
>> + return;
>> + }
>> +
>> + if (bytes == 0)
>> + // don't care how many bytes are used, so use the min
>> + bytes = needed_bytes;
>> + else if (needed_bytes > bytes) {
>> + // the bytes needed to write the given size would exceed
>> the bytes
>> + // that we need to use, so write unknown size. This
>> shouldn't happen.
>>
>
> if it shoulnt happen then it should be an assert()
Fixed.
> [...]
>
>> + * @param size The number of bytes to reserve, which must be at
>> least 2.
>> + */
>> +static void put_ebml_void(ByteIOContext *pb, uint64_t size)
>> +{
>> + offset_t currentpos = url_ftell(pb);
>> +
>> + if (size < 2)
>> + return;
>>
>
> that should be an assert() and of course size must never be <2
> all checks which cant be true unless theres a bug somewhere in our
> code
> should be assert()
I changed all of the checks that shouldn't happen to assert()s, I think.
> [...]
>
>> +static int mkv_add_seekhead_entry(mkv_seekhead *seekhead,
>> unsigned int elementid, uint64_t filepos)
>> +{
>> + mkv_seekhead_entry *entries = seekhead->entries;
>> + int new_entry = seekhead->num_entries;
>> +
>> + // don't store more elements than we reserved space for
>> + if (seekhead->max_entries > 0 && seekhead->max_entries <=
>> seekhead->num_entries)
>> + return -1;
>> +
>> + entries = av_realloc(entries, (seekhead->num_entries + 1) *
>> sizeof(mkv_seekhead_entry));
>> + if (entries == NULL)
>> + return -1;
>>
>
> this should be AVERROR(ENOMEM)
> also not every call to this function checks the return value
Both fixed as well as for mkv_add_cuepoint().
>> +
>> + entries[new_entry].elementid = elementid;
>> + entries[new_entry].segmentpos = filepos - seekhead-
>> >segment_offset;
>> +
>> + seekhead->entries = entries;
>> + seekhead->num_entries++;
>>
>
> the code mixes seekhead->num_entries and the local copy new_entry
>
> i would write
> entries[seekhead->num_entries ].elementid = elementid;
> entries[seekhead->num_entries++].segmentpos = filepos - seekhead-
> >segment_offset;
>
> but using new_entry everywhere is fine too, just the mix of both is
> a little confusing
Okay, I agree that this is a bit clearer, so changed for both
seekhead and cues.
> [...]
>
>
>> + case CODEC_TYPE_SUBTITLE:
>> + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE,
>> MATROSKA_TRACK_TYPE_SUBTITLE);
>> + break;
>> + default:
>> + av_log(s, AV_LOG_ERROR, "Only audio and video are
>> supported for Matroska.");
>> + break;
>>
>
> indention, and contradiction
Fixed.
> [...]
>
>> + mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT, 0);
>> + mkv->segment_offset = url_ftell(pb);
>> +
>> + // we write 2 seek heads - one at the end of the file to
>> point to each cluster, and
>> + // one at the beginning to point to all other level one
>> elements (including the seek
>> + // head at the end of the file), which isn't more than 10
>> elements if we only write one
>> + // of each other currently defined level 1 element
>> + mkv->main_seekhead = mkv_start_seekhead(pb, mkv-
>> >segment_offset, 10);
>>
>
> i assume matroska needs seekable destination ...
It is possible to write matroska as a stream, but it's necessary to
write lots of unknown sizes and I'm not sure how well parsers
currently deal with that. At any rate, I think it should work now
that seeks aren't required to write the extradata and other
unnecessary seeks aren't done.
> [...]
>
>> + mkv->duration = pkt->pts + pkt->duration;
>>
>
> FFMAX(mkv->duration, pkt->pts + pkt->duration);
Fixed.
New patch minus the combining of ebml_id_size/put_ebml_id with
ebml_size_bytes/put_ebml_size attached.
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: mkvenc.txt
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20070820/d5421af7/attachment.txt>
More information about the ffmpeg-devel
mailing list