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

Implement SDL 0048 RTP Video Encoder #703

Conversation

shoamano83
Copy link
Contributor

@shoamano83 shoamano83 commented Aug 23, 2017

Fixes #619

This PR is ready for review.

Risk

This PR makes minor API changes.

This PR adds new public methods in SDLStreamingMediaManager, SDLStreamingMediaLifecycleManager and SDLVideoEncoder. Also, it updates the default flow of video streaming in sdl_videoEncoderOutputCallback().

I carefully implemented the changes not to break compatibility and I believe the risk is low, but I hope them to be double checked.

Testing Plan

Unit test cases are added for newly added classes.

Summary

Implements SDL 0048

This PR adds RTP packetizer implementation which is necessary for SDL-0048.

It also adds following changes which are necessary for the RTP format to work:

  • Existing implementation of SDLVideoEncoder is split into two parts. One is to extract NAL units from sample buffer, and the other is to add start codes in front of every NAL unit (which is in SDLH264ByteStreamPacketizer class)
  • New public method -sendVideoData:pts: is added so that SDL app can provide PTS (presentation timestamp) along with the video frame

Changelog

Breaking Changes
  • None
Enhancements
  • Update SDLVideoEncoder to extract list of NAL units from encoded data
  • Add SDLH264ByteStreamPacketizer to append start codes in front of the NAL units
  • Add SDLRTPH264Packetizer to create RTP packets from the NAL units
  • Add public method -sendVideoData:pts:
Bug Fixes
  • None

Tasks Remaining:

  • None

CLA

This packetizer sends H.264 video stream using
RTP frames defined by RFC 6184 and RFC 4571.
Also, latest PTS value is kept in SDLStreamingMediaLifecycleManager
so that video image with outdated PTS will be rejected.
@shoamano83
Copy link
Contributor Author

@joeljfischer Please kindly review.

@joeljfischer joeljfischer changed the title Feature/rtp packetizer 5.0.0 RTP Video Encoder Aug 23, 2017
@joeljfischer joeljfischer changed the title RTP Video Encoder Implement SDL 0048 RTP Video Encoder Aug 23, 2017
@joeljfischer
Copy link
Contributor

@shoamano83 Because this adds a method to SDLStreamingMediaManager, which is a public class, this proposal makes minor API changes. I've updated your PR post accordingly.

@joeljfischer joeljfischer self-requested a review August 23, 2017 13:38
@joeljfischer joeljfischer added enhancement proposal Accepted SDL Evolution Proposal labels Aug 23, 2017
@joeljfischer joeljfischer added this to the 5.0.0 milestone Aug 23, 2017
Copy link
Contributor

@joeljfischer joeljfischer left a comment

Choose a reason for hiding this comment

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

Hi @shoamano83, mostly asking for small changes. One big current question is that this appears to be adding code for RTP, but it's not currently enabled. I'm assuming that it won't be until the control payload / video capabilities PR for 5.0?

I've tested against Ford's Sync 3 TDK and everything is still working there, so I just still need to test against your recommended GStreamer settings.

// SmartDeviceLink-iOS
//
// Created by Sho Amano on 4/11/17.
// Copyright © 2017 Xevo Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Please change this to how other files do, you can either remove the copyright entirely or alter it to say "Copyright © 2017 SmartDeviceLink Consortium, Inc."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @joeljfischer, I’m aware of the copyright headers in other files, but it is a request from our legal department to add our company name in the copyright header for newly created and updated files. I hope this is OK for you.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍


#import <Foundation/Foundation.h>

@protocol SDLH264Packetizer
Copy link
Contributor

Choose a reason for hiding this comment

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

All protocols, extensions, and classes need to be wrapped in NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END and nullable tags when the developer is allowed to pass nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed by afecd28.

* All NAL units that belongs to a frame should be included in
* nalUnits array.
*/
- (NSArray *)createPackets:(NSArray *)nalUnits pts:(double)pts;
Copy link
Contributor

Choose a reason for hiding this comment

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

All container classes need generics to specify what is to be passed in or out.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to see the pts:pts part expanded out of the acronym to presentationTimestamp:(double)presentationTimestamp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed by 107382f.

*
* @return List of NSData. Each NSData holds a packet.
*
* @note This method cannot be called more than once with same pts value.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be @warning instead of @note

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated by 107382f.

