Playing audio files continually using audio recorded url - ios

I am newcomer in Objective-C and have experience only 12 months in iPhone development.
I am recording audio files in One UIViewController, and playing on another UIViewController. For playing purpose i am saving the date string for generation of url,it is fine working properly,
But ,now my problem is i want to play previous audio record file for some time after that i want to play next audio file using url . i am saving all the data using nsuser dafaults please help me
NSM![enter image description here][1]utableArray *dateString;
NSURL recordFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:[self.dateString objectAtIndex:sender.tag]]];
For playing
player = [[AVAudioPlayer alloc] initWithContentsOfURL:recordFile error:&error];
from fig when i click a tag 2 i want to play first 5 sec tag1 after that i want to play tag2

Record the audio -> Save the audio in the Temp -> Track the saved path of the audio file (You can store this saved path in an array) . Repeat the same steps for the next file.
Use AVQueuePlayerfor playing items one after the other.

Related

Ios Recorded video Compression/Optimization, reduce weight but conserve resolution at 1920x1080

In my app users records videos and upload it on their youtube channels. I have notice that for a video of 4 seconds my app needs twice the time of the official Youtube app to upload a video. I'm using the ios rest Framework from google, so i think that's a difference in weight beetween my 4 seconds video and a 4 seconds video uploaded by official youtube app. So i wanna reduce my video file weight, but at the same time i wanna stay with a resolution of 1920x1080, so how i can i do it?
I have to work on every single frame, to get a lower quality?
I have to lower the bitrate?
My actual approch is to record a full quality video and save it as a file in documents directory. Then i retake the video to trying to compress.
I have tried:
- (void)compressVideo:(NSURL*)inputURL
outputURL:(NSURL*)outputURL2
handler:(void (^)(AVAssetExportSession*))completion {
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:urlAsset presetName:AVAssetExportPreset1920x1080];
exportSession.outputURL = outputURL2;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
//completion(exportSession);
NSData *prima = [NSData dataWithContentsOfURL:self.outputURL];
NSLog(#"Size of new Video before compression is (bytes):%lu",(unsigned long)[prima length]);
NSData *newDataForUpload = [NSData dataWithContentsOfURL:outputURL2];
NSLog(#"Size of new Video after compression is (bytes):%lu",(unsigned long)[newDataForUpload length]);
[self performSegueWithIdentifier:#"registraToSaved" sender:nil];
}];
}
but this code itself reduce the weight only if i change the size of the video.
I have read about solutions like this:
How can I reduce the file size of a video created with UIImagePickerController?
but they works with AssetWriter and in the library i use to record the video this object is not used. There's a way to use a similar concept with LLSimpleCamera library ? Or maybe there is a way do modify my compressVideo method to achive this result?
EDIT
this is the way i have tested the upload. I can record video directly in my app (using LLSimpleCamera library), so : record a 4 seconds video by my app -> upload to youtube by my app and google rest library .
To make a match with the official youtube app i have recorded the same subject for 4 seconds by youtube app and then , always by youtube app, i have uploaded to my channel.
Probably those two recorded video have not the same exact weight, but the difference are surely minimal. Anyway the upload time with my app is very long , so i have started to think that youtube create a smaller file for a 4 seconds recorded video 1920x1080 than my app

AVURLAsset tracks is empty, but video/audio is playable

I have video that lives here:
http://195.16.112.71/adaptive/66aebabb-2632-44fc-abf1-df29bca6b941.video/66aebabb-2632-44fc-abf1-df29bca6b941.m3u8
Ffmpeg says that this video has 5 tracks and it's correctly.
But if I use AVURLAsset with that link it says me that there isn't any tracks:
NSArray* const tracks = asset.tracks; // it's empty
I modified Apple's StichedStreamPlayer sample to reproduce this problem, it lies here:
https://yadi.sk/d/hV3jfbx1Z9sfC
Simply click 'Load Movie', than the 'Play' button - movie plays perfectly, but if you check tracks variable in prepareToPlayAsset function you find it's empty.
The question is: why it's empty if in reality the video has 5 tracks and how this video could be playing if no tracks exist, as AVURLAsset says?
Thanks for your help in advance!
If you are directly streaming the video then it won't have tracks. You can download the file and ask for the tracks of the video file asset.

How to play an array of songs using streamingkit library

