1

This script is designed to get a video frame from the input and use it as an image. I know this works, I had this code structured originally in a different way but I want to use a return to get the video thumbnail instead but this section of the code

canvas.toBlob(function(blob){
...
}

Makes it very difficult to get the return value. I tried a global variable and still no luck and I even check this link out on here

access blob value outside of canvas.ToBlob() async function

and many different kind of links but I'm still having difficulties understanding how I can implement those answers to my script. This return is returning as undefined so how can I get that generated video thumbnail link as a return value successfully?

This is my code example.

document.addEventListener('DOMContentLoaded', function(){

document.querySelector('input').addEventListener('change', video_thumbnail_grabber__starter);

function video_thumbnail_grabber__starter(){

var this_input = document.querySelector('input').files[0];
var blob = new Blob([this_input],{type: 'video/mp4'});
var object_url = URL.createObjectURL(blob);
var media_link = object_url;

var thumbnail = video_thumbnail_grabber(
                video_source = media_link,
                        time = 61,
                        scale_factor = 0.25
);

document.querySelector('img').setAttribute('src', thumbnail);

}


function video_thumbnail_grabber(
        video_source,
        time,
        scale_factor, 
        for_this_function
){

    var video = document.createElement('video');

    video.addEventListener('loadeddata', function(){
        video.currentTime = time;
    });

    video.addEventListener('seeked', function(){

    //<Best highest quality generation method found>

    if(scale_factor == null){
        
        scale_factor = 1;

    }

    var w = video.videoWidth * scale_factor;
    var h = video.videoHeight * scale_factor;
    var canvas = document.createElement('canvas');
    canvas.width  = w;
    canvas.height = h;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(video, 0, 0, w, h);

    //</Best highest quality generation method found>
      
    //<Edge and IE polyfill for toBlob>

    if(!HTMLCanvasElement.prototype.toBlob){
       Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
         value: function (callback, type, quality) {
           var canvas = this;
           setTimeout(function() {
             var binStr = atob( canvas.toDataURL(type, quality).split(',')[1] ),
             len = binStr.length,
             arr = new Uint8Array(len);

             for (var i = 0; i < len; i++ ) {
                arr[i] = binStr.charCodeAt(i);
             }

             callback( new Blob( [arr], {type: type || 'image/png'} ) );
           });
         }
      });
    }

    //</Edge and IE polyfill for toBlob>

    canvas.toBlob(function(blob){
        
    var video_thumbnail_link = URL.createObjectURL(blob);

    return video_thumbnail_link; //<-This return is not going through.

    });

    });

    video.preload = 'auto';
    video.src = video_source;

}

});
<input type='file'>

<img src=''>
  • You can move `video.src = video_source;` inside the callback of canvas.toBlob – Vinay Oct 02 '22 at 02:34
  • @Vinay Thanks for your response but it did not work I notice any return inside the canvas.toBlob does not let me use the return value outside that section. – Michael Nunez Oct 02 '22 at 03:09
  • I mean did you try like `video.src = video_thumbnail_link`? any return inside that callback won't work because there is a temporal difference between the code inside it and the code outside of it, they don't run at same time therefore they cannot "communicate" as such – Vinay Oct 02 '22 at 03:16
  • Well that won't work because I'm not trying to add the thumbnail as the video source I'm trying to get the different generated video_thumbnail_link process to return I just need to find a way to return it from that section to outside use originally I had to send that link as a parameter to a outside function but I don't want to use that method because its makes my code to complex and it don't work like a return really people online suggest using a promise to return the video_thumbnail link but I don't know how that will look like I played around with promises but no luck for this structure. – Michael Nunez Oct 02 '22 at 03:39
  • I mean I don't know how promises will look like with this code structure. – Michael Nunez Oct 02 '22 at 03:40
  • This link https://stackoverflow.com/questions/42458849/access-blob-value-outside-of-canvas-toblob-async-function gave a good example how to return a value from canvas.toBlob... but I can't figure that method out how to have it work with my code. – Michael Nunez Oct 02 '22 at 03:42
  • " I'm not trying to add the thumbnail as the video source..." now I got it – Vinay Oct 02 '22 at 03:55

1 Answers1

0

I notice some issue in how you have organized the event handler, video_thumbnail_grabber__starter is called only when user is choosing a file but you want thumb image on every seek so sending the thumb image back to video_thumbnail_grabber__starter doesn't make sense. Please have a look at this sample, let me know your comments

<div>

<label for="myfile">Select a file:</label>
<input type="file" id="myfile" name="myfile">


<img style="float: left">

</div>

<script type="text/javascript">

document.addEventListener('DOMContentLoaded', function() {

    document.querySelector('input').addEventListener('change', video_thumbnail_grabber__starter);

    function video_thumbnail_grabber__starter() {
        var this_input = document.querySelector('input').files[0];
        var blob = new Blob([this_input], {
            type: 'video/mp4'
        });
        var object_url = URL.createObjectURL(blob);

        var media_link = object_url;

        var thumbnail = video_thumbnail_grabber(
            video_source = media_link,
            time = 2,
            scale_factor = 0.25,
            for_this_function = 'targeted_function'
        ); //<-- what is the use of this return?
    }


    function video_thumbnail_grabber(
        video_source,
        time,
        scale_factor,
        for_this_function
    ) {
      return new Promise((resolve, reject) => {
                      var video = document.createElement('video');
        video.controls = true

        video.addEventListener('loadeddata', function() {
            video.currentTime = time;
        });

        video.addEventListener('seeked', function() {

            //<Best highest quality generation method found>

            if (scale_factor == null) {

                scale_factor = 1;

            }

            var w = video.videoWidth * scale_factor;
            var h = video.videoHeight * scale_factor;
            var canvas = document.createElement('canvas');
            canvas.width = w;
            canvas.height = h;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(video, 0, 0, w, h);

            //</Best highest quality generation method found>

            //<Edge and IE polyfill for toBlob>

            if (!HTMLCanvasElement.prototype.toBlob) {
                Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
                    value: function(callback, type, quality) {
                        var canvas = this;
                        setTimeout(function() {
                            var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
                                len = binStr.length,
                                arr = new Uint8Array(len);

                            for (var i = 0; i < len; i++) {
                                arr[i] = binStr.charCodeAt(i);
                            }

                            callback(new Blob([arr], {
                                type: type || 'image/png'
                            }));
                        });
                    }
                });
            }

            //</Edge and IE polyfill for toBlob>

            canvas.toBlob(function(blob) {

                var video_thumbnail_link = URL.createObjectURL(blob);

                document.querySelector('img').setAttribute('src', video_thumbnail_link);

            });

        });

        video.preload = 'auto';

        video.src = video_source;

        document.body.appendChild(video)

      })
    }

});
</script>
Vinay
  • 7,442
  • 6
  • 25
  • 48
  • Hey Vinay thanks for your replies I appreciate your responses but I'm not trying to use the document.querySelector('img').setAttribute('src', video_thumbnail_link); in that function I plan to use the video_thumbnail_grabber function as a return value as a video thumbnail link generator only in complex functions that is why I want to use the video_thumbnail_grabber function as a return value – Michael Nunez Oct 02 '22 at 07:06
  • I'm aware I can use document.querySelector('img').setAttribute... but I'm trying to avoid doing that because this script will be use in a app I'm building which is a video editor with multiple complex functions so I just want to find a way where I can only use the video_thumbnail_grabber function only as a return value the document.querySelector('img') part was just a simple example I was showing. – Michael Nunez Oct 02 '22 at 07:11
  • Ok but you have bound video `seek` event inside it so you want the `var thumbnail` to reflect the seeking like if user seeks to some other timestamp in video the contents of thumbnail must change or what? since as of now it can just return a value one time so seek wont have any effect once `video_thumbnail_grabber` has returned – Vinay Oct 02 '22 at 07:21
  • Hey Vinay I liked how you suggested this structure of the use of a promise any ideas how I can return the resolve value of video_thumbnail_link so it can be used outside the video_thumbnail_grabber function as a return? Just curious I was playing around with the promise structure you constructed but this return value stuff is really difficult for me to do I keep looking at other articles to understand how it can be done but it does not fit right with my example and I don't know how it can be structured to my example. – Michael Nunez Oct 02 '22 at 17:29
  • Actually the promise part is a typo which I forgot to remove since it makes no sense as a promise can resolve only once it can give you at most 1 result thumbnail but since you want multiple ( based on user seek on video) it was useless – Vinay Oct 03 '22 at 17:14