Sunday, September 6, 2009

Notes on Speex

I've been working on improving the Speex support in FFmpeg. I pretty much have everything completed except for getting it all through review to SVN.

I started out just making an encoder for libavcodec based on libspeex, along with adding support for Speex in the Ogg muxer. This was fairly simple. Art Clarke of Xuggler also submitted similar patches around the same time, so there was some good discussion as well as some shared code.

The Ogg muxer is obviously needed first, so I tested it using stream copy from Ogg-to-Ogg and FLV-to-Ogg. In both cases I ran into problems with a small pop near the beginning of the output file when decoded using speexdec. After about a full day of debugging, I managed to figure out the problem. The first frame is supposed to have skipped samples due to transmission delay. The way this is communicated is through Ogg granule positions. However, the first granule position reflects the last sample of the last frame in the first Ogg page. In order to determine how much of the first frame to skip, one must calculate what that granule position would normally be without skipping, then calculate the difference. I submitted a patch to ffmpeg-devel to handle this in the Ogg demuxer. As a side note, it seems that Adobe Flash Media Server does not take this into account, and therefore the timestamps skip samples in the middle of the stream instead of at the start. So stream copy of those files from FLV-to-Ogg will always produce incorrect granule positions.

The libspeex decoder should also be modified to skip these samples in the first frame. I have a patch ready for that. Cutting off samples from the last frame is not possible though because you never know if you're encoding the last frame. That's not as big a deal though.

Speex-in-FLV muxing support is a pretty simple and straightforward patch, but should not be committed until the Ogg timestamp issues are fixed to prevent more broken files in the wild. The same goes for the Speex-in-Ogg muxing.

Today I have been cleaning up the libspeex encoder for lavc. Speex is weird in that it can use either a quality setting or bitrate to set the CBR rate. My proposal will be to add a flag2 for explicitly enabling VBR. That way it will not just depend on CODEC_FLAG_QSCALE like in most other audio encoders. For setting frames-per-packet, the user can set AVCodecContext.frame_size to a multiple of the Speex frame size (e.g. 960 in wideband mode would be 3 frames-per-packet). Complexity is set using AVCodecContext.compression_level.

After I get all this through review (hopefully it won't take terribly long) I think I'll try adding support for vorbiscomment muxing in lavf.