Capturing camera/picture data without PhoneGap - An Update

This post is more than 2 years old.

Over two years ago I wrote an article about using the device camera on the web without using PhoneGap (Capturing camera/picture data without PhoneGap). While an old blog entry, it is easily one of the most popular entries on this blog in terms of how much traffic it gets. It is also a great reminder that most of us web developers probably forget just how powerful the platform is. The original article discusses the use of the capture attribute in input fields to allow for camera access. The idea was simple - take a regular input/type=file and add the capture attribute:


<input type="file" capture="camera" accept="image/*" id="takePictureField">

When used, the browser would then prompt you to select a picture or take a new one with your camera. You can see the original demo below, and my god, iOS has really come a long way since then.

I thought it would be interesting to take a look at this feature and see if it has changed any in the past two years.

I began by checking into the official spec. This proved a bit difficult. While there is a spec for Media Capture and Streams, I was more interested in the particulars of the syntax in regards to the input tag.

I went to MozDevNet's input page and it had this to say about the capture attribute:

When the value of the type attribute is file, the presence of this Boolean attribute indicates that capture of media directly from the device's environment using a media capture mechanism is preferred.

Note - "this Boolean". When I last worked with this code it wasn't a boolean but rather a value specifying camera.

To make things even more interesting, this older article from 2013, Capturing Audio & Video in HTML5 used the accept attribute instead:


<input type="file" accept="image/*;capture=camera">

Back on the MDN page, if you look at the accept attribute, nothing about this is mentioned. However, their docs on capture link to a spec that mentions:

If the accept attribute's value is set to a MIME type that has no *associated capture control type*, the user agent must act as if there was no capture attribute.

Notice the "associated capture control type"? So that seems to imply that - officially - you should use capture="true" along with the accept attribute which may or may not specify a particular capture type or may stay nice and vague, i.e. accept="video/*".

Clear as mud, right? Ok, so let's play with this a bit. I built a demo that tried to hit a few variations of this:


<!DOCTYPE html>
<html>
<head>
	<meta name="viewport" content="width=device-width">
	<title>Capture Tests</title>
</head>
<body>

<h1>test 1 (capture attr)</h1>
<input type="file" capture="camera" accept="image/*">

<h1>test 2 (capture in acc)</h1>
<input type="file" accept="image/*;capture=camera">

<h1>test 3 (capture bool)</h1>
<input type="file" accept="image/*" capture>

<h1>test 4 (capture bool, video)</h1>
<input type="file" accept="video/*" capture>

</body>
</html>

You can see four different input fields. The first uses the capture attribute, while the second uses it as part of the accept attribute and the final two use them both, as I believe the spec dictates. Also note the last is asking for video. So let's see what happens.

Mobile Safari

The first and second buttons behave the same way. Consider the screen shot below.

Notice the "Take Photo" and "Photo Library" options allowing for both a new and old picture. Notice Dropbox. I only see that because I clicked More:

I was a bit surprised that Google Photos didn't show up here, but maybe they need to do something to let iOS know they are a "provider" for images.

While iOS prompted for a picture, I was able to go into the photo library and select a video. I was also able to go into Dropbox and select a file that wasn't media at all. This is not a bug and I would hope that if you are building an app that accepts any type of upload that you always verify on the server that the right type was sent!

Here's where things get interesting. My third test, which asks for an image but doesn't specify camera anywhere, had this result:

As you can see, now it's asking for either a picture or video. Finally, the fourth option - and it's what you expect - prompting for a video:

Mobile Chrome

Mobile Chrome does things a bit differently. The first input opens up the device camera. The second prompts you for an 'action':

Camera and Camcorder act as you expect. Documents let me browse my tablet, and notice Dropbox shows up here too.

And just like iOS, I can go into Dropbox and select something that isn't an image or video.

Input #3 acts just like Input #1 - it opens the camera. I honestly can't tell you why.

Finally, input #4 also opens up the device camera, but sets it in video mode automatically.

Desktop Chrome

So for the heck of it, I decided to try a few desktop browers as well. Here is what I saw.

