0

Let me begin by saying that I'm not sure on which site on the Stack Exchange network I should post this, as the central theme is essentially codec internals.

Background

I would like to be able to generate audio messages from a specified set by concatenating the right files.

0s       1s      2s
|  /A1\  |       |
| //A2\\ | /B1\  |  /C1\
|...A3...|..B2...|...C2...
| \\A4// | \B3/  |
|  \A5/  |       |

For example,

cat A1.bin B3.bin C2.bin > result.ogg

There is absolutely no way to inject more smarts into this part.

After some investigation, I'm convinced that the Ogg container format is capable of doing this, as long as all alternatives in one group have the same sample length and span the same number of Ogg pages. Specifically, you need to do the following:

  • Remove the pages with granule position 0 from all files except the first set, as that is the header.
  • Disable the this-is-the-last-page bit in all files except the last set.
  • Set the bitstream serial number to the same value in all files.
  • Compute the page sequence numbers and granule positions each file will be starting at, and offset the pages appropriately.
  • Correct the CRC checksums of the pages.

Long story short, it works. Almost.

The problem

Aligning a set of files to have the exact same granule length is proving difficult. It seems that the vorbis packets themselves have only an approximate length, and the exact value is set by the granule position of the last page. Specifically, if I trim the file as follows

$ ffmpeg -i input.ogg -acodec libvorbis -af atrim=end_sample=39690 output.ogg
[...]
$ oggz dump middle/0.ogg | grep 'serialno' | tail -n1
00:00:00.900: serialno 0685297785, granulepos 39690, packetno 63 *** eos: 1 byte

then the granulepos is exactly 39690. However, concatenating files trimmed like this results in the following errors when using the result as input to ffmpeg

[ogg @ 0x17542c0] Non-monotonous DTS in output stream 0:0; previous: 137728, current: 137408; changing to 137728. This may result in incorrect timestamps in the output file.
[ogg @ 0x17542c0] Non-monotonous DTS in output stream 0:0; previous: 137728, current: 137536; changing to 137728. This may result in incorrect timestamps in the output file.
[ogg @ 0x17542c0] Non-monotonous DTS in output stream 0:0; previous: 137728, current: 137664; changing to 137728. This may result in incorrect timestamps in the output file.
[many similar messages left out]

Moreover, when playing the file in VLC, it skips after around 8 concatenated files. This isn't reproducible when playing the file in Firefox or Audacity, though.

If I take the trimmed file and transcode it without the end_sample parameter, it gains a few more samples:

$ ffmpeg -i output.ogg output2.ogg
[...]
$ oggz dump 0.ogg | grep 'serialno' | tail -n1
00:00:00.915: serialno 18446744073311635454, granulepos 40384, packetno 51 *** eos: 1 byte

When I concatenate my files like that, the VLC skipping and ffmpeg diagnostics disappear. However, the files are all a different granule length.

So, my question is, effectively: how can I trim an audio file to a specific number of samples, such that it sticks after reencoding?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.