twilio / video-quickstart-ios

Twilio Video Quickstart for iOS
https://www.twilio.com/docs/api/video
MIT License
466 stars 178 forks source link

TVIH264Codec causes black frames #275

Closed sdgandhi closed 5 years ago

sdgandhi commented 6 years ago

Description

Using the TVIH264Codec causes black frames on the receiver's end. When screen capturing AR from SceneKit using technique from sample app. Without preferring the H264 coded, the frames are transferred correctly.

Steps to Reproduce

Code

builder.preferredVideoCodecs = [TVIH264Codec()]

Expected Behavior

Preferring the TVIH264Codec should send correct frames.

Actual Behavior

Receiver gets black frames

Reproduces How Often

100%

Logs

I'm seeing a few of these in the debug log on the sender side.

2018-06-01 19:06:06.760469-0700 [App Name][668:180076] WARN:TwilioVideo:[Signaling]:RESIP::TRANSPORT: Can't find matching transport [ V4 127.0.0.1:0 TLS target domain=unspecified mFlowKey=0 ]

And here's a sip message that includes the part specifying h264 (on the sender side).

2018-06-01 19:05:57.838369-0700 [App Name][668:180076] DEBUG:TwilioVideo:[Core]:

Sending outgoing SIP message

INVITE sip:mobile-endpoint.twilio.com;transport=tls SIP/2.0

Via: SIP/2.0/TLS 127.0.0.1;branch=z9hG4bK-524287-1---3b2b4e736e439cc3;rport

Max-Forwards: 70

Contact: <sip:A14e4c307332BA2c46Edafd734Ed1bC5@127.0.0.1;transport=tls;ob>

To: <sip:orchestrator@mobile-endpoint.twilio.com>

From: "A14e4c307332BA2c46Edafd734Ed1bC5"<sip:A14e4c307332BA2c46Edafd734Ed1bC5@mobile-endpoint.twilio.com>;tag=35ddb147

Call-ID: 1GMJfOExX6Lea0LX0gwyIQ..

CSeq: 1 INVITE

Session-Expires: 120

Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, UPDATE, NOTIFY

Content-Type: application/room-signaling+json

Supported: timer, outbound, path, gruu, room-signaling

User-Agent: TwilioVideo SDK

X-Twilio-AccessToken: 

...

Content-Length: 4422

{"format":"planb","name":"-LDyEb_gkMmqNxL7XPMO","participant":{"revision":1,"tracks":[{"enabled":true,"id":"9c11cAbDb3BdEB2EC72e94c4AAcB3C3F","kind":"data","name":"9c11cAbDb3BdEB2EC72e94c4AAcB3C3F","priority":"medium"},{"enabled":true,"id":"c1A5e7812f1216DF1C18aa1eEc6E07Bf","kind":"audio","name":"Microphone","priority":"medium"},{"enabled":true,"id":"f4136Ea5ddB3892cCeAf3f5CA77bA3F0","kind":"video","name":"AR capture","priority":"medium"}]},"peer_connections":[{"description":{"revision":1,"sdp":"v=0\r\no=- 990484484844868028 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE audio video data\r\na=msid-semantic: WMS BBB93aaF7e545CDD8ECb7b66edEAC6B8\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:DgCc\r\na=ice-pwd:qGDchqlSSFh4aXN8Qdw4PFjN\r\na=fingerprint:sha-256 B8:D1:E5:49:A5:13:F3:56:8D:5F:74:39:83:D6:D6:70:60:9D:A2:78:6A:E0:B9:88:9B:73:DA:2E:4E:42:C4:8A\r\na=setup:actpass\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:112 telephone-event/32000\r\na=rtpmap:113 telephone-event/16000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:1582303180 cname:J1nAbU1sZYh8f3RT\r\na=ssrc:1582303180 msid:BBB93aaF7e545CDD8ECb7b66edEAC6B8 c1A5e7812f1216DF1C18aa1eEc6E07Bf\r\na=ssrc:1582303180 mslabel:BBB93aaF7e545CDD8ECb7b66edEAC6B8\r\na=ssrc:1582303180 label:c1A5e7812f1216DF1C18aa1eEc6E07Bf\r\nm=video 9 UDP/TLS/RTP/SAVPF 127 96 98 100 102 97 99 101 125\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:DgCc\r\na=ice-pwd:qGDchqlSSFh4aXN8Qdw4PFjN\r\na=fingerprint:sha-256 B8:D1:E5:49:A5:13:F3:56:8D:5F:74:39:83:D6:D6:70:60:9D:A2:78:6A:E0:B9:88:9B:73:DA:2E:4E:42:C4:8A\r\na=setup:actpass\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:4 urn:3gpp:video-orientation\r\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtpmap:100 red/90000\r\na=rtpmap:102 ulpfec/90000\r\na=rtpmap:127 H264/90000\r\na=rtcp-fb:127 ccm fir\r\na=rtcp-fb:127 nack\r\na=rtcp-fb:127 nack pli\r\na=rtcp-fb:127 goog-remb\r\na=rtcp-fb:127 transport-cc\r\na=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\na=rtpmap:125 rtx/90000\r\na=fmtp:125 apt=127\r\na=ssrc-group:FID 2297101943 1900990091\r\na=ssrc:2297101943 cname:J1nAbU1sZYh8f3RT\r\na=ssrc:2297101943 msid:BBB93aaF7e545CDD8ECb7b66edEAC6B8 f4136Ea5ddB3892cCeAf3f5CA77bA3F0\r\na=ssrc:2297101943 mslabel:BBB93aaF7e545CDD8ECb7b66edEAC6B8\r\na=ssrc:2297101943 label:f4136Ea5ddB3892cCeAf3f5CA77bA3F0\r\na=ssrc:1900990091 cname:J1nAbU1sZYh8f3RT\r\na=ssrc:1900990091 msid:BBB93aaF7e545CDD8ECb7b66edEAC6B8 f4136Ea5ddB3892cCeAf3f5CA77bA3F0\r\na=ssrc:1900990091 mslabel:BBB93aaF7e545CDD8ECb7b66edEAC6B8\r\na=ssrc:1900990091 label:f4136Ea5ddB3892cCeAf3f5CA77bA3F0\r\nm=application 9 DTLS/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:DgCc\r\na=ice-pwd:qGDchqlSSFh4aXN8Qdw4PFjN\r\na=fingerprint:sha-256 B8:D1:E5:49:A5:13:F3:56:8D:5F:74:39:83:D6:D6:70:60:9D:A2:78:6A:E0:B9:88:9B:73:DA:2E:4E:42:C4:8A\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n","type":"offer"},"id":"fBaC5bA3CA2AfdccF357efe4C4499Dab"}],"publisher":{"hw_device_arch":"arm64","hw_device_manufacturer":"Apple","hw_device_model":"iPhone10,6","name":"twilio-video-ios","platform_name":"iOS","platform_version":"11.3.1","sdk_version":"2.1.0"},"type":"connect","version":1}

2018-06-01 19:05:57.842200-0700 [App Name][668:180076] WARN:TwilioVideo:[Signaling]:RESIP::TRANSPORT: Can't find matching transport [ V4 127.0.0.1:0 TLS target domain=unspecified mFlowKey=0 ]

2018-06-01 19:05:58.069645-0700 [App Name][668:180076] DEBUG:TwilioVideo:[Core]:

Receiving incoming SIP message from infra

SIP/2.0 100 Giving a try

Via: SIP/2.0/TLS 127.0.0.1;received=107.193.141.3;branch=z9hG4bK-524287-1---3b2b4e736e439cc3;rport=49894

To: <sip:orchestrator@mobile-endpoint.twilio.com>

From: "A14e4c307332BA2c46Edafd734Ed1bC5"<sip:A14e4c307332BA2c46Edafd734Ed1bC5@mobile-endpoint.twilio.com>;tag=35ddb147

Call-ID: 1GMJfOExX6Lea0LX0gwyIQ..

CSeq: 1 INVITE

Server: Twilio Gateway

Content-Length: 0

Versions

Video iOS SDK

TwilioVideo 2.1.0 vis CocoaPods

Xcode

9.4

iOS Version

Sender: 11.3.1 Receiver: 11.4

iOS Device

Sender: iPhone X Receiver: iPhone 6s

piyushtank commented 6 years ago

@sdgandhi Thanks for reaching out.
Our Quickstart sample app demonstrates the use of H.264 codecs. You can choose the video codec by "Settings > Video Codec > H.264". Are you able to reproduce the problem on Quickstart?

ceaglest commented 6 years ago

Hey @sdgandhi,

When screen capturing AR from SceneKit using technique from sample app.

Is there any chance you are you trying to capture in a resolution > 1280x720? This won't work, and is a current limitation of our iOS Client and Group Rooms.

https://www.twilio.com/docs/video/managing-codecs#muti-codecs-limitations-and-known-issues

Best, Chris

sdgandhi commented 6 years ago

@piyushtank I cannot reproduce the problem with the Quickstart with the same phones.

@ceaglest Constraining the capture resolution to 1280x720 does not fix the problem either. Still getting black frames on receiver side.

sdgandhi commented 6 years ago

In addition, the camera preview in my app works fine with H264. It's only the AR capture that sends black frames for H264.

Therefore, I have to surmise that there's an issue with the video data pipeline from AR capture -> H264. Do you guys have a working sample of this?

piyushtank commented 6 years ago

@sdgandhi Thanks for posting more information. I am assuming you are having a custom capturer but It is hard to say where the problem is without looking a the code.

Is it possible for you to share code to help reproduce the problem? or if you can change sample app to reproduce the problem?

We have ARKitExample which demonstrates custom capturer APIs and ARKit use with our video sdk. Please note that ARKitExample has a listed known issue of performance because it snapshots the ARSCNView. We are going to improve the performance of the example app in future.

sdgandhi commented 6 years ago

@piyushtank Yes, modifying the regular video quickstart by using the custom capturer from the ARKitExample will repro the issue.

piyushtank commented 6 years ago

@sdgandhi In order to debug the problem, can you share the code of the modified quickstart which reproduces the problem?

cspecter commented 5 years ago

Has there been any update on this. We are seeing this exact issue with our app. We are using ARKit and running video to a custom captureConsumer. If we set the codec to h264 we get no output (just black frames) from the sender. We can only initialize calls using VP8 on iOS, but this causes some conditions where one participant will sometime fail to connect to a room or will connect then immediately disconnect. We would prefer to use a hardware accelerated codec. This is how we are implementing our capture consumer and initializing the ARKit stream.

func startCapture(_ format: TVIVideoFormat, consumer: TVIVideoCaptureConsumer) {
        self.captureConsumer = consumer

        self.displayLink = CADisplayLink(target: self, selector: #selector(self.displayLinkDidFire))
        self.displayLink?.preferredFramesPerSecond = self.sceneView.preferredFramesPerSecond

        self.displayLink?.add(to: .main, forMode: .commonModes)
        captureConsumer?.captureDidStart(true)
    }

// Send SceneKit View to pixelBuffer
    @objc func displayLinkDidFire(timer: CADisplayLink) {
        let image = self.sceneView.snapshot()

        autoreleasepool {
            let pixelBuffer = ImageProcessor.pixelBuffer(forImage: image)
            let aFrame = TVIVideoFrame(timeInterval: timer.timestamp, buffer: pixelBuffer!, orientation: TVIVideoOrientation.up)
            self.captureConsumer?.consumeCapturedFrame(aFrame!)
        }
    }

// Processing pixel buffer
struct ImageProcessor {
    static func pixelBuffer (forImage image:CGImage) -> CVPixelBuffer? {

        //        let frameSize = CGSize(width: image.width, height: image.height)
        let frameSize = CGSize(width: 720, height: 1280)
        var pixelBuffer:CVPixelBuffer? = nil
        let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(frameSize.width), Int(frameSize.height), kCVPixelFormatType_32BGRA , nil, &pixelBuffer)
        if status != kCVReturnSuccess {
            return nil

        }

        CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags.init(rawValue: 0))
        let data = CVPixelBufferGetBaseAddress(pixelBuffer!)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
        let context = CGContext(data: data, width: Int(frameSize.width), height: Int(frameSize.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!), space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)

        //        context?.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
        context?.draw(image, in: CGRect(x: 0, y: 0, width: 720, height: 1280))

        CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))

        return pixelBuffer

    }

}

// How we build a room
let connectOptions = TVIConnectOptions.init(token: self.accessToken) { (builder) in

                    // Use the local media that we prepared earlier.
                    builder.audioTracks = self.localAudioTrack != nil ? [self.localAudioTrack!] : [TVILocalAudioTrack]()
                    builder.videoTracks = self.localVideoTrack != nil ? [self.localVideoTrack!] : [TVILocalVideoTrack]()

                    // Use the preferred audio codec
                    if let preferredAudioCodec = Settings.shared.audioCodec {
                        builder.preferredAudioCodecs = [preferredAudioCodec]
                    }

                    // Use the preferred video codec
                    if let preferredVideoCodec = Settings.shared.videoCodec {                       
                         builder.preferredVideoCodecs = [TVIH264Codec(), TVIVp8Codec(), TVIVp9Codec()]
                    }

                    // Use the preferred encoding parameters
                    if let encodingParameters = Settings.shared.getEncodingParameters() {
                        builder.encodingParameters = encodingParameters
                    }

                    // The name of the Room where the Client will attempt to connect to. Please note that if you pass an empty
                    // Room `name`, the Client will create one for you. You can get the name or sid from any connected Room.
                    builder.roomName = roomName

                    // The CallKit UUID to assoicate with this Room.
                    builder.uuid = uuid
                    print("Built room", uuid)
                }
piyushtank commented 5 years ago

@cspecter Thanks for reaching out. Apologize for the delay as some of our team members are on vacation at present.

I will try to reproduce the problem tomorrow in PST day time using our ARKitExample sample app. In the meantime, can you provide following information to debug the problem - 1

  1. The iPhone device
  2. iOS version
cspecter commented 5 years ago

Sure thing. We are testing on an iPhone X and an XS Max. 12.1.1. Also we are on the 2.5.6 version of Twilio. We have not switched to 2.6, mostly because of a lack of examples. It looks a bit different so maybe behavior will be different?

piyushtank commented 5 years ago

@cspecter Thanks for the info. can you share RoomSid when you run into the problem?

We have added an escalation ticket in our current sprint for this issue. We are going try reproduce this problem and get back soon. I will keep you posted.

ceaglest commented 5 years ago

Hi,

We tried to reproduce this issue locally, but were unable to. Please reopen if you can provide a Room SID that we can diagnose.

Thanks, Chris