3 ms·
The headers are actually pretty good. I learned to use libav by trial and error over years, and now I'm at the point where I can just look at the headers for a
by ReactiveJelly 5y ago
The headers are actually pretty good.
I learned to use libav by trial and error over years, and now I'm at the point where I can just look at the headers for a refresher whenever I use it.
At a very broad level:
- Encoding and decoding are both pipelines
- Codecs and formats are both part of a pipeline
- An instance of a codec or a format is called a _context_
- The two basic operations for any context are "put data in" and "pull data out"
Putting this all together, a lot of tutorials present this as a case of "Just push the input data in, then pull the output data out!" In my experience this is backwards. This leads to "pipeline buckling" where, e.g., you pull a compressed packet from the demuxer and try to push it into the decoder, and the decoder says "hey, slow down, my buffers are full". You have to just awkwardly stick that packet in your pocket until the decoder's buffers are dry again.
I have always had better luck pulling data. e.g. If you're decoding, first try to pull a decompressed YUV frame from the decoder. If that fails, then try to get a new packet from the demuxer. Only if that fails (and you're not using the default file I/O), then feed the demuxer another buffer. This way it never buckles. It results in a few redundant "Can you give me anything?" checks when warming up the pipeline, but you never have buffers awkwardly outliving a loop iteration. If a buffer enters your control, it's always because you already found an empty spot in the next stage to immediately dump it into.
- marcan_42 5y agoIn my experience, the problem with this is you're still going to run into issues with A/V sync if the input is muxed weird. You can end up having to buffer a ton of A or V before you get the packet for the timestamp in the present. I have an old project using fixed size packet buffers between the demuxer and decoders, and for some inputs it just deadlocks as one side gets full. My workaround to avoid having to buffer a potentially infinite amount of data was to instantiate the demuxer twice, once for A and once for V. Then they can run out of sync with each other in terms of the muxing, and you can keep them in sync in terms of presentation timestamps.