Is there any function to extract all the frames of a video that is downloaded and picked from Documents Directory in iOS Swift? - avasset

I am trying to extract all the frames from a video. Code is working fine for the video in Bundle Path, but it fails when i give a link of the video or I try to pick the video from File Manager.
Following are the issues that i have encountered:
AVAsset throws an error "Error Domn=AVFoundationErrorDomain Code=-11838 "Cannot initialize an instance of AVAssetReader with an asset at non-local URL".
When video is picked from Documents Directory, the fetched asset tracks are always 0.
Following is the code that i am using right now.
let asset = AVAsset(url: URL(string: path!)!)
self.playerController.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
if let path = path {
let asset:AVAsset = AVAsset(url: URL(string: path)!)
self.playerController.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
self.player.play()
asset.loadTracks(withMediaType: .video) { fetchedTracks, err in
do{
let reader = try AVAssetReader(asset: asset)
// read video frames as BGRA
let trackReaderOutput = AVAssetReaderTrackOutput(track: (fetchedTracks?.first)!, outputSettings:[String(kCVPixelBufferPixelFormatTypeKey): NSNumber(value: kCVPixelFormatType_32BGRA)])
reader.add(trackReaderOutput)
reader.startReading()
while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() {
print("sample at time \(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))")
if CMSampleBufferGetImageBuffer(sampleBuffer) != nil {
// process each CVPixelBufferRef here
guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let ciimage = CIImage(cvPixelBuffer: imageBuffer)
self.frames.append(UIImage(ciImage: ciimage))
// see CVPixelBufferGetWidth, CVPixelBufferLockBaseAddress, CVPixelBufferGetBaseAddress, etc
}
}
self.collectionViewFrames.reloadData()
} catch let err {
print(err)
}
}
}

Related

flutter with firebase - detect the url string whether it is image(jpg) or video(avi)

I'm beginner with these Google's products and got 'serious' problem.
I uploaded photos and videos to firebase storage and urls of the photos and videos in the firebase storage is generated and stored automatically in firebase database. In my flutter lib, I could call those urls by my own code and display the url's image on the avd screen.
Image.network(url) is the code to display image url from firebase. But I also wanna display video url's asset simultaneously with single code. That is, videos and photos should be in single screen! In this case, Image.network(url) doesn't work anymore..
If I change that image.network code for video format according to video_player plug-in, I cannot display image asset anymore and if I stay same with that Image.network(url) code, I cannot display video url from firebase. So here is the question:
How can I detect whether the firebase's url string is image or video with my flutter code, and display that asset on the 'single screen' whatever the file format is(at least video and photo) with integrated flutter code?
url example
It's not much of a deal since type of the media is in the URL. You can parse it as a
Uri object then extract the type.
import 'dart:core';
enum UrlType { IMAGE, VIDEO, UNKNOWN }
void main() async {
var imageUrl =
'https://firebasestorage.googleapis.com/v0/b/myAppCodeNameForFirebase.appspot.com/o/Posts%20Pictures%2Fiufri095620200814.jpg?alt=media&token=89b6c22f-b8dd-4cff-9395-f53fc0808824';
var videoUrl =
'https://firebasestorage.googleapis.com/v0/b/myAppCodeNameForFirebase.appspot.com/o/Posts%20Pictures%2Fiufri095620200814.mp4?alt=media&token=89b6c22f-b8dd-4cff-9395-f53fc0808824';
print(getUrlType(imageUrl));
print(getUrlType(videoUrl));
}
UrlType getUrlType(String url) {
Uri uri = Uri.parse(url);
String typeString = uri.path.substring(uri.path.length - 3).toLowerCase();
if (typeString == "jpg") {
return UrlType.IMAGE;
}
if (typeString == "mp4") {
return UrlType.VIDEO;
} else {
return UrlType.UNKNOWN;
}
}
Let me give you an idea.
Consider implementing this scenario.
var url = 'domain.com/file.jpg?querySegment';
In your widget area,
child: url.contains('.mp4?') ? VideoWidget() : ImageWidget()
also, even with multiple conditions,
child: (url.contains('.jpg?') || url.contains('.png?')) ? ImageWidget() : VideoWidget()
May this suits your case.
Improving upon the accepted answer, here is what I have in my code. I needed a way to identify many types of videos and images from the passed url.
Using path package to idetify the extension
Have a list of video and image extension
import 'package:path/path.dart' as p;
enum UrlType { IMAGE, VIDEO, UNKNOWN }
class UrlTypeHelper {
static List<String> _image_types = [
'jpg',
'jpeg',
'jfif',
'pjpeg',
'pjp',
'png',
'svg',
'gif',
'apng',
'webp',
'avif'
];
static List<String> _video_types = [
"3g2",
"3gp",
"aaf",
"asf",
"avchd",
"avi",
"drc",
"flv",
"m2v",
"m3u8",
"m4p",
"m4v",
"mkv",
"mng",
"mov",
"mp2",
"mp4",
"mpe",
"mpeg",
"mpg",
"mpv",
"mxf",
"nsv",
"ogg",
"ogv",
"qt",
"rm",
"rmvb",
"roq",
"svi",
"vob",
"webm",
"wmv",
"yuv"
];
static UrlType getType(url) {
try {
Uri uri = Uri.parse(url);
String extension = p.extension(uri.path).toLowerCase();
if (extension.isEmpty) {
return UrlType.UNKNOWN;
}
extension = extension.split('.').last;
if (_image_types.contains(extension)) {
return UrlType.IMAGE;
} else if (_video_types.contains(extension)) {
return UrlType.VIDEO;
}
} catch (e) {
return UrlType.UNKNOWN;
}
return UrlType.UNKNOWN;
}
}
/// Usage
if(UrlTypeHelper.getType(message) == UrlType.IMAGE) {
/// handle image
}
else if(UrlTypeHelper.getType(message) == UrlType.VIDEO) {
/// handle video
}

