Closed BlackHoleFox closed 2 years ago
Thanks for the report. To start with the good news, the stream can actually decode, i.e., this works:
#[test]
fn decode() -> Result<(), Error> {
let src = &include_bytes!("data/stream_dump.bin")[..];
let packet_lengths = [30, 8, 25656];
let config = DecoderConfig::default().debug(false);
let mut decoder = Decoder::with_config(config)?;
let mut p = 0;
for l in packet_lengths {
if l == 8 {
p += l;
continue;
}
decoder.decode(&src[p..p + l])?;
p += l;
}
Ok(())
}
The issue is the 8 byte packet. Inspecting the file with an H264 analyzer it looks like so:
The 8 bytes is what trips OpenH264 up. This is not just an issue of this lib, both the H264 analyzer I used above doesn't quite know what the packet is, as well as h264dec
fails this packet with an error, but then continues anyway:
H264 source file name: x.264..
Sequence output file name: 1.yuv..
------------------------------------------------------
(1, 21),
(23, 7),
[OpenH264] this = 0x0x7fffee0d7080, Error:NAL_UNIT_PREFIX: DecInitBits() fail due invalid access.
(31, 7),
(39, 25655),
-------------------------------------------------------
iWidth: 2960
height: 1440
Frames: 1
decode time: 0.049273 sec
FPS: 20.295091 fps
-------------------------------------------------------
I'm think that packet can safely be ignored, and I think this might even be what VLC (or a reasonable decoder) should be doing, and maybe there's even a OpenH264 option for that.
With all that said there are 2 options forward:
h264dec
does: first segment the stream into NALs, then decode them one-by-one, skipping over failing packets. I think we should do both. The second option will address your problem and I we should have an API for that in any case. The first one is a bit of an unknown, but it would be good to export more encoder / decoder options anyway.
Unfortunately I won't have much time doing either, but I'd very much welcome PRs.
My preferred solution for the segmentation part would be a separate API call accepting a &[u8]
in return emitting an Iterator<&[u8]>
, splitting the original stream at NAL boundaries. That call should be non-allocating, and either use some hand-crafted NAL parsing (I think that shouldn't be too hard, but haven't tried) or use OpenH264 for that, which I believe has an API built in (but might do some ugly things like aliasing the original buffer or use an internal allocation).
In any case, once that API exists decoding should look as simple as:
#[test]
fn decode() -> Result<(), Error> {
let src = &include_bytes!("data/stream_dump.bin")[..];
let config = DecoderConfig::default().debug(false);
let mut decoder = Decoder::with_config(config)?;
for packet in todo_split_nal(src) {
// If this fails you should continue anyway as some NALs might just be corrupt.
let _result = decoder.decode(packet);
}
Ok(())
}
Turns out that was really just trivially splitting for 001
, fixed and pushed in 0.2.7.
That was a very quick fix since I didn't even have a chance to open this issue back up yet today :) Thanks a ton for the great explanation into why this occurred. Thats a huge help for me as I'm not anywhere near knowledgeable in A/V work.
For the actual fix, once again thanks. Also sorry for essentially forcing you to write out a clause about decoder errors not being actual, uh, bugs. I'll keep that in mind in the future.
Hiya. Thanks for making this crate, its looking like a much better option then dragging in all of
ffmpeg
by a mile :)While trying to decode an H264 video stream coming from an Android phone, I've kept running into issues with the library making bad accesses and then erroring out. While I'm not sure how much of it is my fault, I haven't been able to figure out what, if anything, I'm doing wrong that'd lead to issues. fwiw, ffmpeg decodes the frames without issue.
I threw together a minimal reproduction below and included the H264 stream bytes up until the point it fails. Thanks in advance if or when you get a chance to look at it.
Reproduction source:
Packet stream: stream_dump.zip
OpenH264 logs: