Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add H265RtpDepacketizer #1134

Merged

Conversation

edmonds
Copy link
Contributor

@edmonds edmonds commented Mar 18, 2024

This commit adds an H265 depacketizer which takes incoming H265 RTP packets and emits H265 access units. It is closely based on the H264RtpDepacketizer added by @Sean-Der in #1082.

I originally started with a version of this commit that was closer to the H264RtpDepacketizer and which emitted individual H265 NALUs in H265RtpDepacketizer::buildFrames(). This resulted in calling my Track::onFrame() callback for each NALU, which did not work well with the decoder that I'm using which wants to see the VPS/SPS/PPS NALUs as a unit before initializing the decoder (https://intel.github.io/libvpl/v2.10/API_ref/VPL_func_vid_decode.html#mfxvideodecode-decodeheader).

So for the H265RtpDepacketizer I've tried to make it emit access units rather than NALUs. An "access unit" is (RFC 7798):

A set of NAL units that are associated with each other according to a specified classification rule, that are consecutive in decoding order, and that contain exactly one coded picture.

"Exactly one coded picture" seems to correspond with what a caller might expect an "onFrame" callback to do. Maybe the H264RtpDepacketizer should be revised to similarly emit H264 access units rather than NALUs, too. At least, I could not find a way to receive individual NALUs from the depacketizer and run the VPL decoder without needing to do my own buffering/copying of the NALUs.

With this commit I can now do the following:

  • Generate encoded bitstream output from the Intel VPL encoder.
  • Pass the output of the encoder one frame at a time to libdatachannel's Track::send() on a track with an H265RtpPacketizer media handler.
  • Transport the video track over a WebRTC connection to a libdatachannel peer.
  • Depacketize it with the H265RtpDepacketizer media handler in this commit.
  • Pass the depacketized output via my Track::onFrame() callback to the Intel VPL decoder in "complete frame" mode (https://intel.github.io/libvpl/v2.10/API_ref/VPL_enums.html#_CPPv428MFX_BITSTREAM_COMPLETE_FRAME). Each "onFrame" callback corresponds to a single call to the decoder API to decode a frame.

@edmonds
Copy link
Contributor Author

edmonds commented Mar 18, 2024

By the way, producing access units rather than NALUs seems to be what FFmpeg's H265 RTP depacketizer does. It's kind of awkward to set up, though!

Some open items:

  • Is it always correct to build an access unit by simply prepending the start code { 0x00, 0x00, 0x00, 0x01 } to each NALU?
  • H265RtpDepacketizer::incoming() is identical to H264RtpDepacketizer::incoming() and doesn't even look all that specific to H264/H265. Should that function be factored out somehow?
  • DONL/DOND fields are always assumed to be absent. Does this need to be handled?
  • RFC 7798 § 4.4.3: "If an FU is lost, the receiver SHOULD discard all following fragmentation units in transmission order corresponding to the same fragmented NAL unit, unless the decoder in the receiver is known to be prepared to gracefully handle incomplete NAL units."

@edmonds edmonds force-pushed the h265-rtp-depacketizer branch from aba246f to 1c13f41 Compare March 18, 2024 04:16
@Sean-Der
Copy link
Contributor

Agree on NALU API being awkward!

do you have bandwidth to update H264 depacketizer I can test + approve? We should make that change ASAP before people depend on it.

or maybe this is controlled via constructor

@edmonds
Copy link
Contributor Author

edmonds commented Mar 18, 2024

do you have bandwidth to update H264 depacketizer I can test + approve? We should make that change ASAP before people depend on it.

Yeah, I should be able to take a look later today.

@paullouisageneau
Copy link
Owner

"Exactly one coded picture" seems to correspond with what a caller might expect an "onFrame" callback to do. Maybe the H264RtpDepacketizer should be revised to similarly emit H264 access units rather than NALUs, too. At least, I could not find a way to receive individual NALUs from the depacketizer and run the VPL decoder without needing to do my own buffering/copying of the NALUs.

I think you are right, it corresponds better to what the user would expect the callback to do. Additionally, it makes the API more symmetrical with packetizers.

Is it always correct to build an access unit by simply prepending the start code { 0x00, 0x00, 0x00, 0x01 } to each NALU?

This is the most common, but a short start sequence { 0x00, 0x00, 0x01 } also exists. Packetizers have an NalUnit::Separator constructor parameter to specify what they should expect, maybe depacketizers could have the same.

H265RtpDepacketizer::incoming() is identical to H264RtpDepacketizer::incoming() and doesn't even look all that specific to H264/H265. Should that function be factored out somehow?

Yes it should be, but I can do it in a second time.

Copy link
Owner

@paullouisageneau paullouisageneau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding, I have only a couple comments and suggestions.

src/h265rtpdepacketizer.cpp Outdated Show resolved Hide resolved
src/h265rtpdepacketizer.cpp Outdated Show resolved Hide resolved
src/h265rtpdepacketizer.cpp Outdated Show resolved Hide resolved
src/h265rtpdepacketizer.cpp Outdated Show resolved Hide resolved
src/h265rtpdepacketizer.cpp Outdated Show resolved Hide resolved
@edmonds edmonds force-pushed the h265-rtp-depacketizer branch from a0e2e06 to ee96c7a Compare April 13, 2024 22:31
@edmonds
Copy link
Contributor Author

edmonds commented Apr 13, 2024

Re-based on master, added fix for empty RTP packet payloads, made the start sequence configurable.

Copy link
Owner

@paullouisageneau paullouisageneau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it looks good, feel free to add you name in new files headers.

@edmonds edmonds force-pushed the h265-rtp-depacketizer branch from ee96c7a to 710023d Compare April 17, 2024 14:11
@edmonds
Copy link
Contributor Author

edmonds commented Apr 17, 2024

Thanks, it looks good, feel free to add you name in new files headers.

Done.

@paullouisageneau
Copy link
Owner

@edmonds Sorry, you need to make a couple changes after rebasing as FrameInfo now requires the payload type after #1156 was merged.

@edmonds
Copy link
Contributor Author

edmonds commented Apr 18, 2024

OK, no problem. Should be able to take a look in a couple days.

@paullouisageneau
Copy link
Owner

This would need a fix similar to #1167 to properly handle multiple NAL units.

This commit adds an H265 depacketizer which takes incoming H265 RTP packets and emits H265 access units. It is closely based on the `H264RtpDepacketizer` added by @Sean-Der in paullouisageneau#1082.

I originally started with a version of this commit that was closer to the `H264RtpDepacketizer` and which emitted individual H265 NALUs in `H265RtpDepacketizer::buildFrames()`. This resulted in calling my `Track::onFrame()` callback for each NALU, which did not work well with the decoder that I'm using which wants to see the VPS/SPS/PPS NALUs as a unit before initializing the decoder (https://intel.github.io/libvpl/v2.10/API_ref/VPL_func_vid_decode.html#mfxvideodecode-decodeheader).

So for the `H265RtpDepacketizer` I've tried to make it emit access units rather than NALUs. An "access unit" is (RFC 7798):

> A set of NAL units that are associated with each other according to a specified classification rule, that are consecutive in decoding order, *and that contain exactly one coded picture.*

"Exactly one coded picture" seems to correspond with what a caller might expect an "onFrame" callback to do. Maybe the `H264RtpDepacketizer` should be revised to similarly emit H264 access units rather than NALUs, too. At least, I could not find a way to receive individual NALUs from the depacketizer and run the VPL decoder without needing to do my own buffering/copying of the NALUs.

With this commit I can now do the following:

* Generate encoded bitstream output from the Intel VPL encoder.
* Pass the output of the encoder one frame at a time to libdatachannel's `Track::send()` on a track with an `H265RtpPacketizer` media handler.
* Transport the video track over a WebRTC connection to a libdatachannel peer.
* Depacketize it with the `H265RtpDepacketizer` media handler in this commit.
* Pass the depacketized output via my `Track::onFrame()` callback to the Intel VPL decoder in "complete frame" mode (https://intel.github.io/libvpl/v2.10/API_ref/VPL_enums.html#_CPPv428MFX_BITSTREAM_COMPLETE_FRAME). Each "onFrame" callback corresponds to a single call to the decoder API to decode a frame.
Per PR review feedback, add a parameter to the constructor for
configuring the start sequence to use when writing NALUs.
This follows the API change in paullouisageneau#1156 and is based on
the very similar change to H264RtpDepacketizer (commit
e9060bf).
Based on the fix for H264RtpDepacketizer in paullouisageneau#1167 (commit
4fc4e9b).
@edmonds edmonds force-pushed the h265-rtp-depacketizer branch from 710023d to 3dc1a17 Compare October 28, 2024 00:40
@edmonds
Copy link
Contributor Author

edmonds commented Oct 28, 2024

Sorry for the delay getting back to this!

I rebased on master, added the missing <algorithm> include, made the FrameInfo changes (due to #1156), and made a fix similar to #1167 to handle multiple NALUs.

Copy link
Owner

@paullouisageneau paullouisageneau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thank you for the work!

@paullouisageneau paullouisageneau merged commit 770d074 into paullouisageneau:master Oct 30, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants