'How to detect if a video file is supported by the HTML5 video tag?

I have a File object in JavaScript of a file in the user's machine and I want link to it in a video tag and play it. The problem is that I need to detect first if the browser supports the codec of the file I have, how do I do that? I know I can detect what codecs the browser supports with the canPlayType() function, but how can I detect the codec of the file I have?



Solution 1:[1]

I've looked into it more, and canPlayType() can also contain the codec, so you can try:

canPlayType('video/mp4; codecs="avc1.42E01E"');

and

canPlayType('video/mp4; codecs="mp4a.40.2"');

separately for example

Solution 2:[2]

"I have a File object in JavaScript of a file in the user's machine... I know I can detect what codecs the browser supports with the canPlayType() function, but how can I detect the codec of the file I have?"

(1) If codec is known... Use canPlayType to check codec compatibility.

var vid = document.getElementById("myVideoElementID");

//# for AVC1 codecs like avc1.42E01E
if( vid.canPlayType('video/mp4; codecs="avc1"') )
{ alert("can play AVC1 / H.264 "); }

(2) If codec is unknown... Check file bytes for codec type (then do step (1) to confirm playability)

This means getting a chunk of the file's bytes (for example getting the first 64kb or last 64kb, depends if MP4 header is at front or back of file).

Read the data into an Array and then search that Array for the stsd section, which is where MP4 keeps the Sequence Parameter Set (SPS) of the H264 data, from that section you can get extract the codec info.

a) Find stsd position.
b) From that position, skip +16 bytes to find either avc1 or hev1.
c) From the +16 pos, skip forward from here +91 to get the SPS (3 bytes).

Here is an example using a selected (local) file which is read into an Array via FileReader API.

<!DOCTYPE html>
<html>
<body>

<!-- button Choose MP4 Video -->
<div style="z-index: 1; overflow:hidden; position: absolute; top: 18px; left: 10px; " >
<text> <b> Choose a Video (.MP4) file...</b> <br> 
<input type="file" id="choose_video" accept=".mp4" />
</div>

<video id="myVideo" width="640" height="480" controls  style="position: absolute; top: 80px; left: 10px; " >
<source src="" type="video/mp4">
</video>

</body>

<script>

var temp_int = 0; var temp_str = ""; var temp_arr = [];

var reader; var path;
var fileBytes;  //# is updated to: uint8Array []
var file; //# a File object (using Reader) 

//# codec name (is String object)
var str_codec = "-1";

var myvid = document.getElementById( 'myVideo' );

addEventListener("load", on_page_Ready );

function on_page_Ready()
{
    //# listener for selecting MP4 file
    document.getElementById('choose_video').addEventListener('change', onSelectFile, false);
}

function onSelectFile(evt) 
{
    file = evt.target.files[0]; //# FileList object
    path = (window.URL || window.webkitURL).createObjectURL(file);
    
    reader = new FileReader();
    reader.readAsArrayBuffer(file);
        
    reader.onload = function(evt)
    {
        if (evt.target.readyState == FileReader.DONE) 
        {
            fileBytes = new Uint8Array( evt.target.result );
            
            //# extract codec info
            get_MP4_Codec( fileBytes ); 
            
            //# load video data and play ...
            myvid.setAttribute("src", path);
            myvid.load();
            
            myvid.play();
        }
    }
}

function get_MP4_Codec (inBA)
{
    let idx = 0; //# index (position) in bytes
    
    while(true)
    {
        //# auto stop if "stsd" not found after 32 kilobytes
        if ( (idx > 32000) || (idx >= inBA.length-1) ) { break; }
        
        //# look for starting "s" (is byte value 0x73)
        if( inBA[ idx ] == 0x73 ) //# find "s"
        {
            //# check if next following 3 bytes are the required values
            if( (inBA[ idx+1 ] == 0x74) &&  //# find "t"
                (inBA[ idx+2 ] == 0x73) &&  //# find "s"
                (inBA[ idx+3 ] == 0x64)     //# find "d"
            )
            {
                //# when found... 
                
                //# note the "stsd" position
                temp_int = idx; 
                
                //# skip forward by 16 bytes to get codec type
                //# codec type can be "avc1" or "hev1" or "hvc1"
                
                idx += 16;
                str_codec =  String.fromCharCode( inBA[ idx+0 ] );
                str_codec += String.fromCharCode( inBA[ idx+1 ] );
                str_codec += String.fromCharCode( inBA[ idx+2 ] );
                str_codec += String.fromCharCode( inBA[ idx+3 ] );
                
                //# need that dot "." also
                str_codec += ".";
                
                //# skip forward by 91 bytes to get codec SPS details
                //# example"avc1.64001f" the SPS is "64001F"
                idx += 91;
                
                temp_str = (inBA[idx].toString(16)).toUpperCase();
                str_codec += temp_str.length === 2 ? temp_str : '0' + temp_str;
                
                idx += 1;
                temp_str = (inBA[idx].toString(16)).toUpperCase();
                str_codec += temp_str.length === 2 ? temp_str : '0' + temp_str;
                
                idx += 1;
                temp_str = (inBA[idx].toString(16)).toUpperCase();
                str_codec += temp_str.length === 2 ? temp_str : '0' + temp_str;
                
                break;
                
            }
            
        }
        
        idx++;
    }
    
    alert("found STSD @ byte pos : " + temp_int +"\n"+ "codec type : " + str_codec );

}

</script>

</html>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ian Devlin
Solution 2 VC.One