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

[RSDK-9846] - Add broader mpeg4 support #89

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

hexbabe
Copy link
Collaborator

@hexbabe hexbabe commented Feb 7, 2025

Hinges on upstream PR here to allow for dynamic clock rate configuration.

Other big change is massaging the Generic codec format into mpeg4, when it is secretly an mpeg4 track.

Edit: tested that setting the clock rate dynamically is not needed for a healthy MPEG4 stream with mediamtx and the office physical camera. Closing upstream change for the time being.

Image of manual test with physical cam in a relevant Slack thread

@hexbabe hexbabe marked this pull request as ready for review February 11, 2025 17:11
@hexbabe
Copy link
Collaborator Author

hexbabe commented Feb 11, 2025

I can take @nicksanford off this review if priority is an issue

rtsp.go Outdated

// List of formats/codecs in priority order
codecFormats := []codecFormat{
{&h264, H264},
{&h265, H265},
{&mjpeg, MJPEG},
{&mpeg4, MPEG4},
{nil, MPEG4}, // ambiguous codec, MPEG4 can be in a generic format
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add more context here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added

@@ -976,22 +1043,33 @@ func modelToCodec(model resource.Model) (videoCodec, error) {

// getAvailableCodec determines the first supported codec from a session's SDP data
// returning Unknown if none are found.
func getAvailableCodec(session *description.Session) videoCodec {
func (rc *rtspCamera) getAvailableCodec(session *description.Session) videoCodec {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function does not need to be on the struct. I don't see it accessing any of the struct's elements,

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

rc.logger

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah I see. But why not just return the error there rather than swallow it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we return the error, we may skip over the other codecs if we ever change the priority order / add another codec

Copy link
Collaborator

Choose a reason for hiding this comment

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

Add a comment on why we swallow the error there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added

rtsp.go Outdated
Comment on lines 1060 to 1072
if codecFormat.codec == MPEG4 {
mpeg4, _, err := findMPEG4InSession(session)
switch {
case err != nil:
rc.logger.Debugf("error checking for MPEG4 format: %v", err)
case mpeg4 != nil:
return MPEG4
}
continue
}

// For all other codecs, check if format exists directly
if media := session.FindFormat(codecFormat.formatPointer); media != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's make this entire thing a case structure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I swapped the order s.t. the special case handling happens after the direct checking of the session. Should we still use case structure? It would cause duplicate code.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nah this is okay, my goal was readability and this is more readable.

rtsp.go Outdated
Comment on lines 1062 to 1068
switch {
case err != nil:
rc.logger.Debugf("error checking for MPEG4 format: %v", err)
case mpeg4 != nil:
return MPEG4
}
continue
Copy link
Collaborator

Choose a reason for hiding this comment

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

So... We check from mpeg4 to special case it because it's format is sufficiently generic, and when we have an error in this case, return. What is the continue for? Should this be default case to return the other codecs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The continue was to avoid redundant checking of media := session.FindFormat(codecFormat.formatPointer) since findMPEG4InSession already does that. I refactored it so that we don't continue and decoupled the special generic handling from the direct check.

rtsp.go Outdated Show resolved Hide resolved
rtsp.go Outdated
Comment on lines 695 to 696
if !strings.HasPrefix(strings.ToUpper(generic.RTPMap()), "MP4V-ES/") {
return nil, fmt.Errorf("generic format is not MPEG4: %s", forma.Codec())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does blueenviron have a checker for this logic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

After looking for a bit, I could not find one

rtsp.go Outdated
Comment on lines 712 to 713
if profileID, ok := fmtp["profile-level-id"]; ok {
id, err := strconv.Atoi(profileID)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's return after !ok and err rather than nesting if statements.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Using early return now

rtsp.go Outdated Show resolved Hide resolved
rtsp.go Outdated Show resolved Hide resolved
@randhid
Copy link
Collaborator

randhid commented Feb 11, 2025

Do we have a plan for testing?

@hexbabe
Copy link
Collaborator Author

hexbabe commented Feb 11, 2025

Do we have a plan for testing?

So far I've tested

  • ffmpeg + mediamtx with various stream configurations e.g.
ffmpeg -re -f lavfi -i testsrc=size=640x480:rate=30 \
-c:v mpeg4 -pix_fmt yuv420p -r 30 -g 60 -bf 2 \
-f rtsp -rtsp_transport tcp rtsp://127.0.0.1:8554/live.stream
  • Physical camera in the office

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.

2 participants