I'm trying to autoplay a video in my app. The video needs to play without the controls.
I've set up the video and the settings, including MPMovieControlStyle.None but the video controls appear for about 2 seconds before disappearing. I have no idea why.
I've used this code (exact code) for another project and it works well, but here for some reason it will not.
override func viewDidLoad() {
super.viewDidLoad()
generateVideo()
}
func generateVideo () {
let movieURL = NSBundle.mainBundle().pathForResource("video", ofType: "mp4")
let videoFilePath = NSURL(fileURLWithPath: movieURL!)
self.view.addSubview(MoviePlayerViewController.moviePlayer.view)
self.view.sendSubviewToBack(MoviePlayerViewController.moviePlayer.view)
MoviePlayerViewController.moviePlayer.contentURL = videoFilePath
MoviePlayerViewController.moviePlayer.shouldAutoplay = true
MoviePlayerViewController.moviePlayer.prepareToPlay()
MoviePlayerViewController.moviePlayer.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
MoviePlayerViewController.moviePlayer.fullscreen = true
MoviePlayerViewController.moviePlayer.controlStyle = MPMovieControlStyle.None
MoviePlayerViewController.moviePlayer.play()
MoviePlayerViewController.moviePlayer.repeatMode = MPMovieRepeatMode.One
MoviePlayerViewController.moviePlayer.scalingMode = MPMovieScalingMode.AspectFit
}
Any idea why this is happening?
After checking how the view is loaded I'm sure the problem is there.
I used a different layout with one Storyboard calling another storyboard and that was the source of the problem.
Related
For the life of me I can't get the GIF to display using the SwiftyGif library. Is there something I'm missing here?
var outgoingMessageView: UIImageView!
outgoingMessageView = UIImageView(frame:
CGRect(x: llamaView.frame.maxX - 50,
y: llamaView.frame.minY + 75,
width: bubbleImageSize.width,
height: bubbleImageSize.height))
outgoingMessageView.delegate = self
if textIsValidURL == true {
print("URL is valid")
outgoingMessageView.image = bubbleImage
let maskView = UIImageView(image: bubbleImage)
maskView.frame = outgoingMessageView.bounds
outgoingMessageView.mask = maskView
outgoingMessageView.frame.origin.y = llamaView.frame.minY - 25
let url = URL(string: text)
outgoingMessageView.setGifFromURL(url, manager: .defaultManager, loopCount: -1, showLoader: true)
} else {
outgoingMessageView.image = bubbleImage
}
// Set the animations
label.animation = "zoomIn"
//outgoingMessageView.animation = "zoomIn"
// Add the Subviews
view.addSubview(outgoingMessageView)
print("outgoingMessageView added")
The delegate lets me know it runs successfully via:
gifDidStart
gifURLDidFinish
Checking outgoingMessageView.isAnimatingGif() tells me it's still running.
Checking outgoingMessageView.isDisplayedInScreen(outgoingMessageView) tells me it's not being displayed
It "finishes" almost immediately, but it's the same in the example project, yet the gif still loops and displays in the project. I've changed loop counts, imageviews, not running via a mask as I intended and instead just a UIImageView, changed the GIF urls, all to no avail. Is this problem related to my view structure?
I am calling this function based on actions in a collectionView.Image Example Here
Using the latest SwiftyGIF version.
I just made a sample about this with the following code:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
testSwiftyGif()
}
public func testSwiftyGif() {
let imgPath = "https://github.com/kirualex/SwiftyGif/blob/master/SwiftyGifExample/1.gif?raw=true"
let imgUrl = URL(string: imgPath)!
var outgoingMessageView: UIImageView!
outgoingMessageView = UIImageView(frame:
CGRect(x: llamaView.frame.maxX - 50,
y: llamaView.frame.minY + 75,
width: 200,
height: 200))
outgoingMessageView.setGifFromURL(imgUrl, manager: .defaultManager, loopCount: -1, showLoader: true)
self.view.addSubview(outgoingMessageView)
print("outgoingMessageView added")
}
And it adds the gif as intended:
Aparently the issue is your view structure. The image is being added to the view, but the view is not visible due mask, frame or superview position.
Try to check the view hierarchy using the xCode View Hierarchy Debugger
The project I am working on takes an h264-encoded bitstream and displays the video frames.
The decoder––which takes raw bytes and outputs a CMSampleBuffer containing a video frame––is working well. It's formatDescription contains the expected values, it correctly interprets NALU types, all that. I may be wrong though, as this is my first time working with all of this, so if anyone suspects that the problem may be in the decoder I would be happy to provide code.
My problem arises when I pass a CMSampleBuffer to an AVSampleBufferDisplayLayer. The video frames aren't appearing on screen.
This is the function that is supposed to display the video frames:
func videoFrameReceived(_ frame: CMSampleBuffer) {
videoLayer.enqueue(frame)
DispatchQueue.main.async { [weak self] in
self?.videoLayer.setNeedsDisplay()
}
}
This is how I initialized my AVSampleBufferDisplayLayer:
if let layer = videoLayer {
layer.frame = CGRect(x: 0, y: 150, width: 640, height: 480)
layer.videoGravity = .resizeAspect
let cmTimebasePointer = UnsafeMutablePointer<CMTimebase?>.allocate(capacity: 1)
let status = CMTimebaseCreateWithMasterClock(kCFAllocatorDefault, CMClockGetHostTimeClock(), cmTimebasePointer)
layer.controlTimebase = cmTimebasePointer.pointee
if let controlTimeBase = layer.controlTimebase, status == noErr {
CMTimebaseSetTime(controlTimeBase, kCMTimeZero)
CMTimebaseSetRate(controlTimeBase, 1.0)
}
self.layer.addSublayer(layer)
}
Thanks!
User figured out how to do it and posted a link to the solution as a comment to the question. The link was broken,
here is the updated link to solution
I am new to swift and I am trying to make a Mac OS app that loops a video from the app's resources using AVPlayer as the background of the window once the app has been launched. When the user selects a menu item/clicks a button the background video will instantly change to a different video from the app's resources and start looping that video as the window's background.
I was able to play the first video once the app launches following this tutorial: (https://youtu.be/QgeQc587w70) and I also successfully made the video loop itself seamlessly following this post: (Looping AVPlayer seamlessly).
The problem I am now facing is changing the video to the other one once a menu item was selected/a button was clicked. The approach I was going for is to change the url and create a new AVPlayer using the new URL and affect it to the playerView.player following this post: (Swift How to update url of video in AVPlayer when clicking on a button?). However every time the menu item is selected the app crashes with the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)". This is apparently caused by the value of playerView being nil. I don't really understand the reason for this as playerView is an AVPlayerView object that I created using the xib file and linked to the swift file by control-dragging and I couldn't seem to find another appropriate method of doing the thing I wanted to do. If you know the reason for this and the way of fixing it please provide me some help or if you know a better method of doing what I've mention above please tell me as well. Any help would be much appreciated!
My code is as follow, the line that crashes the app is at the bottom:
import Cocoa
import AppKit
import AVKit
import AVFoundation
struct videoVariables {
static var videoName = "Test_Video" //declaring the video name as a global variable
}
var videoIsPlaying = true
var theURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4") //creating the video url
var player = AVPlayer.init(url: theURL!)
class BackgroundWindow: NSWindowController {
#IBOutlet weak var playerView: AVPlayerView! // AVPlayerView Linked using control-drag from xib file
#IBOutlet var mainWindow: NSWindow!
#IBOutlet weak var TempBG: NSImageView!
override var windowNibName : String! {
return "BackgroundWindow"
}
//function used for resizing the temporary background image and the playerView to the window’s size
func resizeBG() {
var scrn: NSScreen = NSScreen.main()!
var rect: NSRect = scrn.frame
var height = rect.size.height
var width = rect.size.width
TempBG.setFrameSize(NSSize(width: Int(width), height: Int(height)))
TempBG.frame.origin = CGPoint(x: 0, y: 0)
playerView!.setFrameSize(NSSize(width: Int(width), height: Int(height)))
playerView!.frame.origin = CGPoint(x: 0, y: 0)
}
override func windowDidLoad() {
super.windowDidLoad()
self.window?.titleVisibility = NSWindowTitleVisibility.hidden //hide window’s title
self.window?.styleMask = NSBorderlessWindowMask //hide window’s border
self.window?.hasShadow = false //hide window’s shadow
self.window?.level = Int(CGWindowLevelForKey(CGWindowLevelKey.desktopWindow)) //set window’s layer as desktopWindow layer
self.window?.center()
self.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
if let screen = NSScreen.main() {
self.window?.setFrame(screen.visibleFrame, display: true, animate: false) //resizing the window to cover the whole screen
}
resizeBG() //resizing the temporary background image and the playerView to the window’s size
startVideo() //start playing and loop the first video as the window’s background
}
//function used for starting the video again once it has been played fully
func playerItemDidReachEnd(notification: NSNotification) {
playerView.player?.seek(to: kCMTimeZero)
playerView.player?.play()
}
//function used for starting and looping the video
func startVideo() {
//set the seeking time to be 2ms ahead to prevent a black screen every time the video loops
let playAhead = CMTimeMake(2, 100);
//loops the video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object:
playerView.player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.playerView.player?.seek(to: playAhead)
self.playerView.player?.play()
}
})
var playerLayer: AVPlayerLayer?
playerLayer = AVPlayerLayer(player: player)
playerView?.player = player
print(playerView?.player)
playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
player.play()
}
//changing the url to the new url and create a new AVPlayer then affect it to the playerView.player once the menu item is being selected
#IBAction func renderBG(_ sender: NSMenuItem) {
videoVariables.videoName = "Test_Video_2"
var theNewURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4")
player = AVPlayer.init(url: theNewURL!)
//!!this line crashes the app with the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)" every time the menu item is being selected!!
playerView.player = player
}
}
Additionally, the background video is not supposed to be interactive(E.g. User cannot pause/ fast-forward the video), so any issues that might be caused by user interactivity can be ignored. The purpose of the app is to play a video on the user's desktop creating the exact same effect of running the command:
"/System/Library/Frameworks/ScreenSaver.framework/Resources/
ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background" in terminal.
Any help would be much appreciated!
You don't need to create AVPlayer from url. There is AVPlayerItem class to manipulate player playback queue.
let firstAsset = AVURLAsset(url: firstVideoUrl)
let firstPlayerItem = AVPlayerItem(asset: firstAsset)
let player = AVPlayer(playerItem: firstPlayerItem)
let secondAsset = AVURLAsset(url: secondVideoUrl)
let secondPlayerItem = AVPlayerItem(asset: secondAsset)
player.replaceCurrentItem(with: secondPlayerItem)
Docs about AVPlayerItem
I know in iOS 10 you can pinch the video player to keep the video playing within the webview. Is there any way to set the webview so that the video player doesn't open in full screen on default?
webView.allowsInlineMediaPlayback = true
is what I believe you are looking for
The following is working for in WKWebView in swift 4.1
The main part of the WKWebView in WKwebviewConfiguration
wkwebView.navigationDelegate = self
wkwebView.allowsBackForwardNavigationGestures = true
self.wkwebView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
wkwebView = WKWebView(frame: wkwebView.frame, configuration: config)
self.view.addSubview(wkwebView)
self.wkwebView.load(NSURLRequest(url: URL(string: self.getUrl())!) as URLRequest)
I have this code but it doesn't show anything only a black frame.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var filepath: NSString = NSBundle.mainBundle().pathForResource("Akbar_ipad", ofType: "mp4")
var fileURL: NSURL = NSURL(string: filepath)
let moviePlayerController = MPMoviePlayerController(contentURL: fileURL)
moviePlayerController.shouldAutoplay = true
moviePlayerController.movieSourceType = MPMovieSourceType.File
moviePlayerController.view.frame = CGRect(x: 200, y: 200, width: 500, height: 300)
self.view.addSubview(moviePlayerController.view)
moviePlayerController.prepareToPlay()
moviePlayerController.play()
...
Have you got any idea how can i solve my problem? I am going crazy!
Thanks for your time.
I have a similar problem: How to load MPMoviePlayerController contentUrl asynchronous when loading view?
But here some ideas for you:
Maybe the url is constructed incorrectly/not local. Try using: let fileURL = NSBundle.mainBundle().URLForResource("Akbar_ipad", withExtension: "mp4")
You might need a global variable to hold a reference to the movie player instance (as described in the documentation.
For your problem, you maybe forgot add your resources as "Bundle Resources"
Follow below step to add:
In the Project Navigator select your project root
Select "Build Phases" tab
In "Bundle Resources" section to click "+" to add your resources
After that I think you can use both URLForResource and pathForResource