playing a WAV file on iOS Safari - ios

I'm struggling to get a WAV file to play on a HTML page as either an HTML5 audio tag or via a regular downloadable link in mobile Safari (iPad/iPhone):
<audio controls src="audio-pcm_s16le-8k.wav"/></audio>
audio
The HTML5 audio object just shows Cannot play audio file in it, and when I click to download the anchor referenced one I get a black page with a "can't play this circle" in the middle. I've tried various frequencies (8000, 11025, 16000, 32000, 44100), various encodings (mu-law, Linear Signed 16-bit LE and BE), various containers (.wav, .caf, .aiff), and various audio conversion programs (Audacity, ffmpeg, and Apple's own afconvert)... I can't get audio to play (unless I make it MP3 -- and no, I can't just use MP3 or AAC, I need a "raw" format for reasons too long to get in to here).
I looked at the supported formats for iOS and it appears to support WAV... anyone got any experience with this issue? I'm on latest iOS 6.0.1
EDIT: The selected answerer got me to the issue, but the reason is in the comments of the answer. Bottom line is it requires HTML range headers for playing the files.

Well, you've certainly gotten me on the right track at least... it's apparently not the file at all... it's the web server. Same exact content from stockley works, but on my web server it doesn't. I'll have to look over the HTTP response in detail I suppose. My web server is returning Content-Type: audio/x-wav so it's not that...
Checking the headers for the test site I set up, you want to be returning Content-Type: text/html for the page containing the HTML data you need:
And Content-Type: audio/x-wav for the actual audio file (http://www.test.com/file.wav)

Through a run of trial and error I found that you need a content-range header for it to work. Here's an example of my headers that allows a wav file to play in Safari for iOS:
Content-Range: bytes XX-XX/XX
Content-Type: audio/wav
Content-Disposition: attachment; filename="whatever.WAV"
Content-Length: XX
Hope this helps!

It works great. I used Content-Range: bytes XX-XX/XX and Content-Length: XX

Related

Does Safari on iOS refuse to download `application/octet-stream` content?

I have a Web server written on top of your standard http module in Node.js. It's been working like a clock for me for serving all kinds of HTML, JavaScript, CSS, and asset content -- making up the bulk of my web pages, naturally.
When the service is requested a URL that maps to a file with an unknown extension (there is no MIME sniffing going on), it simply serves content using chunked transfer encoding with the header Content-Type: application/octet-stream, which has been working wonderfully as well -- I occasionally host files of all kinds on the server and am able to download these without any interference from the user agent, as I'd expect from application/octet-stream handling.
Today, however, I tried to download on a Safari mobile browser running on iOS 9.3.5 on an iPhone 4S, and that thing just plain refused to download from a valid URL, alerting me with:
Download failed. Safari cannot download this file.
I've since tried to add Content-Disposition: attachment and also Content-Disposition: attachment; filename="foobar" for good measure, but it still refuses.
This has been the case with a file called random which is filled with 1000 random bytes. My host headers are (Content-Disposition is optional, as said above):
HTTP/1.1 200 OK
Host: example.com
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="foobar"
Date: Mon, 01 May 2017 13:48:37 GMT
Connection: keep-alive
Transfer-Encoding: chunked
For the record, Android 6.x phone also behaves as expected (in addition to Firefox 53 on my Windows 10 x86_64 host), downloading from the URL without a hitch.
What's going on with Safari here?
Yes, the only thing you can download with Safari on iOS is image. This is because iOS does not expose file structure to user (download file to somewhere user can never see does not make any sense), it has nothing to do with anti web attack. Browser on Android can download arbitrary files because Android file structure is visible to user.
Here is a previous discussion on Apple community: https://discussions.apple.com/thread/3697948?start=0&tstart=0
The ONLY thing you can save to an iPhone, from Safari, is a pic. Nothing else, and no downloads.
It is possible that iOS supported video files can be downloaded in future, but it is unlikely for arbitrary files with application/octet-stream MIME type.
After I have spent so much time trying to find the solution, I came with this solution:
Replace the octet-stream with pdf.
<?php
$file = 'data/report/foobar.pdf';
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: '.filesize($file));
readfile($file);
?>

libffmpeg: writing an RTSP stream to an output file

I'm working with libffmpeg in an iOS app. My goal is to connect to an RTSP source and write the media out to a file that can later be used with the iOS media player. Ideally I'd like to do this without transcoding the incoming data. I also want to be able to later re-encode the media with AVAssetExportSession if the user chooses to do so.
Because I want to create a file that is compatible with iOS, I'm limited (I believe) to mpeg, mp4 or quicktime (mov) formats.
Whenever I try to use one of these formats, I see the following warnings during my call to avformat_write_header:
[mov # 0x16401c00] Codec for stream 0 does not use global headers but container format requires global headers
[mov # 0x16401c00] Codec for stream 1 does not use global headers but container format requires global headers
My understanding is that the header wants to know the ultimate file size, which I do not know (the RTSP server is live streaming a camera, and the user stops the recording whenever they want). I guess that makes sense, but I know that others have successfully done this using the ffmpeg command line, so I'm confused as to what else I need to do here.
If I ignore the warning, I can still proceed with writing the file. If I choose mpeg or mp4 formats, my app crashes when I call av_write_trailer. If I use mov, I can successfully close the file, and the file does play back, but usually fails when I try to hand it to the AVAssetExportSession.
I would appreciate any insight into this. Thanks.
Frank
I found what appears to be a solution -- at least, it eliminates the warning. I had to set the CODEC_FLAG_GLOBAL_HEADER on both the audio and video codecs, before calling avcodec_open2.

Force MP3 link to download instead of stream but keep HTML5 audio

So apparently the way to force an MP3 to download instead of play in the browser is to set the MIME type as file and/or set the Content-Disposition response header in the .htaccess.
What is the difference between these two methods and is it better to use one or the other, or both?
Also, will doing either of these break HTML5's handling of the <audio> tag when using an MP3 file as the source?
1. Use headers correctly
This is a very widespread problem and unfortunately even the PHP manual is plagued with errors. Developers usually say “this works for me” and they copy stuff they don’t fully understand.
First of all, I notice the use of headers like Content-Description and Content-Transfer-Encoding. There is no such thing in HTTP. Don’t believe me? Have a look at RFC2616, they specifically state “HTTP, unlike MIME, does not use Content-Transfer-Encoding, and does use Transfer-Encoding and Content-Encoding“. You may add those headers if you want, but they do absolutely nothing. Sadly, this wrong example is present even in the PHP manual.
Second, regarding the MIME-type, I often see things like Content-Type: application/force-download. There’s no such thing and Content-Type: application/octet-stream (RFC1521) would work just as fine (or maybe application/x-msdownload if it’s an exe/dll). If you’re thinking about Internet Explorer, it’s even better to specify it clearly rather than force it to “sniff” the content. See MIME Type Detection in Internet Explorer for details.
Even worse, I see these kinds of statements:
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
The author must have been really frustrated and added three Content-Type headers. The only problem is, as specified in the header() manual entry, “The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace“. So unless you specify header("Content-Type: some-value", FALSE), the new Content-Type header will replace the old one.
2. Forcing download and Internet Explorer bugs
What would it be like to not having to worry about old versions of Internet Explorer? A better world, that’s for sure.
To force a file to download, the correct way is:
header("Content-Disposition: attachment; filename=\"$file_name\"");
Note: the quotes in the filename are required in case the file may contain spaces.
The code above will fail in IE6 unless the following are added:
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
Now, the use of Cache-Control is wrong in this case, especially to both values set to zero, according to Microsoft, but it works in IE6 and IE7 and later ignores it so no harm done.
If you still get strange results when downloading (especially in IE), make sure that the PHP output compression is disabled, as well as any server compression (sometimes the server inadvertently applies compression on the output produced by the PHP script).
Look at this,
.mp3 audio/mpeg3
.mp3 audio/x-mpeg-3
.mp3 video/mpeg
.mp3 video/x-mpeg
See this link for more info.
Using Content-Disposition: attachment... forces a download box to appear instead of having to right click -> save target as.

How iOS HTML5 video player detects a file type?

Now i am implementing a video playback on my site and for mobile devices i use HTML 5 video player:
<video src="/get_video.php/myfile.mov" controls width="400" height="250"></video>
In src attribute stored url to php file which output a contents of video file. MIME-type of file i don't know(it's dynamic) so i send content type header - application/octet-stream.
And my iPhone cant play this video: screenshot
So Question: How to force the player to play videos?
Thanks.
First of all, I recommend reading these resources on Video tags:
http://www.w3schools.com/tags/tag_video.asp
http://www.w3schools.com/tags/att_video_src.asp
https://developer.mozilla.org/en-US/docs/HTML/Element/video
As I understand, you have to specify in your MIME type a real video type (vs application/octet-stream). You mentioned that it's dynamic, however on the server side you know which file are you reading (so, you can figure out file format).
Supported types are:
video/mp4
video/webm
video/ogg

Embedded HTML 5 video is stored in the cache but is not displayed on iPad 2

I am creating an offline webapp for the iPad 2, which includes video content. When the page first loads, the video displays fine. But when I reload the page, the video's play button becomes broken.
I've gone into Settings > Safari > Advanced > Website Data and, sure enough, the video is in the cache... So the problem seems to be that it is not being retrieved from the cache.
My HTML code snippet:
<html manifest="cache.manifest">
...
<video width="320" height="240" controls="controls">
<source src="videos/movie.mp4" type="video/mp4" />
</video>
My cache.manifest snippet:
CACHE MANIFEST
# Updated 2012-08-22 19:49:00
index.php
...
videos/movie.mp4
For good measure, my .htaccess snippet:
AddType text/cache-manifest .manifest
AddType video/mp4 .mp4
Does anyone have any ideas?
This unfortunately isn't an answer but after now 20 hours continuous searching and testing to resolve the exact same problem i can tell you where i am now.
This appears to be an ipad iOS specific problem where no matter what size the video / sound file is it will not draw on the cached files although they clearly are cached and on first load it plays the file OK.
I have tried making the smallest video possible.
I have looked at wrapping in a native app but that's not an option for delivery reasons.
I have tried forcing a reload of the video .src on page load using javascript.
I have tried all possible variations of the manifest file.
Looked at all the Apple developer docs i can stomach.
After reading hundreds of posts, that never actually complete, i think the answer, other than getting the client to buy Android tablets, is to use the local database to store the video in binary form to be retrieved when needed by the app. Unfortunately i am still searching for examples of this and as yet cant find any with any detail. Local saving of text / numerical data isn't a problem. I just dont know if its possible to store the raw file data and retrieve it in a local database.
Sorry its not what you were after but hope it helps point you in less directions.
An update but not much progress. I decided to use base64 ecoded mp4 and paste the text in a simple xml file. My app would read this xml video data and by using in the video tag SRC. This was about a 4MB string.
SRC="data:video/mp4;base64,AAAAA /...../ AA"
This worked fine in Chrome. When i used it on the Ipad the good points are that i didnt ever get the play button crossed out and it tried to play then flashed a message it could not complete this operation.
I had a somewhat related issue with playing video on an iPad. This was in an HTML widget that will reside in an iBooks file. My problem was I couldn't get the videos to rewind, so when you went back to that screen the video was stuck at the end (or still playing if you went back fast enough.)
The workaround I came up with was to load a different video and then reload the video I wanted to play. It's ugly, but it works, and it may provide a workaround for your problem.
var sources = videoEl.getElementsByTagName('source');
sources[0].src = "assets/TeethMouth_Anim_Part3_03.mp4"; // Load some other video into the source, in my case, a video that I'm playing later in the presentation.
videoEl.load();
sources[0].src = "assets/TeethMouth_Anim_Part1_03.mp4"; // Then reload the video I want to play.
videoEl.load();
Although now I see that this thread is a year old so it's probably not an issue anymore. Still, thought I'd post it.

Resources