Has anyone out there ever used this https://github.com/tumtumtum/StreamingKit/ package to play remote mp3 files? How can one load an array of music urls and play them one after the other?
Just as document says:
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer queue:#"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer queue:#"http://www.abstractpath.com/files/audiosamples/airplane.aac"];
You just need to add url to queue.

Stream video while downloading iOS

I am using iOS 7 and I have a .mp4 video that I need to download in my app. The video is large (~ 1 GB) which is why it is not included as part of the app. I want the user to be able to start watching the video as soon as is starts downloading. I also want the video to be able to be cached on the iOS device so the user doesn't need to download it again later. Both the normal methods of playing videos (progressive download and live streaming) don't seem to let you cache the video, so I have made my own web service that chunks up my video file and streams the bytes down to the client. I start the streaming HTTP call using NSURLConnection:
self.request = [[NSMutableURLRequest alloc] initWithURL:self.url];
[self.request setTimeoutInterval:10]; // Expect data at least every 10 seconds
[self.request setHTTPMethod:#"GET"];
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:YES];
When I receive a data chunk, I append it to the end of the local copy of the file:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[self videoFilePath]];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:data];
}
If I let the device run, the file is downloaded successfully and I can play it using MPMoviePlayerViewController:
NSURL *url=[NSURL fileURLWithPath:self.videoFilePath];
MPMoviePlayerViewController *controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];
However, if I start the player before the file is completely downloaded, the video starts playing just fine. It even has the correct video length displayed at the top scrubber bar. But when the user gets to the position in the video that I had completed downloading before the video started, the video just hangs. If I close and reopen the MPMoviePlayerViewController, then the video plays until it gets to whatever location I was then at when I launched the MPMoviePlayerViewController again. If I wait until the entire video is downloaded, then the video plays without a problem.
I am not getting any events fired, or error messages printed to the console when this happens (MPMoviePlayerPlaybackStateDidChangeNotification and MPMoviePlayerPlaybackDidFinishNotification are never sent after the video starts). It seems like there is something else that is telling the controller what the length of the video is other than what the scrubber is using...
Does anyone know what could be causing this issue? I am not bound to using MPMoviePlayerViewController, so if a different video playback method would work in this situation I am all for it.
Related Unresolved Questions:
AVPlayer and Progressive Video Downloads with AVURLAssets
Progressive Video Download on iOS
How to play an in downloading progress video file in IOS
UPDATE 1
I have found that the video stall is indeed because of the file size when the video starts playing. I can get around this issue by creating a zero-ed out file before I start the download and over overwrite it as I go. Since I have control over the video streaming server, I added a custom header so I know the size of the file being streamed (default file size header for a streaming file is -1). I am creating the file in my didReceiveResponse method as follows:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// Retrieve the size of the file being streamed.
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSDictionary *headers = httpResponse.allHeaderFields;
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
self.streamingFileSize = [formatter numberFromString:[headers objectForKey:#"StreamingFileSize"]];
// Check if we need to initialize the download file
if (![[NSFileManager defaultManager] fileExistsAtPath:self.path])
{
// Create the file being downloaded
[[NSData data] writeToFile:self.path atomically:YES];
// Allocate the size of the file we are going to download.
const char *cString = [self.path cStringUsingEncoding:NSASCIIStringEncoding];
int success = truncate(cString, self.streamingFileSize.longLongValue);
if (success != 0)
{
/* TODO: handle errors here. Probably not enough space... See 'man truncate' */
}
}
}
This works great, except that truncate causes the app to hang for about 10 seconds while it creates the ~1GB file on disk (on the simulator it is instant, only a real device has this problem). This is where I am stuck now - does anyone know of a way to allocate a file more efficiently, or a different way to get the video player to recognize the size of the file without needing to actually allocate it? I know some filesystems support "file size" and "size on disk" as two different properties... not sure if iOS has something like that?
I figured out how to do this, and it is much simpler than my original idea.
First, since my video is in .mp4, the MPMoviePlayerViewController or AVPlayer class can play it directly from a web server - I don't need to implement anything special and they can still seek to any point in the video. This must be part of how the .mp4 encoding works with the movie players. So, I just have the raw file available on the server - no special headers required.
Next, when the user decides to play the video I immediately start playing the video from the server URL:
NSURL *url=[NSURL fileURLWithPath:serverVidelFileURLString];
controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];
This makes it so the user can watch the video and seek to any location they want. Then, I start downloading the file manually using NSURLConnection like I had been doing above, except now I am not streaming the file, I just download it directly. This way I don't need the custom header since the file size is included in the HTTP response.
When my background download completes, I switch the playing item from the server URL to the local file. This is important for network performance because the movie players only download a few seconds ahead of what the user is watching. Being able to switch to the local file as soon as possible is key to avoid downloading too much duplicate data:
NSTimeInterval currentPlaybackTime = videoController.moviePlayer.currentPlaybackTime;
[controller.moviePlayer setContentURL:url];
[controller.moviePlayer setCurrentPlaybackTime:currentPlaybackTime];
[controller.moviePlayer play];
This method does have the user downloading two video files at the same time initially, but initial testing on the network speeds my users will be using shows it only increases the download time by a few seconds. Works for me!
You gotta create an internal webserver that acts like a proxy! Then set your player to play the movie from the localhost.
When using HTTP protocol to play a video with MPMoviePlayerViewController, the first thing the player does is to ask for the byte-range 0-1 (first 2 bytes) just to obtain the file length. Then, the player asks for "chunks" of the video using the "byte-range" HTTP command (the purpose is to save some battery).
What you have to do is to implement this internal server that delivers the video to the player, but your "proxy" must consider the length of your video as the full length of the file, even if the actual file hasn't been completely downloaded from the internet.
Then you you set your player to play a movie from " http:// localhost : someport "
I've done this before... it works perfectly!
Good luck!
I can only assume that the MPMoviePlayerViewController caches the file length of the file when you started it.
The way to fix (just) this issue is to first determine how large the file is. Then create a file of that length. Keeping an offset pointer, as the file downloads, you can overwrite the "null" values in the file with the real data.
So you get to a specific point in the download, start the MPMoviePlayerViewController, and let it run. I'd also suggest you use the "F_NOCACHE" flag (with fcntl()) so you bypass the file block cache (which means you will lower your memory footprint).
The downside to this architecture is that if you get stalled, and the movie player gets ahead of you, well, the user is going to have a pretty bad experience. Not sure if there is any way for you to monitor and take preemptive action.
EDIT: its quite possible that the video is not read sequentially, but certain information requires the player to essentially look ahead for something. If so, then this is doomed to fail. The only other possible solution is to use some software tool to sequentially order the file (I'm no video expert so cannot comment from experience on any of the above).
To test this out, you can construct a "damaged" video of varying lengths, and test that to see what works and what does not. For instance, suppose you have a 100Meg file. Write a little utility program, and over write the last 50Megs of data with zeros. Now play this video. Its should fail 1/2 through. If it fails right away, well, you now know that its seeking in the file.
If non sequential, its possible that its looking at the last 1000 bytes or so, in which case if you don't overwrite that things work as you want. If you get lucky and this is the case, you would eventually download the last 1000 bytes, then then start from the front of the file.
It really gets down to finding some way before introducing real networking into the picture, to play a partial file. You will surely find it easier to artificially introduce the networking conditions without really doing it real time.

AVAudioPlayer not working with .pls shoutcast file?

Good Day,
I am working on one Radio APP that gets shoutcast streaming .pls file and plays it with help of AVFoundation framework.
This job is easily done with AVPlayer, but the problem with it is that I can not code or find any good solution to get it working with volume slider, the AVPlayer class does not have volume property.
So now I am trying to get it working with AVAudioPlayer which has volume property, and here is my code:
NSString *resourcePatch = #"http://vibesradio.org:8002/listen.pls";
NSData *_objectData = [NSData dataWithContentsOfURL:[NSURL URLWithString:resourcePatch]];
NSError *error;
vPlayer = [[AVAudioPlayer alloc] initWithData:_objectData error:&error];
vPlayer.numberOfLoops = 0;
vPlayer.volume = 1.0f;
if (vPlayer == nil)
NSLog(#"%#", [error description]);
else
[vPlayer play];
This code is working with uploaded .mp3 files on my server but it is not working with .pls files generated by shoutcast,
Is there any way to fix AVAudioPlayer to work with .pls files, or implement volume slider to AVPlayer ?
Using AVAudioPlayer for stream from network is not a good idea. See what Apple's documentation say on AVAudioPlayer:
An instance of the AVAudioPlayer class, called an audio player,
provides playback of audio data from a file or memory.
Apple recommends that you use this class for audio playback unless you
are playing audio captured from a network stream or require very low
I/O latency.
For change AVPlayer's volume check this question:Adjusting the volume of a playing AVPlayer

Resources