Like Mobile Chrome (and it shouldn't be surprising), input #1 and #3 act the same. The open a file selection prompt that defaults to Images:

And notice how you can simply change Format and select non-image files. Input #2 acted as if I didn't want an image:

Finally, option 4 prompted for video files:

Desktop Firefox

Firefox acted the exact same way as Chrome. Inputs #1 and #3 selected a format of Image Files, #2 wasn't filtered at all, and #4 set a format of "Video Files".

So at this point - I tried a fifth test. You won't see it in the earlier screen shots, but here it is:


>input type="file" accept="image/*;capture=camera" capture<

This "felt" like it should match the spec best, but both Firefox and Chrome treated it like no particular filter was active. Mobile Chrome prompted for Camera/Camcorder again. Mobile Safari did the prompt thing again, but asked for either a photo or video.

Desktop Safari

Safari acted a bit like Chrome/Firefox, but the UI doesn't tell you it's filtering. Instead, it grays out the non-desired options. Like Chrome/Firefox, inputs #1 and #3 acted the same:

As you can see, only images are available and as an end user, I can't change the option at all. (But of course, don't sit there thinking to yourself that this means you're "protected" from non-image uploads. I can go into Safari's dev tools and modify the code.)

Input #4 acted the same - only my videos were selectable. Finally, the new fifth option acted like Chrome/Firefox - no filters were in place.

Desktop Edge

Microsoft Edge doesn't seem to recognize any of the options. Of course, it "breaks" perfectly well and still lets me select a file to upload, and since I'd have server-side protections in place, I'm certainly not "broken" in Edge at all.

Wrap Up

So on mobile, it looks like this feature still works rather well, with the biggest difference being that Mobile Chrome lets you go right into the camera whereas Mobile Safari is always going to prompt. Safari's prompts seem easy enough so I don't see this as an issue, but obviously user testing would let you know.

If you want to use my test page yourself, you can find it on Surge at: http://regular-rod.surge.sh

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Gary F posted on 6/8/2016 at 1:39 AM

Interesting experimentation. Saves me from working it out. Thanks Ray. :-)