How to access Asset Catalog (images) after downloaded through NSBundleResourceRequest?

I cannot access images inside an asset catalog after it's been downloaded through NSBundleResourceRequest (on demand resource).
My code, say the image set name is "snow_4" with on demand resource tag "tag1"
NSBundleResourceRequest* resourceRequest = [[NSBundleResourceRequest alloc] initWithTags:[NSSet setWithObjects:#"tag1", nil]];
[resourceRequest conditionallyBeginAccessingResourcesWithCompletionHandler:
^(BOOL resourcesAvailable){
// if resource is not available download it
if (!resourcesAvailable) {
[resourceRequest beginAccessingResourcesWithCompletionHandler:
^(NSError * __nullable error){
if(!error){
UIImage* image = [UIImage imageNamed:#"snow_4"]; // image is null
}
}];
}else{
UIImage* image = [UIImage imageNamed:#"snow_4"]; // image is null
}
}];
Below is my Disk Report, please note that [UIImage imageNamed:#"snow_3"] (marked as 'In Use') returns a correct object but not images that are marked as 'Downloaded'
Appreciating your time and help!
Thanks,
Mars
Make sure NSBundleResourceRequest is not released early. Try to let NSBundleResourceRequest variable globally,
and access the image with UIImage(named: "xxx", in: self.bundleReq.bundle, compatibleWith: nil)
do this
image = [UIImage imageNamaed:#"snow_4" inBundle:[resourceRequest bundle]];
let tags = NSSet(array: ["tag1","tag2"])
let resourceRequest = NSBundleResourceRequest(tags: tags as! Set<String>)
resourceRequest.conditionallyBeginAccessingResourcesWithCompletionHandler {(resourcesAvailable: Bool) -> Void in
if resourcesAvailable {
// Do something with the resources
} else {
resourceRequest.beginAccessingResourcesWithCompletionHandler {(err: NSError?) -> Void in
if let error = err {
print("Error: \(error)")
} else {
// Do something with the resources
}
}
}
}

How to use FMDB on the generic iOS device instead of simulator?

I've created an app using Swift3 and Xcode8, and using FMDB as my database, when it runs on the simulator it can get the data from data.db,but when it runs on the generic device (which is my phone), there's no data in the tableView, also could't insert records. I added data.db into my project, but when I changed records on simulator, records in data.db didn't change, but I printed the path, it pointed to simulator folder, database in that folder will changed along the modify in simulator and that folder's path changes almost every time. I'm so confused, is that because I didn't connected to my database in fact?
Here's the Utility.Swift which holds common and often reused function
import UIKit
class Utility: NSObject {
class func getPath(_ fileName: String) -> String {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
print(fileURL.path)
return fileURL.path
}
class func copyFile(_ fileName: NSString){
let dbPath: String = getPath(fileName as String)
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: dbPath) {
let documentsURL = Bundle.main.resourceURL
let fromPath = documentsURL!.appendingPathComponent(fileName as String)
var error : NSError?
do {
try fileManager.copyItem(atPath: fromPath.path, toPath: dbPath)
}
catch let error1 as NSError {
error = error1
}
let alert: UIAlertView = UIAlertView()
if (error != nil) {
alert.title = "Error Occured"
alert.message = error?.localizedDescription
}
else {
alert.title = "Successfully Copy"
alert.message = "Your database copy successfully"
}
alert.delegate = nil
alert.addButton(withTitle: "Ok")
alert.show()
}
}
class func invokeAlertMethod(_ strTitle: NSString, strBody: NSString, delegate: AnyObject?) {
let alert: UIAlertView = UIAlertView()
alert.message = strBody as String
alert.title = strTitle as String
alert.delegate = delegate
alert.addButton(withTitle: "Ok")
alert.show()
}
}
and StudentDataBase.swift contains Query languages
import UIKit
let sharedInstance = StudentDataBase()
class StudentDataBase : NSObject {
var database: FMDatabase? = nil
class func getInstance() -> StudentDataBase{
if((sharedInstance.database) == nil)
{
sharedInstance.database = FMDatabase(path: Utility.getPath("data.db"))
}
return sharedInstance
}
func addStuData(_ student: Student) -> Bool{
sharedInstance.database!.open()
let isInserted = sharedInstance.database!.executeUpdate("INSERT INTO [Student info] (StudentID, FirstName, LastName, PhoneNumber) VALUES (?, ?, ?, ?)", withArgumentsIn: [student.studentID, student.fstName, student.lstName, student.phoneNum])
sharedInstance.database!.close()
return isInserted
}
func updateStuData(_ student: Student) -> Bool {
sharedInstance.database!.open()
let isUpdated = sharedInstance.database!.executeUpdate("UPDATE [Student info] SET FirstName=?, LastName=?, PhoneNumber=? WHERE StudentID=?", withArgumentsIn: [student.fstName, student.lstName, student.phoneNum, student.studentID])
print(student)
print(isUpdated)
sharedInstance.database!.close()
return isUpdated
}
func deleteStuData(_ student: Student) -> Bool {
sharedInstance.database!.open()
let isDeleted = sharedInstance.database!.executeUpdate("DELETE FROM [Student info] WHERE StudentID=?", withArgumentsIn: [student.studentID])
sharedInstance.database!.close()
return isDeleted
}
func getAllStuData() -> [Student] {
sharedInstance.database!.open()
let resultSet: FMResultSet! = sharedInstance.database!.executeQuery("SELECT * FROM [Student info]", withArgumentsIn: nil)
var marrStudentInfo : [Student] = []
if (resultSet != nil) {
while resultSet.next() {
let student : Student = Student()
student.studentID = resultSet.string(forColumn: "StudentID")
student.fstName = resultSet.string(forColumn: "FirstName")
student.lstName = resultSet.string(forColumn: "LastName")
student.phoneNum = resultSet.string(forColumn: "PhoneNumber")
marrStudentInfo.append(student)
}
}
sharedInstance.database!.close()
return marrStudentInfo
}
}
also in AppDelegate.swift, I've written:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Utility.copyFile("data.db")
return true
}
I'm new to Swift and FMDB, please explain and I'll try my best.Thank you very much!!!!
Edit:
After I downloaded the contents in my phone and the database is blank. And after I throwaway the error, it shows
Inserted failed:Optional("no such table: Student info")
SQLite offers good error reporting and you should avail yourself of it. So, for example, if any of these executeUpdate calls fail, you should examine the lastErrorMessage (before you close the database) so you know why it failed.
Personally, I'd suggest using executeQuery(_:values:) and executeUpdate(_:values:) rather than executeQuery(_:withArgumentsIn:) and executeUpdate(_:withArgumentsIn:) because the former methods throw errors (which shifts to a more active error handling paradigm from the more passive one in which you have to manually remember to check for errors). But regardless of how you do your error handling, do it, or else we're all guessing.
Personally, I'd suggest using Xcode's "Devices" window and download your app's bundle (see https://stackoverflow.com/a/38064225/1271826) and look at the database. I bet it's a blank database with no tables defined at all. In terms of why, it could be because you did some earlier testing and there's an old version of the database in the Documents folder. Try completely deleting the app from the device and re-running and see if that fixes it.
Other, more obscure sources of problem include filename capitalization (e.g. Data.db vs data.db). The macOS file system will often handle that gracefully because it's not (generally) case sensitive. But iOS devices are case sensitive.
But, like I said, we're flying blind until (a) you add some error handling to your code so you can see why its failing; and (b) look at the database on the device to see what it looks like.

NSXMLParser Failed to parse data on watch but working fine on watch simulator

NSXMLParser Failed to parse data on watch but working fine on watch simulator.How to resolve this issue?
Kindly help
Here is Code:
let url:String="http://www.someurl.com/data"
let urlToSend: NSURL = NSURL(string: url)!
// Parse the XML
parser = NSXMLParser(contentsOfURL: urlToSend)!
parser.delegate = self
let success:Bool = parser.parse()
if success {
print("parse success!")
print(strXMLData)
} else {
print("parse failure!")
}
I've experienced the same issue, and I couldn't debug the app when running on the watch. My solution was to parse the XML file in the iPhone App side and communicate the data to the watch through a WCSession.
I had exactly the same problem. I found an answer on one of Apple's own forums.
You need to read in the XML from URL into NSData, then invoke XML parser with NSData object, not the NSURL.
Here is some code
var nsXMLData = NSData()
var parser = NSXMLParser()
func xmlFileRequest(urlweb:NSURL){
print("in xmlFileRequest")
let requestURL: NSURL = urlweb
let urlRequest: NSMutableURLRequest =
NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
if error == nil {
print(">>>>>>xmlFileRequest success!")
self.nsXMLData = data!
self.beginParsing()
} else {
print(">>>>>>>xmlFileRequest fail")
}
}
task.resume()
}
func beginParsing()
{
parser = NSXMLParser.init(data:nsXMLData)
parser.delegate = self
let success:Bool = parser.parse()
if success {
print("***parse success***")
} else {
print("***parse failure!***")
let errorMsg:String =
(parser.parserError?.localizedDescription)!
print("Error = " + errorMsg)
}
}
I just tested this code on our Apple watch.

how can we store a html page into sqlite in blackberry on memory card / phone memory?

Below code specifies that we we can make http connection in blackberry and how to store html page as a string?
I am doing this but I am able to get that http request but when I get response i.e http_ok it is not correct so that I can save text oh html as a string and I can further store that into sqlite.
LabelField title = new LabelField("SQLite Create Database Sample",
LabelField.ELLIPSIS |
LabelField.USE_ALL_WIDTH);
setTitle(title);
add(new RichTextField("Creating a database."));
argURL="https://www.google.com:80";
try {
connDesc = connFact.getConnection(argURL);
if (connDesc != null) {
httpConn = (HttpConnection) connDesc.getConnection();
// //Send Data on this connection
// httpConn.setRequestMethod(HttpConnection.GET);
// //Server Response
StringBuffer strBuffer = new StringBuffer();
inStream = httpConn.openInputStream();
int chr;
int retResponseCode = httpConn.getResponseCode();
if (retResponseCode == HttpConnection.HTTP_OK) {
if (inStream != null) {
while ((chr = inStream.read()) != -1) {
strBuffer.append((char) chr);
}
serverResponceStr = strBuffer.toString();
// appLe.alertForms.get_userWaitAlertForm().append("\n"+serverResponceStr);
//returnCode = gprsConstants.retCodeSuccess;
}
} else {
//returnCode = gprsConstants.retCodeNOK;
}
}
} catch (Exception excp) {
//returnCode = gprsConstants.retCodeDisconn;
excp.printStackTrace();
} `enter code here`
The code does not perform any database functionality, however I tested and it does successfully perform an HttpRequest to an external URL. The data that comes back is based on the response of the server you are making the request to.
The code I used can be found here:
http://snipt.org/vrl7
The only modifications is to keep a running summary of various events, and the response is displayed in the RichTextField. Basically, this looks to be working as intended, and the resulting String should be able to be saved however you see fit; though you may need to be cautious of encoding when saving to a database so that special characters are not lost or misinterpreted.

Resources