28

So far I know how to stream a video and how to download it and afterwards stream it, but here's the tricky bit: streaming it once, storing it on the device and in the future play it from the device.

Is that possible?

Tudor
  • 4,137
  • 5
  • 38
  • 54
  • 7
    For anybody how encouters this problem in the future, here is the rough solution I found: Use an NSURLConnection to async download the content of the video URL into a local file and after a certain fraction has been downloaded (2 MB worked for me), play the file. NSURLConnection with connection:didReceiveData delegate method does the trick. – Tudor Jun 09 '11 at 16:15
  • It's not clever because it doesn't fully work. – Tudor Jul 12 '11 at 11:47
  • 3
    "iPhone OS Note: The NSURLDownload class is not available in iPhone OS as downloading directly to the file system is discouraged. Use the NSURLConnection class instead. See “Using NSURLConnection” for more information." – Tudor Jul 13 '11 at 13:34
  • Have you tried creating a HTTP server on the iPhone and re-streaming from localhost as the file is downloading? – Steven Veltema Sep 06 '11 at 11:21
  • @Steven, that sounds interesting. Do you have any links/tips on this? – Tudor Sep 09 '11 at 09:08
  • I have done this now on android, but not iOS. The implementation should be fairly similar. An incomplete android example can be seen here: http://stackoverflow.com/a/5432091/256372 Addition of some more robust HTTP responses (416, 206, 501) and careful buffering so that tne media player dosen't overrun does the trick on android. If I ever get around to writing an iOS version, I'll post a followup. – Steven Veltema Dec 13 '11 at 05:36
  • Did you check this.. http://stackoverflow.com/q/34526253/3908884 – Meet Doshi Jan 06 '16 at 13:59

5 Answers5

1

It's quite easy to save the video. Do something similar to this:

//Saving Movie
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];          
[archiver encodeObject:*MovieObject* forKey:@"MovieObjectDataKey"];
[archiver finishEncoding];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"MovieObjectDefaultsDataKey"];
[archiver release];
[data release];

//Retrieving movie 
NSData *savedMovieData = [[NSUserDefaults standardUserDefaults] objectForKey:@"MovieObjectDefaultsDataKey"];
if (savedMovieData != nil) {
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:savedMovieData];
    *MovieObject* = [[unarchiver decodeObjectForKey:@"MovieObjectDataKey"] retain];
    [unarchiver finishDecoding];
    [savedMovieData release];
    [unarchiver release];
} else {
    //Download Stream of Your Movie
}

The only thing you really have to change there is * MovieObject *, once in each step.

David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
Dyldo42
  • 2,358
  • 2
  • 22
  • 26
  • I'm not sure what you are doing in there? Archiving for what? – Tudor Jul 12 '11 at 11:49
  • * MovieObject * is the variable that stores your movie. Just to clarify, you've downloaded a movie and want to save it so you can load it again next time, yes? Or are you streaming the movie and want to store it in a local variable? – Dyldo42 Jul 13 '11 at 06:18
  • I want to start downloading the video and start playing it, even if it's partially downloaded. It's kind of like streaming, but while caching on disk. Next time the movie plays, it plays the disk version. – Tudor Jul 13 '11 at 07:41
  • Gotcha. Are you already able to play it while it's being downloaded? – Dyldo42 Jul 18 '11 at 03:58
  • 1
    Yes, but I'm using an ugly approach: in connection:didReceiveData from NSURLConnection, after each 10 seconds I reload the movie from disk, from the previous playback position. When the download is finished, I reload it a final time. This is ugly for many reasons, the uglies is a short black frame while the video is reloaded. – Tudor Aug 16 '11 at 21:22
  • to avoid the black frame: can't you reload it to another object and then change the to displaying data from this object and deallocing the first one ? – Robin Rye Aug 17 '11 at 07:17
1

Not quite sure here how you get your stream but look in to the AVAssetWriter, AVAssetWriterInput and AVAssetWriterPixelBufferAdaptor and as soon as you receive data you should be able to append the data to the to the pixel buffer adaptor using:

appendPixelBuffer:withPresentationTime:

not sure it will work for you but with some fiddling you should be able to adapt your input to match this method. There are lots of example code for setting up the writer

Robin Rye
  • 480
  • 1
  • 7
  • 20
  • Hmm..I'm not sure how this fits in the picture. – Tudor Aug 16 '11 at 21:20
  • you receive a video stream from somewhere? if you can get the stream frames, and append each of these to the AVAssetWriterPixelBufferAdaptor this will write it to a file and afterwards you can play it from what ever location you store it to on device. – Robin Rye Aug 17 '11 at 07:09
0

I know what you want to achieve, I only got a workaround. I had to implement the same behavior and ended up with streaming the video from the server and downloading it next to streaming. Next time the user tries to stream the video determine whether it was downloaded to disk, otherwise stream it again. In a normal case the video was downloaded properly and could be reviewed offline.

Martin Stolz
  • 5,176
  • 3
  • 21
  • 19
0
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:somePath];

and

fileURLWithPath:isDirectory:

Initializes and returns a newly created NSURL object as a file URL with a specified path.

+ (id)fileURLWithPath:(NSString *)path isDirectory:(BOOL)isDir

Parameters

path

The path that the NSURL object will represent. path should be a valid system path. If path begins with a tilde, it must first be expanded with stringByExpandingTildeInPath. If path is a relative path, it is treated as being relative to the current working directory. Passing nil for this parameter produces an exception.

isDir

A Boolean value that specifies whether path is treated as a directory path when resolving against relative path components. Pass YES if the path indicates a directory, NO otherwise. Return Value An NSURL object initialized with path.

Availability

Available in iOS 2.0 and later.

You can't stream it and save it at the same time, especially with large video files as the Apple doc sais that you must use a transport stream for HTTP Live Streaming.

Noji
  • 56
  • 3
0

ASIHttpRequest might make your life easier.

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadDestinationPath:@"video.m4v"]; // use [NSBundle mainBundle] to find a better place

From your delegate, handle this:

- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data;

Do whatever data transcoding with data as you get it and push it off to your AVAssetWriter or movie player layer in real time, whatever you are using. When you're done, the asset should still be saved so you can get it later.

slf
  • 22,595
  • 11
  • 77
  • 101
  • I see another answer about how to download the video, but yours has an hidden insteresting part : "you get it and push it off to your AVAssetWriter or movie player layer in real time, whatever you are using". Can you elaborate on that bit? That is what I'm curious about. – Tudor Sep 23 '11 at 17:13
  • How are you currently streaming video? Or are you not yet? – slf Sep 24 '11 at 01:02