[theora-dev] Theora packets with granulepos of -1
salsaman at gmail.com
Mon Feb 23 07:07:11 PST 2009
On Mon, Feb 23, 2009 at 7:35 AM, Robin Siegemund
<r.siegemund at digitalpublishing.de> wrote:
> Hi Gabriel,
> thanks for your reply. Well, I haven't take a look into your code yet because my algorithm to seek to a target frame works very similar and I'm also not so good at reading foreign code... at least I get a headache most times. ;)
> I just want to understand every piece of code I need to do a seeking operation and if I read your method I see that I understand it right... but ok, I haven't verified the first frame if it's starting at 0 or 1, I thought this is specified exactly.
Yes, it is specified, but I discovered there was a bug in some earlier
versions of the encoder which resulted in the wrong value being used.
If you try to decode one of these files, it will not work without the
> But also this time I'm confused again. In my current code I use the keyframe offset in the theora header to do some calculations related to the offsets of the keyframes or just for debugging. This offset does always work on all of my ogg theora files which are up to 90 seconds in length so there you get a keframe every 64th frame.
> Recently I encoded another theora stream which has a length of about 4 minutes. And here I don't get a keyframe every 64th frame even if I name it in the ffmpeg2theora command line parameters. The keyframe distance varies as the oggzdump output shows:
> 00:00:31.333: serialno 0000005187, granulepos 430|40, packetno 472
> 00:00:31.400: serialno 0000005187, calc. gpos 471|0, packetno 473
> 00:00:34.266: serialno 0000005187, granulepos 471|43, packetno 516
> 00:00:34.333: serialno 0000005187, granulepos 515|0, packetno 517
> It's very confusing to see that no one of my >100 older theora files have varying keyframe offsets but this one. (?)
> So, uhm... another of my assumptions from earlier theora based development failed. This happened quite often last days so seeking seems to be THE way to really understand (or at least experience) what is really going on under the hood.
That is correct. You have to calculate the keyframe granule shift for
each file. For some strange reason best known to the developers this
is split over 2 bytes:
keyframe_granule_shift = (char) ((hpacket & 0x03) << 3); // lower 3 bits
keyframe_granule_shift |= (hpacket & 0xe0) >> 5; // top 5 bits
where hpacket is the (first) theora header packet.
> You were asking about seeking,
> Seeking in ogg is not easy. Here is the method I devised, which seems
> the most efficient:
> 1) Parse stream headers to get data. Some files have a first keyframe
> of 0, others have a first keyframe of 1.
> 2) Begin reading pages until we get at least one packet. Then check
> the granulepos to find the first keyframe number (0 or 1). Store this
> as e.g. keyframe_offset.
> 3) To get a target frame, first search through the file to find the
> largest granulepos<=target, taking into account the keyframe_offset.
> From this we get the keyframe of the target.
> 4) Subtract 1 from the keyframe, then repeat step 3).
> 5) Begin reading from the frame discovered in step 4. Drop any packets
> which are output on the first page. Count down until we reach the
> keyframe, dropping packets until then.
> 6) Continue counting down until we reach the target frame, we are now
> decoding each frame/packet. At the target frame produce the YUV
> You can see this in code here:
> My code also builds an index of file_offset/granulepos at it discovers
> them, so it is ultra-efficient.
> Feel free to make use of this code.
> By the way, if anybody wants to use this code in liboggz I would
> recommend it. I and other users have been testing it for several weeks
> now, and this code is running flawlessly. There was one problem with
> files with odd sized heights, that has been taken care of now.
> On Mon, Feb 16, 2009 at 9:20 AM, Conrad Parker <conrad at metadecks.org> wrote:
>> 2009/2/16 Robin Siegemund <r.siegemund at digitalpublishing.de>:
>>> Thanks for your fast reply.
>>> Well, my decoding loop is based on the sample code that comes with Theora so there are all necessary checks included. I think there is even no other way to decode video/audio properly without checking the function results because you need the feedback if more data is needed, a page is ready, packetOut is successfully, etc.
>>> And those -1 packets have correct packet numbers (in order) and they are true content data.
>>> I also checked out the results from oggzdump. But this tool is even more confusing because it seems not telling the truth. In the output you have packets like:
>>> 00:00:11.200: serialno 0000025769, granulepos 129|39, packetno 170: 513 bytes
>>> But also packets like:
>>> 00:00:11.266: serialno 0000025769, calc. gpos 129|40, packetno 171: 1.013 kB
>>> So why it shows "calc. gpos"? I think this means oggzdump has to calculate the position because it's originally not there. So I suppose it's the same issue here but the tool doesn't want to print a -1.
>> that's right -- there is no granulepos recorded in the file for those
>> packets. Oggz is calculating the granulepos by keeping track of frame
>> numbers and parsing the Theora packet headers.
>> Older versions of oggz-dump printed -1, before the code to calculate
>> the missing values was added.
>>> It should be mentioned that I still use an aged 1.0alpha7 version of libtheora but if the -1 granulepos is already in some packets of a file then this is not decoder related at all. So maybe there was a bug in the old encoder and the latest ffmpeg2theora still uses it, I don't know.
>> It's not a bug (in theora or in your code). The granulepos of a packet
>> is only recorded in the stream if it is the last packet ending on some
>> page. So even in a case as simple as having a page that contains two
>> complete packets, one after another, the first one will not have its
>> granulepos recorded in the stream -- even though the encoder passed a
>> granulepos value to libogg for writing.
>> When that stream is parsed with libogg, libogg will set a value of -1
>> in the ogg_packet structure for that first packet of the page.
>> Ogg is a lossy container format, in that it will discard some
>> timestamp information. (That information can usually be reconstructed,
>> as oggz does, with some understanding of the codec).
>> This is one reason that Ogg formats which require granulepos on all
>> packets (like CMML and Kate :) mandate that only one packet is allowed
>> per page.
>> theora-dev mailing list
>> theora-dev at xiph.org
> theora-dev mailing list
> theora-dev at xiph.org
More information about the theora-dev