Play data while downloading - macos

I am trying do play songs from soundcloud, which is working fine for one exception: when the response handler is called, the download of the file is allready complete. I'd like to start playing the file directly after the download started, but i have no clue how to access the data before the response handler gets called. Accessing the data ln the progress handler would be nice, but i need a hint on how to do it.

If you write your own download code using NSURLConnection and
initWithRequest:delegate:startImmediately:, the delegate methods (e.g.
connection:didReceiveData:) will be called as the data becomes available.
You risk running out of data if you try to play the sound as it is downloaded. You should probably implement a buffering system where you download enough extra data for several seconds of playback before you start playing. That way you can smooth over short "stutters" in the download.

I suggest you at least two options to perform what you want:
1. AVPlayer supports playing a file from http. If you download a static file - you can just ask a player to stream over http.
2. You can download a small chunks of file (5 MB at once, for example) and append them to the result file or write directly into memory buffer. You can download a file chunk of specified size by just adding a Content-Range header with an offset you need. (see RFC, 14.16 Content-Range for more specific info). This method requires server to support partial downloads, but in nowadays it is harder to find a sever that does not support this =) Alamofire easily allows you to do that.

Related

Issues when downloading a file with a service worker and ReadableStream

I did some testing and I have some problems with streaming a file meant to be downloaded (with Content-Disposition header set to attachment in the response). The following behaviour is common to both Firefox et Chrome and I'll call 'browser' the entity that executes code that is not code written by the user.
The stream starts being consumed straight away when the response is sent back with event.respondWith(), meaning that while the popup appears to ask the user if he wants and where to download the file or if he simply wants to refuse the download, the stream is being consumed and pulling data non-stop. This seems like a crazy behavior, is it intended, or a bug in both browsers?
If the user refuses the download or accept and cancel it later, the browser just stops consuming the stream and that's it. It never calls cancel() on its ReadableStreamDefaultReader instance, nor does it call releaseLock(). So the stream just pull data from the underlying source until the queue is full and wait there without knowing anything. How are we supposed to deal with it?
From the Streams specification (https://streams.spec.whatwg.org/#rs-cancel):
The cancel method cancels the stream, signaling a loss of interest in
the stream by a consumer. The supplied reason argument will be given
to the underlying source’s cancel() method, which might or might not
use it.
So I think the browser should call cancel on its ReadableStreamDefaultReader instance but neither Firefox nor Chrome does.
Moreover, if I call the error method on the stream's underlying ReadableStreamDefaultController instance, the lock is still never released by the browser.

Receive file via websocket and save/write to local folder

Our application is entirely built on websockets. We don't do any HTTP request-reply. However, we are stuck with file download. If i receive file content via websockets can I wrote to local folder on user computer ?
If it makes a difference, we are only supporting Chrome so not issue if it doesn't work on other browsers.
Also, I know i can do this via HTTP. Trying to avoid it and stick to websockets since thats how the entire app is.
Thanks a lot in advance!
The solution depends on size of your file.
If size is less than about 50 MB, I would encode file's content to base64 string on the server and send this string to the client. Client should receive parts of the string, concat them to single result, and store. After receiving whole string, add link (tag <a>) to your page with attribute href set to "data:<data_type>;base64,<base64_encoded_file_content>". <data_type> is a mime type of your file, for example "text/html" or "image/png". Suggest file name by adding download attribute set to name of file (doesn't work for Chrome on OS X).
Unfortunately I have no solution for large files. Currently there is only FileEntry API that allows to write files with JS, but according to documentation it is supported only by Chrome v13+, learn more here https://developer.mozilla.org/en-US/docs/Web/API/FileEntry.

BackgroundTransferService: what are some of the details of its operation?

I kick off 5 uploads (of varying size) via the BackgroundTransferService. I have the following questions about the way it works:
It seems that on the emulator it does 2 uploads at a time. Is this how it works on the actual device? Can I programmatically change this behavior?
Can I count on the uploads going out in the order that I submitted them? I seem to be getting conflicting results in my testing.
When I inspect the BackgroundTransferService in my application, does it contain requests from other apps as well or just mine?
Do I need to reconnect events for all the BackgroundTransferRequest objects when coming back from being tombstoned? What about coming back from being reactivated?
Do I need to disconnect events from the BackgroundTransferRequest when I remove it from the BackgroundTransferService.Requests collection?
When I try to upload a non-existing URL:Port (on the localhost), the TransferStatus is reported as WaitingForNonVoiceBlockingNetwork. The upload never actually completes/fails. Is this how it is on the device? Should I remove the request when it encounters this TransferStatus?
You cannot influence the behaviour of the BTS. If you don't like the way it works you can write the transfer functionality as part of your own application but then you have to handle running in the background yourself.
There is no guarantee on sequence.
The BTS may be handling requests from other apps but you won't be able to see the details. Requests() will only return details for your app.
Surely a quick test will tell you this.
It's good practice to.
Have you checked the TransferError property whe you reach this situation? This is a perfectly valild status in other situations and so you shouldn't treat this as a automatic fail.

