pushExternalVideoFrame returns false although the setExternalVideoSource is set to true.
Normal AgoraCameraSourcePush method does works, but when I switch to my custom push, it fails.
What could be causing this?
Is there any way that I can see a log?
func updateClientRole(_ role: AgoraClientRole) {
print("updateClientRole,", role.title)
self.role = role
if role == .broadcaster {
makeConfig(mirror: false)
agoraKit.enableLocalVideo(true)
agoraKit.enableLocalAudio(true)
agoraKit.enableAudioVolumeIndication(250, smooth: 3, report_vad: true)
agoraKit.setAudioProfile(.speechStandard, scenario: .communication)
agoraKit.setExternalVideoSource(true, useTexture: true, pushMode: true)
// this works
localVideoView = CustomVideoSourcePreview(frame: view.frame)
videosView.appendBackgroundViewContainer(uid: myUID, itsTeacher: self.amTeacher).addSubview(localVideoView!)
customCamera = AgoraCameraSourcePush(delegate: self, videoView: localVideoView!)
self.customCamera?.startCapture(ofCamera: .front)
self.localVideoView?.bindFrameToSuperviewBounds()
// this does not work
let myVideoView = videosView.appendBackgroundViewContainer(uid: myUID, itsTeacher: self.amTeacher)
avatarView = MyAvatarView(to: myVideoView)
avatarView?.delegate = self
}
}
func myVideoCapture(_ capture: AgoraCameraSourcePush?, didOutputSampleBuffer pixelBuffer: CVPixelBuffer, rotation: Int, timeStamp: CMTime) {
let videoFrame = AgoraVideoFrame()
videoFrame.format = 12
videoFrame.textureBuf = pixelBuffer
videoFrame.time = timeStamp
videoFrame.rotation = Int32(rotation)
let success = agoraKit?.pushExternalVideoFrame(videoFrame)
print("myVideoCapture.success",success) -> this print "myVideoCapture.success Optional(false)"
}
I was converting UIImage directly to CVPixelBuffer, but converting UIImage -> CIImage -> CVPixelBuffer magically worked.
Related
I am getting some issues related to APNG file, APNG file animation working perfect if i put APNG files in resource bundle , But when i have download same APNG file from assets server and saving APNG file into resource directory and then load using MSSticker like this way. after loading it showing only first frame.if anyone wanna try to check APNG file please have a look to this.
let imagePath = Bundle.main.path(forResource: imgName, ofType: ".png")
let pathurl = URL(fileURLWithPath: imagePath!)
do {
try cell.stickerview.sticker = MSSticker(contentsOfFileURL: pathurl, localizedDescription: "anything that you want")
}
catch {
fatalError("Failed to create sticker: \(error)")
}
Here i am saving image & getting saved image url from resource directory:
static func saveImage(image: UIImage , name:String) -> Bool? {
guard let data = UIImagePNGRepresentation(image) else {
return false
}
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
return false
}
do {
try data.write(to: directory.appendingPathComponent(name)!)
return true
} catch {
print(error.localizedDescription)
return false
}
}
static func getSavedImageUrl(named: String) -> URL? {
if let dir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
return URL(fileURLWithPath: dir.absoluteString).appendingPathComponent(named)
}
return nil
}
I have written the extension in custom MSSticker class
extension MSStickerView {
func downloadedFrom(url: URL , name: String) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { () -> Void in
// self.sticker = image
_ = GameUtil.saveImage(image: image, name: name)
if let pathurl = GameUtil.getSavedImageUrl(named: name) {
do {
try self.sticker = MSSticker(contentsOfFileURL: pathurl, localizedDescription: "Raid")
}
catch {
fatalError("Failed to create sticker: \(error)")
}
}
self.startAnimating()
}
}.resume()
}
func downloadedFrom(link: String , name: String) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url ,name: name)
}
I think problem is this UIImagePNGRepresentation. Why convert Data to UIImage and then use UIImagePNGRepresentation.
Try saving data directly.
static func saveData(data: Data , name:String) -> Bool? {
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
return false
}
do {
try data.write(to: directory.appendingPathComponent(name)!)
return true
} catch {
print(error.localizedDescription)
return false
}
}
And ignore image just pass data.
_ = GameUtil.saveImage(data: data, name: name)
I am playing around with the google drive API and trying to build a simple app that uploads a picture to my google drive. The app is supposed to upload a picture once the user is signed in, however it gives an error of
"2017-09-14 00:55:20.342237-0400 driveTest[6705:1647551] An error
occurred: Error Domain=com.google.GTLRErrorObjectDomain Code=403
"Insufficient Permission"
UserInfo={GTLRStructuredError=GTLRErrorObject 0x1c4251d30:
{message:"Insufficient Permission" errors:[1] code:403},
NSLocalizedDescription=Insufficient Permission}"
I have tried to pass it the service which is of type GTLRDriveService to the initSetup() function of the userSetUp class, but to no avail. Could someone please point me to the right track as to why my permissions are not working even though I have logged on correctly, and the part where I am passing in the GTLRDriveService is in the code that runs after a sucessful login.
I instantiate a userSetUp object and I
let setUpUser = userSetUp()
setUpUser.initSetup(service)
I have userSetUp written in objective c as such and it is bridged correctly as I am able to instantiate it in my viewcontroller file which is written in swift.
UserSetUp:::::::
#import "userSetUp.h"
#import <GoogleSignIn/GoogleSignIn.h>
#import GoogleAPIClientForREST;
#implementation userSetUp
- (void) initSetup:(GTLRDriveService *) driveService {
printf("heloooooaiosuoiadoidauoalo");
//GTLRDriveService *driveService = [GTLRDriveService new];
//NSData *fileData = [[NSFileManager defaultManager] contentsAtPath:#"files/apple.jpg"];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"apple" ofType:#"jpg"];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
GTLRDrive_File *metadata = [GTLRDrive_File object];
metadata.name = #"apple.jpg";
//metadata.mimeType = #"application/vnd.google-apps.document";
GTLRUploadParameters *uploadParameters = [GTLRUploadParameters uploadParametersWithData:fileData
MIMEType:#"image/jpeg"];
uploadParameters.shouldUploadWithSingleRequest = TRUE;
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:metadata
uploadParameters:uploadParameters];
query.fields = #"id";
[driveService executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRDrive_File *file,
NSError *error) {
if (error == nil) {
//NSLog(#"File ID %#", file.identifier);
printf("it worked");
} else {
NSLog(#"An error occurred: %#", error);
}
}];
printf("upload complete!");
}
#end
And Viewcontroller. swift
import GoogleAPIClientForREST
import GoogleSignIn
import UIKit
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate {
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLRAuthScopeDriveReadonly]
let service = GTLRDriveService()
let signInButton = GIDSignInButton()
let output = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
// Configure Google Sign-in.
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().scopes = scopes
GIDSignIn.sharedInstance().signInSilently()
signInButton.frame = CGRect(x: view.frame.width/2 - signInButton.frame.width , y: view.frame.height/2, width: signInButton.frame.width, height: signInButton.frame.height)
// Add the sign-in button.
view.addSubview(signInButton)
// Add a UITextView to display output.
output.frame = view.bounds
output.isEditable = false
output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
output.autoresizingMask = [.flexibleHeight, .flexibleWidth]
output.isHidden = true
view.addSubview(output);
//let itsASetup()
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
if let error = error {
showAlert(title: "Authentication Error", message: error.localizedDescription)
self.service.authorizer = nil
} else {
self.signInButton.isHidden = true
self.output.isHidden = false
self.service.authorizer = user.authentication.fetcherAuthorizer()
listFiles()
}
}
// List up to 10 files in Drive
func listFiles() {
let query = GTLRDriveQuery_FilesList.query()
query.pageSize = 10
service.executeQuery(query,
delegate: self,
didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:))
)
}
// Process the response and display output
#objc func displayResultWithTicket(ticket: GTLRServiceTicket,
finishedWithObject result : GTLRDrive_FileList,
error : NSError?) {
if let error = error {
showAlert(title: "Error", message: error.localizedDescription)
return
}
var text = "";
if let files = result.files, !files.isEmpty {
text += "Files:\n"
for file in files {
text += "\(file.name!) (\(file.identifier!))\n"
}
} else {
text += "No files found."
}
output.text = text
let setUpUser = userSetUp()
setUpUser.initSetup(service)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil
)
alert.addAction(ok)
present(alert, animated: true, completion: nil)
}
}
try to Change your scope like:
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate
{
// If modifying these scopes, delete your previously saved credentials by
private let scopes = ["https://www.googleapis.com/auth/drive"]
...
}
I am making an app in which I have to send the locations to the server on an Api call . It can work in the background also . I have implemented the code for this , but when I came back from background . My current work is not resuming . It is going just to the dashboard page , not backing to the page which is in the background .
Here is my code in the appdelegate
func applicationWillEnterForeground(_ application: UIApplication) {
if !isComingFromTrip {
locationManager.stopUpdatingLocation()
}
locationStarted = false
}
func applicationDidEnterBackground(_ application: UIApplication) {
if isComingFromTrip {
if UIApplication.shared.applicationState == .background {
print("start backgroun tracking from appdelegate")
locationManager.startUpdatingLocation()
}
//change locationManager status after time
self.runBackgroundTask(20)
} else {
isComingFromTrip = false
}
FIRMessaging.messaging().disconnect()
print("Disconnected from FCM.")
}
func runBackgroundTask(_ time: Int) -> Void {
if UIApplication.shared.applicationState == .background {
//create UIBackgroundTaskIdentifier and create tackground task, which starts after time
backgroundUpdateTask = app.beginBackgroundTask(expirationHandler: {() -> Void in
self.app.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
})
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
var t = Timer.scheduledTimer(timeInterval: TimeInterval(time), target: self, selector: #selector(self.startTrackingBg), userInfo: nil, repeats: false)
RunLoop.current.add(t, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
})
}
}
func startTrackingBg() {
//write background time remaining
print(String(format: "backgroundTimeRemaining: %.0f", UIApplication.shared.backgroundTimeRemaining))
//set default time
var time: Int = 60
//if locationManager is ON
if locationStarted == true {
//stop update location
locationManager.stopUpdatingLocation()
locationStarted = false
}
else {
//start updating location
locationManager.startUpdatingLocation()
locationStarted = true
//Time how long the application will update your location
time = 5
}
self.runBackgroundTask(time)
}
func StartupdateLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startUpdatingLocation()
}
I have enabled the background mode also Target->Capabilities-> Location
and also added key for this .
I am implementing UserNotification in my app. When the notification gets fired it shows two action, in one i want to add snooze effect, it must snooze after 5 mins again. How to handle it ? thanks for all ! help if any one do have idea
Well to snooze notification you can create another notification with same details of current notification and increase the fire date by 5 mins.
Here is the code I used :
func snoozeScheduledNotification(notification:UILocalNotification) -> Void {
// Snooze for 10 mins
let localNotification = UILocalNotification()
localNotification.fireDate = notification.fireDate?.addingTimeInterval(60*10)
localNotification.repeatInterval = NSCalendar.Unit(rawValue: 0) // 0 = No Repeat
localNotification.alertBody = notification.alertBody
localNotification.soundName = notification.soundName
localNotification.userInfo = notification.userInfo
localNotification.category = notification.category
UIApplication.shared.scheduleLocalNotification(localNotification)
}
Hope it helps you.
The shortest and simplest code I found about it
For Swift 3/4
extension UNNotification {
func snoozeNotification(for hours: Int, minutes: Int, seconds: Int) {
let content = UNMutableNotificationContent()
content.title = "Another Alert"
content.body = "Your message"
content.sound = .default()
let identifier = self.request.identifier
guard let oldTrigger = self.request.trigger as? UNCalendarNotificationTrigger else {
debugPrint("Cannot reschedule notification without calendar trigger.")
return
}
var components = oldTrigger.dateComponents
components.hour = (components.hour ?? 0) + hours
components.minute = (components.minute ?? 0) + minutes
components.second = (components.second ?? 0) + seconds
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
debugPrint("Rescheduling failed", error.localizedDescription)
} else {
debugPrint("rescheduled success")
}
}
}
}
You just need to call it this way :
response.notification.snoozeNotification(for: 0, minutes: 0, seconds: 30)
Credit goes to Simon Ljungberg : https://gist.github.com/simme/96264d5ceee394083d18e2c64f42a3a9
For iOS10, use this code.
Use this code in AppDelegate.swift file.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
let category = UNNotificationCategory(identifier: "identifier", actions: [], intentIdentifiers: [])
center.setNotificationCategories([category])
center.requestAuthorization(options: [.badge, .alert , .sound]) { (greanted, error) in
print(error)
}
return true
}
You can put this code in any view controller.
let content = UNMutableNotificationContent.init()
content.title = "Notification Title"
content.subtitle = "Notification Sub-Title"
content.body = "Notification Body"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "identifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
UNUserNotificationCenter.current().delegate = self
if (error != nil){
//handle here
}
}
You can handle notification using following method:
extension UIViewController: UNUserNotificationCenterDelegate {
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Swift.Void) {
completionHandler( [.alert, .badge, .sound])
}
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Swift.Void) {
print("Tapped in notification")
}
}
You can use this Blog as reference and Example.
Using the templates and TVML, I start my app with my own loading page, and then call a service to create the main page for the user.
If I initiate the call to the server inside didFinishLaunchingWithOptions, I get the error ITML <Error>: undefined is not an object - undefined - line:undefined:undefined.
From this I assume my asynchronous call to the server is finishing before the javascript App.onLaunch function has completed, and I can only get it to work if I force a wait time before the server is called.
Here is the AppDelegate method:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let appControllerContext = TVApplicationControllerContext()
// our "base" is local
if let jsBootUrl = NSBundle.mainBundle().URLForResource("application", withExtension: "js") {
appControllerContext.javaScriptApplicationURL = jsBootUrl
}
let jsBasePathURL = appControllerContext.javaScriptApplicationURL.URLByDeletingLastPathComponent
baseUrl = jsBasePathURL?.absoluteString
appControllerContext.launchOptions["BASEURL"] = jsBasePathURL?.absoluteString
appController = TVApplicationController(context: appControllerContext, window: window, delegate: self)
// initiate conversation with the server
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
return true
}
Here is the (somewhat boilerplate) javascript function:
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
});
} else {
/* handle error case here */
}
});
}
Now, if I change the call to the server in the didFinishLaunchingWithOptions, and force it to wait, like this:
...
// race condition hack:
_ = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "testing", userInfo: nil, repeats: false)
return true
}
// initiate conversation with the server
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
.. it will work. But I don't like that solution! What can I do to stop this race condition from happening?
You need a way for Javascript to communicate with Swift so that you know when App.onLaunch has finished running it's scripts.
Run this code in your didFinishLaunchingWithOptions method. It will allow you to call onLaunchDidFinishLoading() in Javascript and handle the callback in Swift.
appController.evaluateInJavaScriptContext({(evaluation: JSContext) -> Void in
let onLaunchDidFinishLoading : #convention(block) () -> Void = {
() -> Void in
//when onLaunchDidFinishLoading() is called in Javascript, the code written here will run.
self.testing()
}
evaluation.setObject(unsafeBitCast(onLaunchDidFinishLoading, AnyObject.self), forKeyedSubscript: "onLaunchDidFinishLoading")
}, completion: {(Bool) -> Void in
})
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
Inside App.onLaunch just add onLaunchDidFinishLoading() when the template is done being loaded.
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
//ADD THE FOLLOWING LINE
onLaunchDidFinishLoading();
});
} else {
/* handle error case here */
}
});
}