MFMailComposeViewController large Video attachment failed to send - ios

I am sending video file as an mail attachment, when i send small video then every thing is work perfect and mail has sent, but when video size is large the it give memory warning several time and unable to sent the mail. I am allocating memory for NSdata and releasing it properly. Please give your valuable suggestion. Thanks in advance
Here is my code
MFMailComposeViewController *picker1 = [[MFMailComposeViewController alloc] init];
picker1.mailComposeDelegate = self;
[picker1 setSubject:#"AKR Video"];
NSData *videoData=[[NSData alloc] initWithContentsOfFile:self.path ];
[picker1 addAttachmentData:videoData mimeType:#"video/MOV" fileName:#"Video.MOV"];
[videoData release];
[self presentModalViewController:picker1 animated:YES];
[picker1 release];

Loading a 15 MB anything into RAM on a pre-2009 iPhone or iPod is going to really push the limits of your process's available memory, so it's not surprising there's a cap. I wouldn't try to convince the compose view to accept a larger attachment; instead, I'd suggest you transfer the file to some external server and either e-mail a link to it or send the mail from there.(+1)

Related

MFMailComposeViewController Large Pdfs attachments get stuck

So I am using MFMailComposeViewController to attach pdf files to an email and send it out using the default iOS mail app set up with an exchange account.
If I send a file with size 4863K it works perfectly. Everything goes off as expected.
If I send a file with size 5688K it seems like its working and gets MFMailComposeResultSent as the result sent back to the delegate.
However, the mail never arrives and when I look in the mail app, I see that it is stuck in the Outbox with an error of:
The server rejected the message
What's super weird is that when I try to resent the message from the outbox, it sends out perfectly fine and gives no further error message.
I am sort of at my wits end and have not the first clue what could be going wrong. Clearly I cannot screw around with apple's code, but there don't seem to be many options with the MFMailComposeViewController that I could possibly have screwed up.
The code is fairly straightforward:
- (void) setAttachments:(NSArray*)attatchments
ofDraft:(MFMailComposeViewController*)draft
{
if (attatchments)
{
for (NSString* path in attatchments)
{
NSData* data = [self getDataForAttachmentPath:path];
NSString* basename = [self getBasenameFromAttachmentPath:path];
NSString* pathExt = [basename pathExtension];
NSString* fileName = [basename pathComponents].lastObject;
NSString* mimeType = [self getMimeTypeFromFileExtension:pathExt];
// Couldn't find mimeType, must be some type of binary data
if (mimeType == nil) mimeType = #"application/octet-stream";
[draft addAttachmentData:data mimeType:mimeType fileName:fileName];
}
}
}
The fact that this works for smaller files implies to me that there is nothing wrong with this code and it is copied out of a cordova plugin that sees pretty wide usage. But for whatever reason, files that are over ~5MB just fail for no apparent reason.
Thanks for your help.
Update
I captured the http traffic and discovered that in the situation where the mail is sent using the MFMailComposeViewController, the POST request has a content-length of 0, which is not allowed, so the server returns a 400 error. When I try to resend the same message from the mail app directly, the content-length is the size expected.
This appears to be a limitation of the MessageUI framework, but I cannot verify that there is a universal limitation imposed by Apple.

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.

Can I send UIDocuments by email, as an attachment?

I'm working with Xcode.
In my app I save some UIdocuments at that location
[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
I'm searching for a way to share documents, my first option is by email.
Can I send those documents by email, as an attachment? Can I open then with another device with the same app?
You could do like the following.
Create a MFMailComposeViewController and use - (void)addAttachmentData:(NSData*)attachment mimeType:(NSString*)mimeType fileName:(NSString*)filename method to add your attachment.
For example.
MFMailComposeViewController *mailVC = [[MFMailComposeViewController alloc] init];
[mailVC setSubject:#"Shared documents"];
[mailVC setToRecipients:#[#"sample#example.com"]];
[mailVC setMessageBody:#"Here the docs I want to share" isHTML:NO];
[mailComposer addAttachmentData:pdfData mimeType:#"application/pdf" fileName:#"file.pdf"];
[mailVC setMailComposeDelegate:self];
[self presentViewController:mailVC animated:YES completion:nil];
where pdfData is of type NSData. So, you need to transform your document into a NSData.
From Apple doc.
addAttachmentData:mimeType:fileName:
This method attaches the specified data after the message body but
before the user’s signature. You may attach multiple files (using
different file names) but must do so prior to displaying the mail
composition interface. Do not call this method after presenting the
interface to the user.
About the second part of your question. Could you explain what type of document do you need to display?
In the meantime, take a look at Adding "Open In..." option to iOS app.
To send any attachment you need to get the contents into an NSData object. If the document is on disk then this is simple. You just need the path or file URL to the document. Then you can create the NSData object using the path or URL.
If the receiver of the email has the same app and the app is setup to appear in the "Open In" menu for documents of this type, then the user can open the app from the attachment. Your app then just needs to know what to do when it is asked to open a file of this type. There are plenty of existing documentation and questions here on SO that describe how to register an app to open certain file types.

VLC-can't playback online video

i'm trying to use VLC to playback youtube online video for IOS5.
I set a NSURL to MVLCMovieViewController, use code like this:
NSString *conntentURL = #"http://www.youtube.com/watch?v=FWKYriGgmCo";//(it's a workable link)
NSURL *url = [NSURL URLWithString:connectURL];
MVLCMovieViewController *movieViewController = [[MVLCMovieViewController alloc] init];
movieViewController.url = url;
[self presentModalViewController:movieViewController animated:YES];
[movieViewController release];
run the app, but i got a stop at http.c file with a hint "Program received signal "EXC_BAD_ACCESS"" near code:
p_sys->psz_user_agent = var_InheritString(p_access, "http-user-agent");
for(char *p = p_sys->psz_user_agent; *p, p++)
So does VLC support online playbacking? Or what should to be modified so that i can play a url directly on ios?
Thanks a lot for your help in advance!
I've done a lot of work on the VLC iOS source code, to try to get it to handle RTP and UDP streams. The short answer is that I didn't get it to work for those protocols but HTTP works, and the blocking seems to be at the OS level.
If you want the details on what I did to make VLC compile correctly and work on the latest XCode, please read the following forum thread https://forum.videolan.org/viewtopic.php?f=12&t=108691
Since YouTube seems to be HTTP, it should work but your mileage may vary.
Hope this helps.

Issue when using MFMailComposeViewController

I have a tricky problem. In one of my app, with over 150.000 downloads... I have a problem which seldom occurs and which I can't seem to figure out.
The problem is the following:
In a view where the user can share a list via email, I open the mail window using MFMailComposeViewController. However, in some few cases the app seems to get a problem using the mail composer. The user presses the share button, the mail windows slides up, waits about 1-2 sec and then closes again. No content in the mail window, although I do send data to it.
I myself have not been able to re-create the problem on any device or in the simulator, however one colleague has.
I ran the app using XCode on his phone and got the following in the logs:
2013-03-01 14:43:39.604 appname[318:907] <MFMailComposeRemoteViewController: 0x1ebfb100> timed out waiting for fence barrier from com.apple.MailCompositionService
2013-03-01 14:43:39.631 appname[318:907] viewServiceDidTerminateWithError: Error Domain=XPCObjectsErrorDomain Code=2 "The operation couldn’t be completed. (XPCObjectsErrorDomain error 2.)"
I googled the error "timed out waiting for fence barrier from com.apple.MailCompositionService" but can't really find any help.
Does anybody have any experience with this? How can I solve it?
My code for opening the view:
-(void)displayComposerSheetWithBodyString:(NSString *)aBody
{
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc]init];
picker.mailComposeDelegate = self;
[picker setSubject:#"Lista"];
NSString *emailBody = aBody;
[picker setMessageBody:emailBody isHTML:NO];
[self.navigationController presentModalViewController:picker animated:YES];
}
else
{
[[[UIAlertView alloc]initWithTitle:NSLocalizedString(#"Din enhet är inte redo att skicka e-post. Kontrollera dina inställningar", nil)
message:nil
delegate:self
cancelButtonTitle:NSLocalizedString(#"OK", nil)
otherButtonTitles:nil]
show];
}
}
Not sure if you have fixed the problem, but I have met it recently in my project.
A workaround I did was to allocate and initiate MFMailComposeViewController in an earlier stage, and hold it in one static variable, whenever it's needed, get the static MFMailComposeViewController instance and present it.
It seems working for me, hope it will work for you, too.
a had the same issue, and this fixe helped me:
https://twitter.com/christian_beer/statuses/321295533077565440
"#nathangaskin well… that was long ago :) But if I remember correctly, it worked after I removed the custom fonts from my UIAppearance code"
It works fine for me.
Also, second option is to simply wrap displaying call into
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
block
I have exactly the same issue. I think I have nailed it down to the time it takes to formulate the message body string.
Content from comment
//Message Body
NSString *msgBody = [NSString stringWithFormat:
#"I noticed these results in pocketKPI. The %# was at %# which is a variance of %#(or %#) to the target defined as %#. When you have some time let's discuss.",
self.itemToView.kpiName,
[DFSKpiFormatter formatNumberAsString:self.itemToView.currentValue], [self.itemToView determineVarianceLabelText],
[self.itemToView determineVariancePercentLabelText],
[DFSKpiFormatter formatNumberAsString:self.itemToView.targetValue]];

Resources