MagicMirror Forum
    • Recent
    • Tags
    • Unsolved
    • Solved
    • MagicMirror² Repository
    • Documentation
    • 3rd-Party-Modules
    • Donate
    • Discord
    • Register
    • Login
    A New Chapter for MagicMirror: The Community Takes the Lead
    Read the statement by Michael Teeuw here.

    MMM-RTSPStream Snapshot image resolution appears low, and blurry.

    Scheduled Pinned Locked Moved Unsolved Troubleshooting
    7 Posts 3 Posters 2.2k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S Offline
      sdetweil
      last edited by sdetweil

      i had a similar problem displaying images on my mirror,
      they were zoomed in (clipping all 4 sides(landscape pics) or top and bottom (pics in portrait mode))…

      the dimension ratios of the pic did not match the dimension ratios of the display space (div)…

      so I had to scale them myself…

      i found this routine to calculate the scale of the image, thru searching

      ScaleImage: function(srcwidth, srcheight, targetwidth, targetheight, fLetterBox) {
      
          var result = { width: 0, height: 0, fScaleToTargetWidth: true };
      
          if ((srcwidth <= 0) || (srcheight <= 0) || (targetwidth <= 0) || (targetheight <= 0)) {
              return result;
          }
      
          // scale to the target width
          var scaleX1 = targetwidth;
          var scaleY1 = (srcheight * targetwidth) / srcwidth;
      
          // scale to the target height
          var scaleX2 = (srcwidth * targetheight) / srcheight;
          var scaleY2 = targetheight;
      
          // now figure out which one we should use
          var fScaleOnWidth = (scaleX2 > targetwidth);
          if (fScaleOnWidth) {
              fScaleOnWidth = fLetterBox;
          }
          else {
             fScaleOnWidth = !fLetterBox;
          }
      
          if (fScaleOnWidth) {
              result.width = Math.floor(scaleX1);
              result.height = Math.floor(scaleY1);
              result.fScaleToTargetWidth = true;
          }
          else {
              result.width = Math.floor(scaleX2);
              result.height = Math.floor(scaleY2);
              result.fScaleToTargetWidth = false;
          }
          result.targetleft = Math.floor((targetwidth - result.width) / 2);
          result.targettop = Math.floor((targetheight - result.height) / 2);
      
          return result;
      },
      

      then changed the display code, by using an onload handler

      to eliminate lots of empty display time,

      • only create DIV once,
      • load the image hidden,
      • once completely loaded,
        ** calculate scale size
        ** adjust image display size,
        ** then make visible
      getDom: function() {
          
          // if wrapper div not yet created
          if(this.wrapper ==null)
            // create it
            this.wrapper = document.createElement("div");
      
          // get the size of the margin, if any, we want to be full screen
          var m = window.getComputedStyle(document.body,null).getPropertyValue('margin-top');
          // set the style for the containing div
          Log.log("body size="+document.body.clientWidth+"+"+document.body.clientHeight+" margin="+m);
      //  use style to get fullscreen 
          if(this.config.position==='fullscreen')
            this.wrapper.style.class=this.config.position+".above";
      
        // instead of trying to force it manually
        //	this.wrapper.style.position = "absolute";
        //	this.wrapper.style.left = 0;
        //	this.wrapper.style.top = parseInt(this.wrapper.style.height)+60; //document.body.clientHeight+(parseInt(m))+"px";
        //	this.wrapper.style.opacity = self.config.opacity;
        //	this.wrapper.style.width  = document.body.clientWidth+(parseInt(m)*2)+"px";
        //	this.wrapper.style.height = document.body.clientHeight+(parseInt(m)*2)+"px";
          
          this.wrapper.style.backgroundColor = this.config.backgroundColor;
          
          this.wrapper.style.border = "none";
          this.wrapper.style.margin = "0px";
      			
        //	Log.log("body size="+this.wrapper.style.width+"+"+this.wrapper.style.height+" pos="+this.wrapper.style.top);
      
          var photoImage = this.randomPhoto();
          var img = null;
          if (photoImage) {
      
            // create img tag element
            img = document.createElement("img");
      
            // set default position, corrected in onload handler
            img.style.left = 0+"px";
            img.style.top = document.body.clientHeight+(parseInt(m)*2);  
            img.style.position="relative"; //(to the containing div)
      
            img.src = photoImage.url;
      
            // make invisible
            img.style.opacity = 0;
            // append this image to the div
            this.wrapper.appendChild(img);
      
            // set the onload event handler
            img.onload= function (evt) {
      
              // get the image of the event
              var img = evt.currentTarget;
              Log.log("image loaded="+img.src+" size="+img.width+":"+img.height);
      
                // what's the size of this image and it's parent
                var w = img.width;
              var h = img.height;
              var tw = document.body.clientWidth+(parseInt(this.m)*2);  // use img.parent if not fullscreen
               var th = document.body.clientHeight+(parseInt(this.m)*2);
      
              // compute the new size and offsets
              var result = this.self.ScaleImage(w, h, tw, th, true);
      
              // adjust the image size
              img.width = result.width;
              img.height = result.height;
      
              Log.log("image setting size to "+result.width+":"+result.height);
              Log.log("image setting top to "+result.targetleft+":"+result.targettop);
      
              // adjust the image position
              img.style.left = result.targetleft+"px";
              img.style.top = result.targettop+"px";	
              img.style.opacity =	this.self.config.opacity;
              img.style.transition = "opacity 1.25s";
      
              // if another image was already displayed
              if( this.self.wrapper.firstChild!=this.self.wrapper.lastChild)
              {
                // hide it
                this.self.wrapper.firstChild.style.opacity=0;	
                // remove the image element from the div
                this.self.wrapper.removeChild(this.self.wrapper.firstChild);
              }
      
            }.bind({self: this, m:m});
            
          }
          return this.wrapper;
        },
      
      

      Sam

      How to add modules

      learning how to use browser developers window for css changes

      P 1 Reply Last reply Reply Quote 0
      • S Offline
        shbatm Module Developer
        last edited by shbatm

        Sorry you’re having issues. To give a brief background: showing snapshots instead of video was an afterthought in this module. I created the module because there were modules to show MJPEG streams and to show snapshots, but nothing that could play the video from an RTSP source which is what my cameras put out. I added snapshots in because the original ffmpeg version was a CPU and RAM hog and I needed to show something when I didn’t need to stream the video (sounds like the situation described above). Now fast-forward a year, and I’ve found that all I really need for my main mirror is snapshots. :(

        Anyways, when I added the snapshots to get around some CORS issue I was having, the module grabs the snapshots from the node_helper.js on the “server” end, converts the jpeg to base64 and sends over the socket to the front end/browser, where it draws the image on the canvas (relevant code) trying to make use of the same elements which the original ffmpeg version used to draw the video on the canvas. I am sure this is not the most efficient or best way to render the images correctly which I’m sure is why you’re seeing some quality issues.

        If you guys want to take a look at the code and have a solution, I’d be happy to accept a PR and correct the problem.

        1 Reply Last reply Reply Quote 0
        • P Offline
          peterh @sdetweil
          last edited by

          @sdetweil @shbatm Thanks for the info. i’ll have a look at integrating/fixing it then figuring out how to do a pull request… or i might cheat and just use another module for the snapshot and use MMM-RTSPStream to render over the top using the omxplayer, that way the remote virwer( if any) can always see a higher res snapshot that keeps updating, and the the local viewer gets live video and audio.

          1 Reply Last reply Reply Quote 0
          • S Offline
            shbatm Module Developer
            last edited by

            Some options I’ve thought about or tried but did not fully implement:

            1. node_helper grabs image and saves to disk, front end grabs the image. This was the first method, but I went away from it because it involves (a) repeatedly writing to the SD card and (b) requires two timed actions to stay relatively in sync (front and back end).
            2. Directly accessing the image from camera via the front end. Works for some, and would also allow MJPEG streams to be used, but for certain cameras this gives a CORS violation error. This is what I did in my Octoprint module.
            3. Use an iframe overlay/underlay in the same position as the canvas tag, and then same as #2. Should get around CORS and would be the best option that I know of, just haven’t gone back and implemented.
            S 1 Reply Last reply Reply Quote 0
            • S Offline
              sdetweil @shbatm
              last edited by

              @shbatm said in MMM-RTSPStream Snapshot image resolution appears low, and blurry.:

              requires two timed actions to stay relatively in sync (front and back end).

              you can use event handlers for both… so don’t need to do timed…

              Sam

              How to add modules

              learning how to use browser developers window for css changes

              P 1 Reply Last reply Reply Quote 0
              • P Offline
                peterh @sdetweil
                last edited by

                @sdetweil @shbatm
                Well it seems this is a bit beyond me at this point, I have a bit of a learning curve to get past if want to sort it out. Im more of a hardware guy. :-) At this point I’ve got an separate iframe MMM-module pulling the cam image and the omx player overlay covers it when called. not the best solution but it’s within my skill set. thank you all for your input.

                1 Reply Last reply Reply Quote 0
                • 1 / 1
                • First post
                  Last post
                Enjoying MagicMirror? Please consider a donation!
                MagicMirror created by Michael Teeuw.
                Forum managed by Sam, technical setup by Karsten.
                This forum is using NodeBB as its core | Contributors
                Contact | Privacy Policy