*
* @note This must be between 0 and 127. Default value is 96.
*/
@property (assign, nonatomic) UInt8 payloadType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a link or reference to explain what this means?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've put some documentation with commit d585057. Hope this will make clearer.

const UInt8 DEFAULT_PAYLOAD_TYPE = 96;
const NSUInteger FU_INDICATOR_LEN = 1;
const NSUInteger FU_HEADER_LEN = 1;
const UInt8 TYPE_FU_A = 0x1C;
Copy link
Contributor

Choose a reason for hiding this comment

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

Unsure what "FU" or "A" are.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"A" seems to be just a name of a version (there are "FU-A" and "FU-B" in the RFC). I've updated the constant's name in d585057, hope this is OK.

*
* @return Whether or not the data was successfully encoded and sent.
*/
- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer pts:(CMTime)pts;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to see the pts:pts part expanded out of the acronym to presentationTimestamp:(CMTime)presentationTimestamp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, updated with 33b5d07.

@@ -517,8 +538,14 @@ - (void)sdl_sendBackgroundFrames {
return;
}

const CMTime interval = CMTimeMake(1, 30);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this still okay even if they're not doing 30fps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it should work, since the delta of timestamp doesn’t need to be constant all the time.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just want to make doubly sure, because devs could try to send fps anywhere from half to double this number that this is going to work for them.

*
* @return Whether or not the data was successfully encoded and sent.
*/
- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer pts:(CMTime)pts;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to see the pts:pts part expanded out of the acronym to presentationTimestamp:(CMTime)presentationTimestamp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated with 33b5d07.