Comment 2 (In reply to #1) by Raymond Camden posted on 6/8/2016 at 1:55 AM

You are most welcome.

Comment 3 by Rob Caldecott posted on 9/1/2016 at 12:56 PM

Very useful. One question: when I record a video on iOS using HTML <inout> where are they stored? Once recorded there is no way to get back to the video if you wanted to, for example, let the user try and select it. Unless you do something with the file when the user taps "Use Video" then it's gone. Is there any way to use the File object and copy the video to the user's photo library?

Comment 4 (In reply to #3) by Raymond Camden posted on 9/1/2016 at 2:18 PM

You absolutely cannot write to the user's library. Web apps can't do that. However, I'm surprised you are seeing the video *not* being stored in the gallery. Are you sure?

Comment 5 (In reply to #4) by Raymond Camden posted on 9/1/2016 at 2:19 PM

Interesting. You are right, it is *not* saved. Maybe the idea is that iOS thinks this is a temp video.

Comment 6 (In reply to #5) by Raymond Camden posted on 9/1/2016 at 2:24 PM

So if you inspect the value, you see:

c:\fakepath\capturedvideo.MOV

The best I can suggest is text on the web page warning the user that their video won't be saved.

Comment 7 (In reply to #6) by Raymond Camden posted on 9/1/2016 at 2:26 PM

As just an FYI, on Mobile Android, the video is saved and available.

Comment 8 by prospecttoreza posted on 12/19/2016 at 10:11 PM

Hi Raymond. Thank you for the informative article.
I need to send the image back to my iis server. Is there a simple way to do it?
Thank you.

Comment 9 (In reply to #8) by Raymond Camden posted on 12/20/2016 at 2:42 AM

It is a regular form field/type=file, so you can POST it to your server. If you are ok using XHR2, you can do a file upload via Ajax as well.

Comment 10 by Romky posted on 1/3/2017 at 4:32 PM

Happy new year, Raymond!
I'm thinking about this as a solution for mobile / iOS devices in combo with the getUserMedia spec for desktop devices in order to record a video.
I hope, this thought makes sense...

Imho, your browser test is missing the desktop version of Safari. Or is that outcome predictable?

Comment 11 (In reply to #10) by Romky posted on 1/3/2017 at 4:34 PM

Omg... I should have read the article until the very end... I'm sorry for that question at the end of my previous post...

Comment 12 (In reply to #11) by Raymond Camden posted on 1/3/2017 at 4:44 PM

No worries. :)

Comment 13 by Antonio Gallo posted on 1/25/2017 at 5:32 PM

This article is wonderfull! As soon as i've free time then i wish to create an ionic2 component that will transparently degrade to the above solution if the camera plugin is not present. This is in order to use a single source for both a Mobile App and a PWA (Progressive Web Application).

Comment 14 (In reply to #13) by Raymond Camden posted on 1/25/2017 at 5:42 PM

Cool. :)

Comment 15 (In reply to #5) by Rob Caldecott posted on 3/3/2017 at 9:01 AM

What's worse is that one of our users is now out of space on their device (they have recorded lots of videos using the <input> mechanism) and there seems to be no way of clearing the data. We have hunted through the various iOS manage storage settings and even clearing all web site data does not fix it. It's as if the device is not removing these temp videos and as they don't show up anywhere we're looking at a factory reset to fix it.

Comment 16 (In reply to #15) by Raymond Camden posted on 3/3/2017 at 5:15 PM

Ouch - let me know if you can reproduce this consistently - a bug should be filed.

Comment 17 (In reply to #15) by Raymond Camden posted on 4/1/2017 at 3:30 PM

I did some testing with the latest iOS. I could confirm available storage went down after recording some long videos. However, on device restart the storage was returned. It's possible what you saw was a bug that has been fixed.

Comment 18 by RobFergusonOrg posted on 4/1/2018 at 11:21 PM

Hey Raymond,

Great post :)

Have you tried your script on a device running iOS 11?

See: https://forum.ionicframewor...

Cheers
Rob

Comment 19 (In reply to #18) by Raymond Camden posted on 4/1/2018 at 11:54 PM

What did you find? I read your post but it seems incomplete - like I’m missing something.

Comment 20 (In reply to #19) by RobFergusonOrg posted on 4/2/2018 at 12:14 AM

Ok, so I'm building a PWA using the Ionic Framework (v3) and I just started working on the Page that allows the user to 'Take a Photo' and/or use an image from their 'Photo Library'.

It works fine on my Android device but not on my iPhone running iOS 11.2.6. The camera will launch but you get a black screen.

See: https://stackoverflow.com/q...

Comment 21 (In reply to #20) by Raymond Camden posted on 4/2/2018 at 1:26 PM

I see you got a fix - have you tried in 11.3?

Comment 22 (In reply to #21) by RobFergusonOrg posted on 4/2/2018 at 11:16 PM

No, its still an issue in 11.2.6 :(

However, I just installed 11.3 and the camera launches and it works :)

I haven't tried the navigator.mediaDevices.getUserMedia method (which returned undefined in 11.2.6):


public ngAfterViewInit() {

const constraints = {
audio: false,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 776, ideal: 720, max: 1080 },
facingMode: 'environment'
}
};

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {

navigator.mediaDevices.getUserMedia(constraints).then(stream => {
this.video.nativeElement.src = window.URL.createObjectURL(stream);
this.stream = stream;
this.video.nativeElement.play();
}).catch(function(err) {
this.logger.error(err.name + ': ' + err.message);
});

} else {
this.logger.error('Your browser does not support the mediaDevices API');
}
}
Comment 23 by Yee Wei Kheoh posted on 6/12/2019 at 2:18 AM

When I do take a picture with my phone camera, is it supposed to trigger the 'onChange' handler in <input type="file"/> ? Cuz I'm trying to display preview images on screen with the 'createObjectURL' right after a picture is taken..

Comment 24 (In reply to #23) by Raymond Camden posted on 6/12/2019 at 4:59 PM

It should work - can you share your code online so I can see?