[OSX Core Foundation]How can I asynchronously upload a file though HTTP and get a callback called while sending bytes of the stream?

on MacOSX, with Core Foundation, I want to upload a large file (several hundreds of megabytes) to a remote server through a REST API.
Since the file is big and I also need to give the user some feedback, I want to implement a resume upload feature and gives the user feedback on the number of bytes written.
I first followed the Apple Guide for CFNetwork programming: http://developer.apple.com/library/ios/#documentation/Networking/Conceptual/CFNetwork/CFFTPTasks/CFFTPTasks.html#//apple_ref/doc/uid/TP30001132-CH9-SW1
But the asynchronous upload of file is for FTP only.
I tried to use CFReadStreamCreateForHTTPRequest butI only got callbacks on response.
I tried with CFReadStreamCreateForHTTPStreamedRequest and I set a delegate on the ReadStreamRef body parameter but it is never called even though I open the stream before actually scheduling it on the runloop.
If somebody has some tips about how to do it, it would be great.
Thanks a lot!
--
Rémy
I got an answer here: http://lists.apple.com/archives/macnetworkprog/2010/Dec/msg00000.html.
CFReadStreamCreateForHTTPStreamedRequest is the good function to use.
For upload feedback, I use a timer scheduled on the runloop when creating the request:
CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 10.0, 0, 0, ...);
For resume, there are two steps.
Seek the local content stream at the good offset
Once the local content stream created (but not yet opened), I can seek in it using
CFReadStreamSetProperty(content_stream, kCFStreamPropertyFileCurrentOffset, uploaded_length);
Configure http headers
I don't have webdav style remote server, so I use HTTP Range headers to inform the server about which part of the file I want to upload. This step depends on what the remote server expects.
CFHTTPMessageSetHeaderFieldValue(request_headers, CFSTR("Range"), content_range_value);
Hope this will help.

How can I implement a content converter in Firefox for all page elements?

I'm attempting to port over an Internet Explorer plugin to Firefox, but I'm not sure where to look for what I need.
Basically I need to be able to filter all content that is received by the browser with a certain Content-Type header. I tried implementing a stream converter, and this works, but only for the top-level document in the page, frame, or iframe. I had the same problem with IE, and getting around it was really hacky, and since I would ideally like this to be cross platform I would really like to be able to do this in Firefox without resorting to vtable hacks.
The content is served compressed with a proprietary compression format. So I need to receive the data, decompress it, and change the Content-Type back to what the original uncompressed file should have.
If there is a way to just filter all data received, that would probably be acceptable, I could handle parsing the header myself.
Thanks
I think I may have found what I needed. I came across this link which is used for tracing HTTP calls: http://blues.ath.cx/firekeeper/resources/http_tracer.html
There seems to be some problems with the JavaScript implementation for some reason, and I'm not a JavaScript guru to figure it out, but I've implemented it in C++ and initial results suggest that I should be able to modify it for my needs.
Basically we're replacing the nsIHttpProtocolHandler service with our own implementation, which keeps a reference to the initial implementation. When a call is made to the service, we just proxy it over to the saved original implementation. Then we provide our own implementation of nsIHttpChannel and nsIStreamListener which we use as proxies too.
Again we proxy most of the calls back off to the original handlers. But in OnDataAvailable, instead of passing the data on to the underlying nsIStreamListener, we save it using nsIStorageStream. Then in OnStopRequest, after we've gotten all of the data, we can decompress it and then call OnDataAvailable on the original handler, followed by OnStopRequest.
It has worked on some small simple tests so far, but I'll have to put it through some more rigorous tests... I'll also have to figure out if I can do the same thing with HTTPS.
The biggest problem I see at the moment is that it relies on some unfrozen interfaces such as nsIHttpChannelInternal. Can't be helped though as far as I can tell, and my version compatibility requirements are pretty small, so I can live with it if I have to.
In the meantime, if anybody has any other suggestions, I'm all ears :D

Resources