- (BOOL)encodeFrame:(CVImageBufferRef)imageBuffer pts:(CMTime)pts {
if (!CMTIME_IS_VALID(pts)) {
pts = CMTimeMake(self.currentFrameNumber, 30);
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this still work even if they're not using 30fps? Shouldn't we be pulling this from the videoEncoderSettings?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this is from current implementation, I kept it as it is. (Please refer to: https://github.com/smartdevicelink/sdl_ios/blob/release/5.0.0/SmartDeviceLink/SDLVideoEncoder.m#L122)

My thought is that app should provide timestamp along with video frames, especially when RTP format is used.
If it cannot provide, then SDL Proxy may create timestamps by capturing current time when -sendVideoData: is called. I was hesitated to add this implementation because it may change behavior even when we use byte-stream format.

I’m aware of kVTCompressionPropertyKey_ExpectedFrameRate key in the configuration. But I thought we cannot guarantee that all navi apps add it in the configuration dictionary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add this as an issue? I agree with keeping it as is for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I created issue #717.

- Update "note" comment to "warning"
- Add generics to NSArray containers
- rename "pts" arg to "presentationTimestamp"
- remove redundant method declarations
- remove redundant protocol conformations
@shoamano83
Copy link
Contributor Author

Hi @joeljfischer, thank you for your detailed review.

One big current question is that this appears to be adding code for RTP, but it's not currently enabled. I'm assuming that it won't be until the control payload / video capabilities PR for 5.0?

That’s right, it is not enabled yet, and it should be enabled when the format is chosen through Constructed Payload + Video Capabilities mechanism.

- Add documentations in private functions
- Rename private functions and args
- Rename “pts” acronym
- Remove “FU” acronym in comments
- Update initializer style
- Add brackets between a boolean expression
- Use dot syntax
- Remove an extra line
Reflecting code review comment.
Reflecting code review comment.
Reflecting review comments.
@joeljfischer
Copy link
Contributor

joeljfischer commented Aug 29, 2017

Hi @shoamano83, I've tried to test the RTP streaming by changing SDLVideoEncoder.m L113 to instantiate an SDLRTPH264Packetizer and am running into issues. We couldn't get the socket to work (got connection refused, is there additional setup for using that mode?) so we attempted VideoStreamConsumer = pipe, but got invalid RTP payload errors.

We tested against a develop version of Core running on Ubuntu 16.04 with gStreamer v1.8.2.

rtp_test.txt

@shoamano83
Copy link
Contributor Author

Hi @joeljfischer, I've never tried pipe transport in my environment. Let me check and get back to you.

@shoamano83
Copy link
Contributor Author

I verified that socket as well as pipe works on my environment. Actually it's quite tricky to test with sdl_hmi and GStreamer. Here is details of what I do.

Environment:

Ubuntu 16.04 (amd64) on VirtualBox
SDL Core: develop branch, commit a2be84f2bbe469cbcaa345e812936f5e55a92291 with modification to log4cxx.properties
sdl_hmi: master branch, commit 18460356c02e985df598b94f5a6bdbba1c451fff + modification
GStreamer: 1.8.3

Mobile: iPhone 5s (iOS 8.4.1)
iOS Proxy: latest feature/rtp_packetizer_5.0.0 + modification
app: our proprietary app using OpenGL

Modifcation to sdl_hmi:

diff --git a/app/model/sdl/Abstract/Model.js b/app/model/sdl/Abstract/Model.js
index 194d30e..cb8f35e 100644
--- a/app/model/sdl/Abstract/Model.js
+++ b/app/model/sdl/Abstract/Model.js
@@ -510,11 +510,11 @@ SDL.SDLModel = Em.Object.extend({
       if (SDL.SDLController.getApplicationModel(appID).navigationStream !==
         null) {
 
-        SDL.SDLModel.data.naviVideo = document.getElementById('html5Player');
-        SDL.SDLModel.data.naviVideo.src = SDL.SDLController.getApplicationModel(
-          appID
-        ).navigationStream;
-        SDL.SDLModel.data.naviVideo.play();
+        //SDL.SDLModel.data.naviVideo = document.getElementById('html5Player');
+        //SDL.SDLModel.data.naviVideo.src = SDL.SDLController.getApplicationModel(
+        //  appID
+        //).navigationStream;
+        //SDL.SDLModel.data.naviVideo.play();
       }
     },
 

Modification to iOS proxy:

Change:

    _packetizer = [[SDLH264ByteStreamPacketizer alloc] init];

to:

    _packetizer = [[SDLRTPH264Packetizer alloc] init];

Modification to SDL Core:

Open log4cxx.properties and change this line:

log4j.rootLogger=ALL, SmartDeviceLinkCoreLogFile

to:

log4j.rootLogger=INFO, SmartDeviceLinkCoreLogFile

This prevents SDL Core from generating a huge amount of logs during streaming. In my environment they often exhaust RAM and Linux will stop working.

Steps:

  1. Launch SDL Core using $ ./start.sh
  2. Start sdl_hmi: $ google-chrome index.html
  3. On HMI, click "OK" then click "i" icon
  4. Start mobile app and connect through TCP transport
  5. Click on the app icon on HMI. (Note: sometimes the app automatically resumes and this is not necessary)
  6. HMI should ask you "Would you like to start video streaming?" so click "OK"
  7. IMMEDIATELY start GStreamer pipeline on a terminal:
$ gst-launch-1.0 souphttpsrc location=http://127.0.0.1:5050 ! "application/x-rtp-stream" ! rtpstreamdepay ! "application/x-rtp,media=(string)video,clock-rate=90000,encoding-name=(string)H264" ! rtph264depay ! "video/x-h264, stream-format=(string)avc, alignment=(string)au" ! avdec_h264 ! videoconvert ! ximagesink sync=false

I noticed that on my Ubuntu 16.04 autovideosink doesn't work for some reasons. I've updated the pipeline to use videoconvert and ximagesink which should work on most Linux environments.

For pipe streamer, update smartDeviceLink.ini and also update the gstreamer pipeline as you tried:

$ gst-launch-1.0 filesrc location=/path/to/sdl_core_build_dir/bin/storage/video_stream_pipe ! "application/x-rtp-stream" ! rtpstreamdepay ! "application/x-rtp,media=(string)video,clock-rate=90000,encoding-name=(string)H264" ! rtph264depay ! "video/x-h264, stream-format=(string)avc, alignment=(string)au" ! avdec_h264 ! videoconvert ! ximagesink sync=false

Note, the whole sequence sometimes fails (possibly because of timing issue between HMI and GStreamer?) so I try it several times.

@shoamano83
Copy link
Contributor Author

JFYI, attaching a video of the whole sequence.
MAH06813.zip

autovideosink doesn't work in some environments, replacing
it with ximagesink.
@joeljfischer joeljfischer merged commit 39b0105 into smartdevicelink:release/5.0.0 Aug 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Accepted SDL Evolution Proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants