Uploading Video with iPhone
Asked Answered
D

4

7

Is it possible to upload video to a server? I know that images are possible. If someone can just point me in the right direction that would be awesome.

Thanks

Danialdaniala answered 30/6, 2009 at 19:47 Comment(1)
are you looking to upload video taken by the new iPhone 3GS or video stored locally on the device?? Remember there is no way to access the global file system of the iPhone so the latter option is not feasible.Erde
C
17

Edited Aug 2015

This answer is now seriously out of date. At the time of writing there weren't to many options and videos were relatively small in size. If you are looking at doing this now, I would use AFNetworking which makes this much simpler. It will stream the upload from file rather than holding it all in memory, and also supports the new Apple background upload Task.

Docs here: https://github.com/AFNetworking/AFNetworking#creating-an-upload-task

--

Yes this is possible and this is how i went about it.

Implement the following function which runs when the media picker is finished.

- (NSData *)generatePostDataForData:(NSData *)uploadData
{
    // Generate the post header:
    NSString *post = [NSString stringWithCString:"--AaB03x\r\nContent-Disposition: form-data; name=\"upload[file]\"; filename=\"somefile\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\n" encoding:NSASCIIStringEncoding];

    // Get the post header int ASCII format:
    NSData *postHeaderData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

    // Generate the mutable data variable:
    NSMutableData *postData = [[NSMutableData alloc] initWithLength:[postHeaderData length] ];
    [postData setData:postHeaderData];

    // Add the image:
    [postData appendData: uploadData];

    // Add the closing boundry:
    [postData appendData: [@"\r\n--AaB03x--" dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];

    // Return the post data:
    return postData;
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ 

    //assign the mediatype to a string 
    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    //check the media type string so we can determine if its a video
    if ([mediaType isEqualToString:@"public.movie"]){
        NSLog(@"got a movie");
        NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
        NSData *webData = [NSData dataWithContentsOfURL:videoURL];
        [self post:webData];
        [webData release];

    }

for the post function i had something like this which i got from somewhere else (sorry i dont know where i found it):

- (void)post:(NSData *)fileData
{

    NSLog(@"POSTING");

    // Generate the postdata:
    NSData *postData = [self generatePostDataForData: fileData];
    NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

    // Setup the request:
    NSMutableURLRequest *uploadRequest = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.example.com:3000/"] cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval: 30 ] autorelease];
    [uploadRequest setHTTPMethod:@"POST"];
    [uploadRequest setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [uploadRequest setValue:@"multipart/form-data; boundary=AaB03x" forHTTPHeaderField:@"Content-Type"];
    [uploadRequest setHTTPBody:postData];

    // Execute the reqest:
    NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:uploadRequest delegate:self];
    if (conn)
    {
        // Connection succeeded (even if a 404 or other non-200 range was returned).
        NSLog(@"sucess");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Got Server Response" message:@"Success" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];
    }
    else
    {
        // Connection failed (cannot reach server).
        NSLog(@"fail");
    }

}

The above snippet builds the http post request and submits it. You will need to modify it if you want decent error handling and consider using a library that allows async upload (theres one on github)

Also Notice the port :3000 on the server url above, I found it easy for bug testing to start a rails server on its default port 3000 in development mode so i could see the request parameters for debugging purposes

Hope this helps

Celenacelene answered 7/8, 2009 at 9:46 Comment(5)
No Probs. One thing to note is the current SDK doesnt seem to give you access to the full res video as it would be synced to iTunes. My tests showed maximum video size of 480x360. If you can find a work around please let us know. thanksCelenacelene
Is this missing garbage collection?Mississippian
It actually means you load whole video data into memory? Good luck with that (memory usage). Of course it would work for relatively short videos, but longer videos will require more sophisticated approach (streaming from file, etc...)Tolbooth
@Tolbooth agree, NSData *webData = [NSData dataWithContentsOfURL:videoURL]; will crash the App immediately if the video is big enough.Tecumseh
@Raptor: Exactly, and that is the CRUCIAL issue here. One just can not post app for production with loading whole video into memory. Videos just MUST be streamed directly from file. I have posted 2 built-in solutions available from both iOS8 and iOS7(no need to use 3rd party libraries) here: https://mcmap.net/q/1402646/-uploading-video-with-iphoneTolbooth
T
3

Since iOS8 there is no need to use 3rd party libraries and you can stream video directly from the file which solves crucial OUT OF MEMORY ERROR when you try to upload bigger videos while loading them from file:

// If video was returned by UIImagePicker ...
NSURL *videoUrl = [_videoDictionary objectForKey:UIImagePickerControllerMediaURL];

NSMutableURLRequest *request =[[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:VIDEO_UPLOAD_LINK]];
[request addValue:@"video" forHTTPHeaderField: @"Content-Type"];
[request setHTTPMethod:@"POST"];

NSInputStream *inputStream = [[NSInputStream alloc] initWithFileAtPath:[videoUrl path]];
[request setHTTPBodyStream:inputStream];

self.uploadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

iOS7 also offers great NSURLSeession / NSURLSessionUploadTask combo solution, which not only let's you stream directly from the file, but can also delegate task to the iOS process, which will let upload to finish even when your app is closed. It requires a bit more coding and I have no time to write it all here (you can Google it).

Here are the most crucial parts:

  1. Confugure audio session in background support:

    -(NSURLSession *)urlSession{

    if (!_urlSession) {
    
    
        NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
        NSString *bundleId = infoDict[@"CFBundleIdentifier"];
    
        NSString *label = [NSString stringWithFormat:@"ATLoggerUploadManager_%@", bundleId];
    
        NSURLSessionConfiguration *conf = (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) ? [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:label] : [NSURLSessionConfiguration backgroundSessionConfiguration:label];
        conf.allowsCellularAccess = NO;
    
        _urlSession = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:self.urlSessionQueue];
        _urlSession.sessionDescription = @"Upload log files";
    
    }
    
    return _urlSession;
    

    }

  2. Upload task method:

    -(NSURLSessionUploadTask *)uploadTaskForFilePath:(NSString *)filePath session:(NSURLSession *)session{

    NSFileManager *fm = [NSFileManager defaultManager];
    NSError *error = nil;
    
    // Consruct request:
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setHTTPMethod:@"POST"];
    NSString *finalUrlString = [self.uploadURL absoluteString];
    
    if (self.uploadUserId) {
        [request setValue:self.uploadUserId forHTTPHeaderField:@"X-User-Id"];
        finalUrlString = [finalUrlString stringByAppendingFormat:@"?id=%@", self.uploadUserId];
    }
    
    [request setURL:[NSURL URLWithString:finalUrlString]];
    
    /*
     It looks like this (it only works if you quote the filename):
     Content-Disposition: attachment; filename="fname.ext"
     */
    
    NSString *cdh = [NSString stringWithFormat:@"attachment; filename=\"%@\"", [filePath lastPathComponent]];
    [request setValue:cdh forHTTPHeaderField:@"Content-Disposition"];
    
    error = nil;
    unsigned long fileSize = [[fm attributesOfItemAtPath:filePath error:&error] fileSize];
    
    if (!error) {
    
        NSString *sizeInBytesAsString = [NSString stringWithFormat:@"%lu", fileSize];
        [request setValue:sizeInBytesAsString forHTTPHeaderField:@"X-Content-Length"];
    
    }
    
    NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileUrl];
    uploadTask.taskDescription = filePath;
    
    return uploadTask;
    

    }

  3. Upload function:

    [self.urlSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

        NSMutableDictionary *tasks = [NSMutableDictionary new];
    
        int resumed_running_count = 0;
        int resumed_not_running_count = 0;
        int new_count = 0;
        // 1/2. Resume scheduled tasks:
        for(NSURLSessionUploadTask *task in uploadTasks) {
    
            //MILogInfo(@"Restored upload task %zu for %@", (unsigned long)task.taskIdentifier, task.originalRequest.URL);
    
            if (task.taskDescription) {
    
                [tasks setObject:task forKey:task.taskDescription];
            }
    
            BOOL isRunning = (task.state == NSURLSessionTaskStateRunning);
            if (!isRunning) {
    
                resumed_not_running_count++;
            }else{
                resumed_running_count++;
            }
    
             [task resume];
        }
    
        // 2/2. Add tasks / files not scheduled yet:
        NSString *uploadFilePath = nil;
    
            // already uploading:
            if (![tasks valueForKey:uploadFilePath]) {
                NSURLSessionUploadTask *uploadTask = [self uploadTaskForFilePath:uploadFilePath session:_urlSession];
                new_count++;
                [uploadTask resume];
            }
    
    }];
    
  4. Background session requires UIApplecation delegate (AppDelegate callback implemented:

    • (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {

      NSLog(@"Background URL session needs events handled: %@", identifier);
      completionHandler();
      

      }

Tolbooth answered 3/7, 2015 at 8:48 Comment(0)
B
2

Have a look at the UIImagePickerController. As of 3.0 you can allow the choose to shoot a video or pick an existing video. According to the docs you're limited to 10min max on the movie though:

http://developer.apple.com/IPhone/library/documentation/UIKit/Reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html

Bousquet answered 30/6, 2009 at 19:51 Comment(0)
B
0
NSURL *urlvideo = [info objectForKey:UIImagePickerControllerMediaURL];

 NSString *urlString=[urlvideo path];

NSLog(@"urlString=%@",urlString);

NSString *str = [NSString stringWithFormat:@"you url of server"];

NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];


ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];

[request setFile:urlString forKey:@"key foruploadingFile"];

[request setRequestMethod:@"POST"];

[request setDelegate:self];

[request startSynchronous];

NSLog(@"responseStatusCode %i",[request responseStatusCode]);

NSLog(@"responseStatusCode %@",[request responseString]);
Buttonhook answered 14/6, 2012 at 4:55 Comment(1)
You can send video file captured by uiimagepicker controller by using this code.and you have to grab assihttprequest framework from githubButtonhook

© 2022 - 2024 — McMap. All rights reserved.