Search code examples
c++ffmpegwebrtc

How to send ffmpeg AVPacket through WebRTC (using libdatachannel)


I'm encoding a video frame with the ffmpeg libraries, generating an AVPacket with compressed data.

Thanks to some recent advice here on S/O, I am trying to send that frame over a network using the WebRTC library libdatachannel, specifically by adapting the example here:

https://github.com/paullouisageneau/libdatachannel/tree/master/examples/streamer

I am seeing problems inside h264rtppacketizer.cpp (part of the library, not the example) which are almost certainly to do with how I'm providing the sample data. (I don't think that this is anything to do with libdatachannel specifically, it will be an issue with what I'm sending)

The example code reads each encoded frame from a file, and populates a sample by setting the content of the file to the contents of the file:

sample = *reinterpret_cast<vector<byte> *>(&fileContents);

sample is just a std::vector<byte>;

I have naively copied the contents of an AVPacket->data pointer into the sample vector:

sample.resize(pkt->size);
memcpy(sample.data(), pkt->data, pkt->size * sizeof(std::byte));    

but the packetizer is falling over when trying to get length values out of that data. Specifically, in the following code, the first iteration gets a length of 1, but the second, looking up index 5, gives 1119887324. This is way too big for my data, which is only 3526 bytes (the whole frame is a single colour so likely to be small once encoded):

while (index < message->size()) {
assert(index + 4 < message->size());
auto lengthPtr = (uint32_t *)(message->data() + index);
uint32_t length = ntohl(*lengthPtr);
auto naluStartIndex = index + 4;
auto naluEndIndex = naluStartIndex + length;
assert(naluEndIndex <= message->size());    
        
auto begin = message->begin() + naluStartIndex;
auto end = message->begin() + naluEndIndex;
nalus->push_back(std::make_shared<NalUnit>(begin, end));
index = naluEndIndex;
}

Here is a dump of

uint32_t length = ntohl(*lengthPtr);

for the first few elements of the message (*lengthPtr in parentheses):

[2022-03-29 15:12:01.182] [info] index 0: 1  (16777216)
[2022-03-29 15:12:01.183] [info] index 1: 359  (1728118784)
[2022-03-29 15:12:01.184] [info] index 2: 91970  (1114046720)
[2022-03-29 15:12:01.186] [info] index 3: 23544512  (3225577217)
[2022-03-29 15:12:01.186] [info] index 4: 1732427807  (532693607)
[2022-03-29 15:12:01.187] [info] index 5: 1119887324  (3693068354)
[2022-03-29 15:12:01.188] [info] index 6: 3223313413  (98312128)
[2022-03-29 15:12:01.188] [info] index 7: 534512896  (384031)
[2022-03-29 15:12:01.188] [info] index 8: 3691315291  (1526728156)
[2022-03-29 15:12:01.189] [info] index 9: 83909537  (2707095557)
[2022-03-29 15:12:01.189] [info] index 10: 6004992  (10574592)
[2022-03-29 15:12:01.190] [info] index 11: 1537277952  (41307)
[2022-03-29 15:12:01.190] [info] index 12: 2701131779  (50331809)
[2022-03-29 15:12:01.192] [info] index 13: 768  (196608)

(I know I should post a complete sample, I am working on it)

  • I am fairly sure I am just missing something basic. E.g. am I supposed to do something with the AVPacket side_data, does AVPacket have or miss some header info?

  • If I just fwrite the pkt->data for a single frame to disk, I can read the codec information with ffprobe:

Input #0, h264, from 'encodedOut.h264':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: h264 (Constrained Baseline), yuv420p(progressive), 1280x720, 30 tbr, 1200k tbn

Update: This issue is solved by changing the H264RtpPacketizer separator setting from H264RtpPacketizer::Separator::Length to H264RtpPacketizer::Separator::LongStartSequence, many thanks to author of libdatachannel paullouisageneau (see answer below)

I have issues related to settings for the libx264 encoder, but can happily encode with h264_nvenc and h264_mf


Solution

  • The input files of the streamer example for libdatachannel use 32-bit length as NAL unit separator. Therefore, the H264RtpPacketizer instance is created with H264RtpPacketizer::Separator::Length.

    If I'm not mistaken the ffmpeg output will have 4-byte start sequences as NAL unit prefix instead (which is actually more common), so if you change the packetizer setting to H264RtpPacketizer::Separator::LongStartSequence it should accept your sample.