I'm working on code that looks at the user's videos and passes them along to AVPlayer by way of an AVPlayerItem which takes a URL.
To get all the videos on an iOS device, you can do:
let videoNumber = MPMediaType.anyVideo.rawValue
let predicate = MPMediaPropertyPredicate.init(value: videoNumber, forProperty: MPMediaItemPropertyMediaType)
let query = MPMediaQuery.init()
query.addFilterPredicate(predicate)
if let items = query.items
{
mediaCollection = MPMediaItemCollection(items: items)
// -1 would indicate an error condition
print("number of items in collection is \(mediaCollection?.count ?? -1)")
}
When you select the MPMediaItem you want to use from the items array, there should be an assetURL to go with it.
Trouble is, on my device, all of my assetURL properties are NULL. Coincidentally, while hasProtectedAsset for each of these items is false, isCloudItem for each of these items is true.
How can I come up with a valid assetURL that I can pass along to any media player? It feels somewhat bogus that developers can't get proper references & access to media in a user's iPod library.
I have uploaded few files on parse framework and I am live streaming those files and play them in AVPlayer.
let url = currentAudioPath
let playerItem = AVPlayerItem( URL:NSURL( string:url ) )
player1 = AVPlayer(playerItem:playerItem)
player1.rate = 1.0;
self.configurePlayer()
player1.play()
I have not downloaded the entire file at once. But is there a way to retrieve the length of the song?
"AVPlayerItem" has a ".duration" property that might help you.
Look at the notes though: You won't be able to get the duration "until the status of the AVPlayerItem is AVPlayerItemStatusReadyToPlay." You could also set a KVO on the property so you could update your UI once the item duration is determined by the AVFoundation layer.
I am working on an iOS app and I need to determine if a song has album art. I am using the MPMusicPlayerController to access the native iOS music library and I am using a MPMediaItemArtwork to capture the artwork sent from the iOS music library. This is the coding I use to get the artwork:
MPMediaItemArtwork *mpArt = [mpSong valueForProperty:MPMediaItemPropertyArtwork];
To test if artwork is present I use this:
if (mpArt)
{
imgArt = [mpArt imageWithSize:CGSizeMake(250, 250)];
}
else
{
imgArt = [UIImage imageNamed:#"Alternative_Artwork_Image.jpg"];
}
No matter what the song's artwork is, the result is always true.
Any help would be appreciated. Thank you in advance.
I think it will always return true if it is an iCloud selection because it will eventually download. Try looking for a correlation with MPMediaItemPropertyIsCloudItem
You can also try getting info from the bounds... perhaps the bounds is 0x0 when the image is not found.
I was able to display a subtitle track with AVPlayer on iOS 6, but I am not able to customize it. It just shows the same style (a small font size, in white).
Here's how I select the subtitle:
AVMediaSelectionGroup *subtitle = [asset mediaSelectionGroupForMediaCharacteristic: AVMediaCharacteristicLegible];
[self.videoPlayer.currentItem selectMediaOption:subtitle.options[0] inMediaSelectionGroup: subtitle];
And how I'm trying to customize the subtitle:
AVTextStyleRule *rule = [[AVTextStyleRule alloc] initWithTextMarkupAttributes:#{
(id)kCMTextMarkupAttribute_ForegroundColorARGB : #[ #1, #1, #0, #0 ],
(id) kCMTextMarkupAttribute_ItalicStyle : #(YES)}];
self.videoPlayer.currentItem.textStyleRules = #[rule];
No matter if I put this snippet before or after selecting the subtitle, the result is the same.
The AVPlayer is created with a local (file) URL (a mp4 file).
Any thoughts on how to do this?
I asked this question on Apple Developer Forums and I got an answer from an Apple employee:
The textStyleRules property only applies to WebVTT content. Your local file is probably carrying subtitles in TX3G format.
You're right that the documentation doesn't mention this limitation, so you should file a bug so we can get our documentation updated.
So, I'll open a radar to ask that they update the docs and I'll post its number here if someone wants to dupe it.
EDIT:
I created rdar://14923673 to ask Apple to update the docs about this current limitation. I also created rdar://14923755 to ask them to provide support to subtitles in TX3G format.
Please dupe them if you're affected by this issue.
I found workaround to modify text foreground color and background correctly.
Just separate styles to multiple AVTextStyleRule.
func initSubtitleStyle() {
let textStyle:AVTextStyleRule = AVTextStyleRule(textMarkupAttributes: [
kCMTextMarkupAttribute_CharacterBackgroundColorARGB as String: [0.2,0.3,0.0,0.3]
])!
let textStyle1:AVTextStyleRule = AVTextStyleRule(textMarkupAttributes: [
kCMTextMarkupAttribute_ForegroundColorARGB as String: [0.2,0.8,0.4,0.0]
])!
let textStyle2:AVTextStyleRule = AVTextStyleRule(textMarkupAttributes: [
kCMTextMarkupAttribute_BaseFontSizePercentageRelativeToVideoHeight as String: 20,
kCMTextMarkupAttribute_CharacterEdgeStyle as String: kCMTextMarkupCharacterEdgeStyle_None
])!
player.currentItem?.textStyleRules = [textStyle, textStyle1, textStyle2]
}
please do not ask me why, that solution come from try and error XD
I'm accessing the camera in iOS and using session presets as so:
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
Pretty standard stuff. However, I'd like to know ahead of time the resolution of the video I'll be getting due to this preset (especially because depending on the device it'll be different). I know there are tables online you can look this up (such as here: http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html ). But I'd like to be able to get this programmatically so that I'm not just relying on magic numbers.
So, something like this (theoretically):
[captureSession resolutionForPreset:AVCaptureSessionPresetMedium];
which might return a CGSize of { width: 360, height: 480}. I have not been able to find any such API, so far I've had to resort to waiting to get my first captured image and querying it then (which for other reasons in my program flow is not good).
I am no AVFoundation pro, but I think the way to go is:
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
AVCaptureInput *input = [captureSession.inputs objectAtIndex:0]; // maybe search the input in array
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
CMFormatDescriptionRef formatDescription = port.formatDescription;
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
I'm not sure about the last step and I didn't try it myself. Just found that in the documentation and think it should work.
Searching for CMVideoDimensions in Xcode you'll find the RosyWriter example project. Have a look at that code (I don't have time to do that now).
You can programmatically get the resolution from activeFormat before capture begins, though not before adding inputs and outputs: https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureDevice_Class/index.html#//apple_ref/occ/instp/AVCaptureDevice/activeFormat
private func getCaptureResolution() -> CGSize {
// Define default resolution
var resolution = CGSize(width: 0, height: 0)
// Get cur video device
let curVideoDevice = useBackCamera ? backCameraDevice : frontCameraDevice
// Set if video portrait orientation
let portraitOrientation = orientation == .Portrait || orientation == .PortraitUpsideDown
// Get video dimensions
if let formatDescription = curVideoDevice?.activeFormat.formatDescription {
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription)
resolution = CGSize(width: CGFloat(dimensions.width), height: CGFloat(dimensions.height))
if (portraitOrientation) {
resolution = CGSize(width: resolution.height, height: resolution.width)
}
}
// Return resolution
return resolution
}
FYI, I attach here an official reply from Apple.
This is a follow-up to Bug ID# 13201137.
Engineering has determined that this issue behaves as intended based on the following information:
There are several problems with the included code:
1) The AVCaptureSession has no inputs.
2) The AVCaptureSession has no outputs.
Without at least one input (added to the session using [AVCaptureSession addInput:]) and a compatible output (added using [AVCaptureSession addOutput:]), there will be no active connections, therefore, the session won't actually run in the input device. It doesn't need to -- there are no outputs to which to deliver any camera data.
3) The JAViewController class assumes that the video port's -formatDescription property will be non nil as soon as [AVCaptureSession startRunning] returns.
There is no guarantee that the format description will be updated with the new camera format as soon as startRunning returns. -startRunning starts up the camera and returns when it is completely up and running, but doesn't wait for video frames to be actively flowing through the capture pipeline, which is when the format description would be updated.
You're just querying too fast. If you waited a few milliseconds more, it would be there. But the right way to do this is to listen for the AVCaptureInputPortFormatDescriptionDidChangeNotification.
4) Your JAViewController class creates a PVCameraInfo object in retrieveCameraInfo: and asks it a question, then lets it fall out of scope, where it is released and dealloc'ed.
Therefore, the session doesn't have long enough to run to satisfy your dimensions request. You stop the camera too quickly.
We consider this issue closed. If you have any questions or concern regarding this issue, please update your report directly (http://bugreport.apple.com).
Thank you for taking the time to notify us of this issue.
Best Regards,
Developer Bug Reporting Team
Apple Worldwide Developer Relations
According to Apple, there's no API for that. It stinks, I've had the same problem.
May be you can provide a list of all posible preset resolutions for every iPhone model and check which device model the app is running on? - using something like this...
[[UIDevice currentDevice] platformType] // ex: UIDevice4GiPhone
[[UIDevice currentDevice] platformString] // ex: #"iPhone 4G"
However, you have to update the list for each newer device model. Hope this helps :)
if preset is .photo, the return size is for still photo size, not preview video size
if preset is not .photo, the return size is for video size, not for captured photo size.
if self.session.sessionPreset != .photo {
// return video size, not captured photo size
let format = videoDevice.activeFormat
let formatDescription = format.formatDescription
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription)
} else {
// other way to get video size
}
Answer of #Christian Beer is a good way for specified preset.
My way is a good for active preset.
The best way to do what you want (get a known video or image format) is to set the format of the capture device.
First find the capture device you want to use:
if #available(iOS 10.0, *) {
captureDevice = defaultCamera()
} else {
let devices = AVCaptureDevice.devices()
// Loop through all the capture devices on this phone
for device in devices {
// Make sure this particular device supports video
if ((device as AnyObject).hasMediaType(AVMediaType.video)) {
// Finally check the position and confirm we've got the back camera
if((device as AnyObject).position == AVCaptureDevice.Position.back) {
captureDevice = device as AVCaptureDevice
}
}
}
}
self.autoLevelWindowCenter = ALCWindow.frame
if captureDevice != nil && currentUser != nil {
beginSession()
}
}
func defaultCamera() -> AVCaptureDevice? {
if #available(iOS 10.0, *) { // only use the wide angle camera never dual camera
if let device = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera,
for: AVMediaType.video,
position: .back) {
return device
} else {
return nil
}
} else {
return nil
}
}
Then find the formats that that device can use:
let options = captureDevice!.formats
var supportable = options.first as! AVCaptureDevice.Format
for format in options {
let testFormat = format
let description = testFormat.description
if (description.contains("60 fps") && description.contains("1280x 720")){
supportable = testFormat
}
}
You can do more complex parsing of the formats, but you might not care.
Then just set the device to that format:
do {
try captureDevice?.lockForConfiguration()
captureDevice!.activeFormat = supportable
// setup other capture device stuff like autofocus, frame rate, ISO, shutter speed, etc.
try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice!))
// add the device to an active CaptureSession
}
You may want to look at the AVFoundation docs and tutorial on AVCaptureSession as there are lots of things you can do with the output as well. For example, you can convert the result to .mp4 using AVAssetExportSession so that you can post it on YouTube, etc.
Hope this helps
Apple is using 4:3 ratio for the iPhone camera.
You can you this ratio to get the frame size of the captured video by fixing either the width or height constraint of the AVCaptureVideoPreviewLayer and set the aspect ratio constraint to 4:3.
In the left image, the width was fixed to 300px and the height was retrieved by setting the 4:3 ratio, and it was 400px.
In the right image, the height was fixed to 300px and width was retrieved by setting the 3:4 ratio, and it was 225px.