RTCVideoTrack shows stretched WebRTC - ios

I am using core WebRTC framework and rendering my local stream in IPhone full screen mode. Unfortunately, my video shows stretched, doesn't show like video view in camera app.
I tried to add aspect ratio in RTCMediaConstraints and also used adaptOutputFormatToWidth method to fix the output.
NSDictionary* mandatoryConstraints;
/* want to calculate aspect ratio dynamically */
NSString *aspectRatio = [NSString stringWithFormat:#"%f",(double)4/3];
if (aspectRatio) {
mandatoryConstraints = #{ kRTCMediaConstraintsMaxAspectRatio:
aspectRatio};
}
RTCMediaConstraints *cameraConstraints = [RTCMediaConstraints alloc];
cameraConstraints = [cameraConstraints initWithMandatoryConstraints:mandatoryConstraints optionalConstraints:nil];
RTCAVFoundationVideoSource *localVideoSource = [peerFactory avFoundationVideoSourceWithConstraints:mediaConstraint];
[localVideoSource adaptOutputFormatToWidth:devicewidth:devicewidth fps:30];
In below link, the difference between camera video view and my app call video view is shown
https://drive.google.com/file/d/1HN3KQcJphtC3VzJjlI4Hm-D3u2E6qmdQ/view?usp=sharing

I believe you are rendering your video in RTCEAGLVideoView, which require adjustment for size, you can use RTCMTLVideoView in place of RTCEAGLVideoView.
and if you want to use RTCEAGLVideoView, use RTCEAGLVideoViewDelegate method.
- (void)videoView:(RTCEAGLVideoView *)videoView didChangeVideoSize:(CGSize)size;
this method will give you correct size of the video.

(For Swift) -> Use RTCMTLVideoView and set videoContentMode
#if arch(arm64)
let renderer = RTCMTLVideoView(frame: videoView.frame)
renderer.videoContentMode = .scaleAspectFill
#else
let renderer = RTCEAGLVideoView(frame: videoView.frame)
#endif

Related

How to display image overlay on video in external playback mode of AVPlayer via AirPlay?

How to add an overlay UIImage in PNG format (such as a logo) with alpha channel on top of video playback, especially in case of external playback mode of AVPlayer when video is casted on Apple TV via AirPlay?
I would like add an overlay UIImage in PNG format with alpha channel (such as a logo) on top of video playback. This can be easily done on the phone by using contentOverlayView of AVPlayerViewController. However, when the video is casted and played on Apple TV via AirPlay, the contentOverlayView does not display.
I also tried to apply customised UIImageView on external screen when UIScreen has detected the external screen. However, the image still does not show. Instead, only the video playback is shown on the external screen. Here you can see my code for this approach:
if UIScreen.screens.count > 1 {
let externalScreen = UIScreen.screens[1]
print("Playing: externalScreen.bounds: \(externalScreen.bounds)")
let secondWindow = UIWindow(frame: externalScreen.bounds)
secondWindow.screen = externalScreen
let overlayImage = UIImage(named: "rain.png")
let overlayImageView = UIImageView(frame: externalScreen.bounds)
overlayImageView.image = overlayImage
secondWindow.addSubview(overlayImageView)
secondWindow.isHidden = false
secondWindow.makeKeyAndVisible()
}
It does not sound like a difficult problem as adding overlay views like subtitle or image logo seems to be very common operations. Can somebody help?
Thanks.

Is Apple using black magic to accomplish camera's preview orientation?

I have this AVFoundation camera app of mine. The camera preview is the result of a filter, applied by didOutputSampleBuffer method.
When I setup the camera I am following what apple did on one of their sample codes (CIFunHouse):
// setting up the camera
CGRect bounds = [self.containerOpenGL bounds];
_eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
_videoPreviewView = [[GLKView alloc] initWithFrame:bounds
context:_eaglContext];
[self.containerOpenGL addSubview:_videoPreviewView];
[self.containerOpenGL sendSubviewToBack:_videoPreviewView];
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
NSDictionary *options = #{kCIContextUseSoftwareRenderer : #(NO),
kCIContextPriorityRequestLow : #(YES),
kCIContextWorkingColorSpace : [NSNull null]};
_ciContext = [CIContext contextWithEAGLContext:_eaglContext options:options];
[_videoPreviewView bindDrawable];
_videoPreviewViewBounds = CGRectZero;
_videoPreviewViewBounds.size.width = _videoPreviewView.drawableWidth;
_videoPreviewViewBounds.size.height = _videoPreviewView.drawableHeight;
dispatch_async(dispatch_get_main_queue(), ^(void) {
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
_videoPreviewView.transform = transform;
_videoPreviewView.frame = bounds;
});
self.containerOpenGL is a full screen view and is constrained to the four corners of the screen. Autorotation is on.
But this is the problem...
When I setup the GLKView and self.ciContext it is created assuming the device is on a particular orientation. If the device is on a particular orientation and I run the application, the previewView will fit the entire self.containerOpenGL area but when I rotate the device the previewView will be out center.
I see that Apple code works perfectly and they don't use any constraints. They do not use any autorotation method, no didLayoutSubviews and when you rotate the device, running their code, everything rotates except the preview view. And worse than that, my previewView appears to rotate but not their's.
Is this black magic? How do I they do that?
They add their preview view to a uiwindow and that is why it does not rotate. I hope this answers the question. If not I will continue to look through their source code.
Quote from source code.
we make our video preview view a subview of the window, and send it to the back; this makes FHViewController's view (and its UI elements) on top of the video preview, and also makes video preview unaffected by device rotation
They also add this
_videoPreviewView.enableSetNeedsDisplay = NO;
This may keep it from responding as well
Edit: It appears that now the preview rotates and the UI does as well so to combat this you can add a second window and send it to the back and make the main window clear and add the previewView to the second window with a dummyViewController that tells it not to autorotate by overriding the appropriate method. This will allow the preview to not rotate but the UI to rotate.

iOS YTPlayerView force video quality

I am currently using iOS-youtube-player-helper library in our application. There is a view controller, with a YTPlayerView that has an aspect ratio of 16:9, which means it takes only a part of the screen. The video is loaded in medium and no matter how, I could not get it to play in 720P or 1080P. I am certain that these qualities are available, it's just the YTPlayerView forcing the quality based on the video player height. Because this is a library and not direct iframe embed, I cannot use "vq" parameter(specifying vq in playerVars does not seem to work), and setting the quality to be small then change it later does not work either(refer to this issue on GitHub)
Now, given the factor that I cannot make the YTPlayerView to fill up the whole screen, because of UI designing issues. So, is it possible to force the YTPlayerView to play in at least 720P? (Workarounds, changing the library code, ...)
Because this is an app that will be on App Store(and of course we don't want to have any legal disputes with google either), please don't suggest using libraries that are against the Youtube ToC such as XCDYouTubeKit
Many Thanks
I've found a workaround and this works well for me.
First of all, the problems depends by the webView size constructed inside the YTPlayerView. For example if you have a 320x200 playerView, try to forcing your video to 720hd don't work because the iFrame youtube player class re-switch to a better resolution according to your player size (in this case small quality because you have 320x200).
You can see this SO answer that explain this issue.
When you have imported the YTPlayerView class to your project you have two files: YTPlayerView.h and YTPlayerView.m
YTPlayerView.m (Update to work also on iPads)
I've change the function where the webview is initialized with a custom size (4k resolution) and to the last part I've added the possibility to scale the contents and restore the original frame, like this:
- (UIWebView *)createNewWebView {
CGRect frame = CGRectMake(0.0, 0.0, 4096.0, 2160.0); //4k resolution
UIWebView *webView = [[UIWebView alloc] initWithFrame:frame];
//UIWebView *webView = [[UIWebView alloc] initWithFrame:self.bounds];
webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
webView.scrollView.scrollEnabled = NO;
webView.scrollView.bounces = NO;
if ([self.delegate respondsToSelector:#selector(playerViewPreferredWebViewBackgroundColor:)]) {
webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self];
if (webView.backgroundColor == [UIColor clearColor]) {
webView.opaque = NO;
}
}
webView.scalesPageToFit = YES;
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.bounds.size;
float scale = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = scale;
webView.scrollView.maximumZoomScale = scale;
webView.scrollView.zoomScale = scale;
// center webView after scaling..
[webView setFrame:CGRectMake(0.0, self.frame.origin.y/3, 4096.0, 2160.0)];
} else {
webView.frame = self.bounds;
}
[webView reload];
return webView;
}
Hope this helps who try to use the Youtube original player in his project.
P.S.: All my tries were did with a swift project and the following vars:
var playerVars:Dictionary =
["playsinline":"1",
"autoplay":"1",
"modestbranding":"1",
"rel":"0",
"controls":"0",
"fs":"0",
"origin":"https://www.example.com",
"enablejsapi":"1",
"iv_load_policy":"3",
"showinfo":"0"]
Using the functions:
self.playerView.load(withVideoId: "Rk6_hdRtJOE", playerVars: self.playerVars)
and the follow method to force the resolution:
self.playerView.loadVideo(byId: "Rk6_hdRtJOE", startSeconds: 0.0, suggestedQuality: YTPlaybackQuality.HD720)
About iPads:
As reported by Edward in comments there was a little problem on iPads, that's because these devices seems don't apply scalesPageToFit. The goal is to check if the device is an iPad, then to scale (zooming out) the scrollView to host the little view bounds.
I've tested on my iPad air and it works. Let me know.

Video-capture vertical output iOS

I am trying to capture video with PBJVision.
I set up camera like this
vision.cameraMode = PBJCameraModeVideo;
vision.cameraOrientation = PBJCameraOrientationPortrait;
vision.outputFormat = PBJOutputFormatWidescreen;
And this produces output 1280x720 where 1280 is width.
Setting orientation to Landscape ROTATES the stream.
I have been trying to record video with GPUImage, and there I can
videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720
cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
_movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:_movieURL size:CGSizeMake(720.0, 1280.0)];
So that i get vertical output.
I would like to achieve vertical output for PBJVision, because I experience problems with GPUImage writing video to disk. (I will make another question for that).
What method/property of AVFoundation is responsible for giving the vertical output instead of horizontal?
Sorry for the question, I have been googling 2 days - can't find the answer.
I was having the same issue. I changed the output format to vision.outputFormat = PBJOutputFormatPreset; and I'm getting that portrait/vertical output.

Which GPUImage fillmode for fullscreen on all iPhone size?

I would like my GPUImageView (portrait, front camera) to be fullscreen whatever the phone screen size (from iPhone 4 to Iphone 6+). Ideally following an "Aspect fill" mode.
Right now I have :
_vidCamera = [[GPUImageVideoCamera alloc]
initWithSessionPreset:AVCaptureSessionPreset640x480
cameraPosition:AVCaptureDevicePositionBack];
_vidCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
// View
CGRect f = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
_filteredVideoView = [[GPUImageView alloc] initWithFrame:f];
Would kGPUImageFillModeStretch be enough ? Do I need to change the SessionPreset as well ? If I change the Session preset to something bigger than say the iPhone 4 can handle, will it cause trouble ?
Sorry I can't test it myself because I don't own more than an iPhone 4.
There are 3 mode in the source code.
public var kGPUImageFillModeStretch: GPUImageFillModeType { get }
// Stretch to fill the full view, which may distort the image outside of its normal aspect ratio
public var kGPUImageFillModePreserveAspectRatio: GPUImageFillModeType { get }
// Maintains the aspect ratio of the source image, adding bars of the specified background color
public var kGPUImageFillModePreserveAspectRatioAndFill: GPUImageFillModeType { get }
// Maintains the aspect ratio of the source image, zooming in on its center to fill the view
In my opinion, It is better to use kGPUImageFillModePreserveAspectRatioAndFill. Using the stretch option causes weird outputs.
To set the fillMode to your GPUImageFilter:
filterView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill

Resources