• Recent
  • Tags
  • Unsolved
  • Solved
  • MagicMirror² Repository
  • Documentation
  • 3rd-Party-Modules
  • Donate
  • Discord
  • Register
  • Login
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.1k 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.
  • P Offline
    peterh
    last edited by Oct 15, 2018, 12:50 PM

    I’ve hit a bit of a snag with implementation of the MMM-RTSPStream module. I have 2 cabled ubiquiti external cameras displaying using omxplayer with only a minor delay of about 3 seconds. These are turned on from my Domoticz server, using an API call to MMM-remote to “SHOW” the module whenever Domoticz picks up a doorbell signal using an 433 Mhz RF receiver. Unlike most others who are having problems, I have the actual streaming working satisfactorily.
    Unfortunately the jpeg snapshot displayed appears compressed or scaled down and is not a clear representation of the actual image.
    I have confirmed that the url for the snapshot presents a full resolution image ( i have worked with the ubiquiti cameras for some time). With my limited css knowledge and a lot of Googlefoo I have used the developer tools in chrome and firefox to confirm that the jpg data loaded is a full resolution image. ( i can copy pasta the data back to a file and name it xxx.jpg and view it).
    Where is the magic happening that scales the snapshot down?
    If i grab pass the url for the snapshot to another module such as MMM-Alert the image displays as a small but clear and non-blurry image.
    Somebody give me a clue please!

    1 Reply Last reply Reply Quote 0
    • S Away
      sdetweil
      last edited by sdetweil Oct 15, 2018, 2:44 PM Oct 15, 2018, 2:40 PM

      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 Oct 16, 2018, 3:53 AM Reply Quote 0
      • S Offline
        shbatm Module Developer
        last edited by shbatm Oct 16, 2018, 3:34 AM Oct 16, 2018, 3:32 AM

        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 Oct 16, 2018, 3:53 AM

          @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 Oct 16, 2018, 9:47 PM

            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 Oct 16, 2018, 9:56 PM Reply Quote 0
            • S Away
              sdetweil @shbatm
              last edited by Oct 16, 2018, 9:56 PM

              @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 Oct 22, 2018, 1:34 AM Reply Quote 0
              • P Offline
                peterh @sdetweil
                last edited by Oct 22, 2018, 1:34 AM

                @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
                1 / 1
                • First post
                  1/7
                  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