BuhByteArray

I am trying to send the contents of a bitmap as as byte array to a server to be re-constituted as a jpeg. The php file creates a jpg from known good data and it’s also receiving my bad data fine. so the problem seems to be the data format i’m outputting from the roku.

so we start here:

thispainting= screen.GetByteArray(0, 0, 5, 5)
						strx=thispainting.ToBase64String()
						? strx
						SendPaintingPost(strx) 

The code the Roku outputs looks like this:

$data ='q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/w==';

and the good sample code i have looks like this:

$data = 'iVBORw0KGgoAAAANSUhEUgAAABwAAAASCAMAAAB/2U7WAAAABl'  //works
       . 'BMVEUAAAD///+l2Z/dAAAASUlEQVR4XqWQUQoAIAxC2/0vXZDr'
       . 'EX4IJTRkb7lobNUStXsB0jIXIAMSsQnWlsV+wULF4Avk9fLq2r'
       . '8a5HSE35Q3eO2XP1A1wQkZSgETvDtKdQAAAABJRU5ErkJggg==';

See how one of these things is not like the other?
This is the PHP thats making the jpeg.

$data = base64_decode($data);
$im = imagecreatefromstring($data);

Any ideas would be appreciated. Thanks

GetByteArray returns pixels (color values), not JPEG data, so if your server is expecting to be passed a JPEG encoded data blob it isn’t going to work.

http://sdkdocs.roku.com/display/sdkdoc/ … erasObject :

ifDraw2D GetByteArray(x as Integer, y as Integer, width as Integer, height as Integer) as Object

Returns an roByteArray representing the 32 bit RGBA pixel values for the rectangle described by the parameters.

Also, even if the server accepted an RGBA bitmap, the server is doing a base64 decode so you would need to do a base64 encode on the Roku before sending it. That’s why your data doesn’t look like the sample data.

–Mark

“RokuMarkn” wrote:
Also, even if the server accepted an RGBA bitmap, the server is doing a base64 decode so you would need to do a base64 encode on the Roku before sending it. That’s why your data doesn’t look like the sample data.

–Mark

OK, so what is this

strx=thispainting.ToBase64String()

doing then? Is it not an encode?

KC - So is this php

$im = imagecreatefromstring($data);

expecting jpeg encoded data? Because you can do either imagejpeg(’ ‘) or imagepng(’ ') after this to make a file.

Either one of you care to make a suggestion on how I might make it work?

Thanks.

Sorry, I didn’t notice the encode. But the $data= line you quoted doesn’t look like it was printed on the Roku. That looks like piece of PHP code. However if the string in quotes is what the Roku is printing, then it’s fine. It’s a valid base64 string representing 25 words of &hABABABFF.

I’ve never used it but according to the PHP doc, imagecreatefromstring accepts JPEG, PNG, GIF, WBMP, and GD2. There may be some tools on your server that would help convert a raw bitmap to one of those file types.

–Mark

“squirreltown” wrote:

KC - So is this php

$im = imagecreatefromstring($data);

expecting jpeg encoded data? Because you can do either imagejpeg(’ ‘) or imagepng(’ ') after this to make a file.

I can only guess about the PHP code. FWIW your example good $data appears to be PNG file data.

You might look into whether the server can accept raw pixel data, in which case I imagine you would also need to pass the dimensions.
Or BMP format, which is also fairly easy to generate a header for it followed by the pixel data.

Is your intent to send a full screen image, at 1280 x 720? Or just a small part of it?
If you are sending a full screen image, without compression it would be pretty big (3.5 MB?).

It seems what you really want is a Roku 2D API that gives you PNG or JPEG output, but that is not available.

“RokuMarkn” wrote:
Sorry, I didn’t notice the encode. But the $data= line you quoted doesn’t look like it was printed on the Roku. That looks like piece of PHP code.Mark
Well i just copied what the roku printed from the console and enclosed it with data=’ and '; . At this point i’m just pasting the data into the php file its easier then sending it each time until it works.

“RokuKC” wrote:
It seems what you really want is a Roku 2D API that gives you PNG or JPEG output, but that is not available.
You sure hit that nail on the head.

“RokuKC” wrote:
Is your intent to send a full screen image, at 1280 x 720? Or just a small part of it?
If you are sending a full screen image, without compression it would be pretty big (3.5 MB?).

Yea, thats the end game. It wouldn’t happen often. I have a function that makes a decent “painting” out of a photgraph. It would have a lot more promise if i could get the silly image out of the Roku.

“squirreltown” wrote:

Either one of you care to make a suggestion on how I might make it work?

I’m not a PHP developer, but spurred by Mark’s comment looked at PHP docs briefly…

Looks like if you had to, on the server you could do $img = imagecreatetruecolor( $w, $h ) to allocate an image buffer,
then walk over all the pixels and set them from the input pixel data using imagesetpixel.

I imagine there may be more efficient approaches though, browsing PHP docs or internet search may provide options.

“RokuKC” wrote:
Looks like if you had to, on the server you could do $img = imagecreatetruecolor( $w, $h ) to allocate an image buffer,
then walk over all the pixels and set them from the input pixel data using imagesetpixel.

I imagine there may be more efficient approaches though, browsing PHP docs or internet search may provide options.

OK thanks I’ll have a look.

Well I got this working to some degree. I used KC’s suggestion about imagesetpixel. The size limit seems to about 350 x 350 at this point, not sure if my servers POST data limit is affecting that but i’m checking, also switching to base64 from the hex string I’m using now would save 40% on the number of characters sent.
The server builds the .jpg at that size pretty fast, as fast as i can refresh my ftp app once the Roku says its sent.
I will say it feels pretty ridiculous to be doing it this way, with pony-express era technology, almost as ridiculous as having to record my own Roku sounds when they are already in the box.
Please Roku implement this feature, just because you all can’t see a good reason for some feature, doesn’t mean someone else can’t. Everyone wins if you just make things available.

at this point, 360 x 360 seems to be the limit. KC, Mark - is there a limit to the string size the Roku will output? I say this because it takes several seconds for URL transfer to send the file (roku3) but the server builds 360x360 immediately, and won’t do anything at 370x370. There are no size limits in the PHP file and I’ve upped the server limits for time and memory way beyond what is needed.

Just a thought … instead of using all that encoding and decoding and bases and hexes and stuff, could you just write your roByteArray to a tmp file using ifByteArray.WriteFile, then send the file to your server using ifUrlTransfer.AsyncPostFromFile?

Then your server can read all the bits and bytes from the file without having to decode anything:

<?php
$in = fopen('php://input', 'r');
$out = fopen('outfile', 'w');
stream_copy_to_stream($in, $out);
?>

“belltown” wrote:
Just a thought … instead of using all that encoding and decoding and bases and hexes and stuff, could you just write your roByteArray to a tmp file using ifByteArray.WriteFile, then send the file to your server using ifUrlTransfer.AsyncPostFromFile?
That seems like a good idea that I can get to later once I can write the whole file - One problem is I actually know very little about PHP ( i just had to look up stream_copy_to_stream, as i did with imagesetpixel when KC mentioned it), although I’ve used it for years on my website, so if i get something working there is usually another big problem to deal with rather then optimizing as I go along, which is certainly the case here. This is the script thats building the jpeg from the POST data which I cobbled together from different examples and some guessing.

      $data = $_POST['subject'];
		$owner = $_GET['owner'];
		$sizex = $_GET['sizew'];
		$sizey = $_GET['sizeh'];
		$hexcolors = str_split ( $data, 8 ); //for hex string roku output
		$file_name= $owner.".jpg";
		$im = imagecreatetruecolor($sizex, $sizey);		
		$i=0;
		$y=0;
		while($y < $sizey) {
			$x=0;
			while($x < $sizex) {
					$color = $hexcolors[$i];  
					$thiscolor= imagecolorallocate($im, hexdec(substr($color,0,2)), hexdec(substr($color,2,2)), hexdec(substr($color,4,2)));
					imagesetpixel($im, $x,$y, $thiscolor);
					$i++;
				$x++;
				}
			$y++;
			}
		imagejpeg($im, $file_name);
		imagedestroy($im);

One thing about the whole decoding thing, when i started this I first printed the bytearray as an ascii string to the console, and it didnt even look like a recognizeable anything - question marks and space was all I saw, probably doing something wrong, but at least hex values make sense to me and as you see above easily decoded, maybe easier then if i had the separate RGB values to deal with?

I was able to create a 720x480 byte array containing a bitmap of the French flag, send the byte array to my server, which in turn converted it to a jpg file, which I then displayed on my screen.

Here’s the PHP code:

<?php
$owner = $_GET['owner'];
$sizex = $_GET['sizew'];
$sizey = $_GET['sizeh'];

$file_name = "$owner.jpg";

$in = fopen('php://input', 'r');

$im = imagecreatetruecolor($sizex, $sizey);      

$y=0;
while($y < $sizey) {
    $x=0;
    while($x < $sizex) {
        $thiscolor = imagecolorallocate($im, ord (fgetc ($in)), ord (fgetc ($in)), ord (fgetc ($in)));
        imagesetpixel($im, $x, $y, $thiscolor);
        fgetc ($in);
        $x++;
    }
    $y++;
}
imagejpeg($im, $file_name);
imagedestroy($im);
?>

And the BrightScript code:

Sub Main ()
    width = 720
    height = 480
    owner = "sqwiril"

    ' Make an image  of the French flag and convert to an roByteArray
    byteArray = MakeByteArray (width, height)

    ' Send the byte array to the server for conversion to a jpg file
    Convert (byteArray, owner, width, height)
    
    ' Display the image
    Display (owner)
End Sub

Function MakeByteArray (width As Integer, height As Integer) As Object
    byteArray = CreateObject ("roByteArray")
    byteArray.SetResize (width * height * 4, False)
    i = 0
    For x = 0 To height - 1
        For y = 0 To width - 1
            If y < CInt (width / 3)
                r = 0   : g = 0     : b = 255   : a = 255
            Else If y < CInt (width * 2 / 3)
                r = 255 : g = 255   : b = 255   : a = 255
            Else
                r = 255 : g = 0     : b = 0     : a = 255
            EndIf
            byteArray [i + 0] = r
            byteArray [i + 1] = g
            byteArray [i + 2] = b
            byteArray [i + 3] = a
            i = i + 4
        End For
    End For
    Return byteArray
End Function

Function Convert (byteArray As Object, owner As String, width As Integer, height As Integer) As Void
    ' Convert the byte array to a file
    byteArray.WriteFile ("tmp:/byteArray")

    ' Send file to server
    ut = CreateObject ("roUrlTransfer")
    port = CreateObject ("roMessagePort")
    ut.SetPort (port)
    ut.SetUrl ("http://belltown.tk/bigimage.php?owner=" + owner + "&sizew=" + width.ToStr () + "&sizeh=" + height.ToStr ())
    If Not ut.AsyncPostFromFile ("tmp:/byteArray")
        Print "AsyncPostFromFile failed"
        Stop
    EndIf
    
    msg = Wait (0, port)
    If msg = Invalid
        Print "Image upload timed out"
        Stop
    Else If Type (msg) <> "roUrlEvent"
        Print "Unknown event"
        Stop
    Else If msg.GetResponseCode () <> 200
        Print "Http Failure"; msg.GetResponseCode ()
        Stop
    Else
        Print "File upload successful"
    EndIf
End Function

Function Display (owner As String) As Void
    url = "http://belltown.tk/" + owner + ".jpg"
    ui = CreateObject ("roImageCanvas")
    port = CreateObject ("roMessagePort")
    ui.SetMessagePort (port)
    ui.SetRequireAllImagesToDraw (True)
    ui.SetLayer (0, {Url: url})
    ui.Show ()
    Wait (0, port)
End Function

It’s even easier if you have ImageMagick installed on your server. Just use this script:

<?php
$owner = $_GET['owner'];
$sizex = $_GET['sizew'];
$sizey = $_GET['sizeh'];

$in = fopen('php://input', 'r');
$out = fopen("$owner.rgba", 'w');
stream_copy_to_stream($in, $out);

exec("convert -size {$sizex}x{$sizey} -depth 8 $owner.rgba $owner.jpg");

@unlink ("$owner.rgba");
?>

I also tried it with a 1280x720 image – and did a base64 encode/decode to make the transfer size smaller. That worked as well, although it can take a while to upload a bitmap that large depending on your internet connection speed.

Vive La belltown! (sorry if that should be a le not la, my french is worse than my php) Thanks for all that. I just got it to work - very good lesson here about error handling. Because you are a professional and I only play one on TV, you did the thing pros do which is to put error handling into the URLtransfer thingie which allowed me to see i was getting a 413 error ( which may have been the problem yesterday) and so i was able to contact the server folks and they just yelled at the hamster on my server rack and fixed it, so now i see the ‘tricolour’ on my TV. Also thanks for the ImageMajik code, i do have that installed and will be using it to profile the image when i get to that part. Thanks again for your help.

Ok one last thing - that Imagemajik code - WoW! that produced a way way way better file - 650kb vs 120kb and no jpeg-ing artifacts. What a difference.

imagejpeg has an optional third parameter to specify the image quality. Tweaking that value might give similar results to imagemagick, though of course no reason to do that if you’re happy with the current implementation.

–Mark

“RokuMarkn” wrote:
imagejpeg has an optional third parameter to specify the image quality. Tweaking that value might give similar results to imagemagick, though of course no reason to do that if you’re happy with the current implementation.

–Mark
Yea, plus Imagmajik has two things which are pretty much necessary - color profiling and bluring to de-jaggie the image.

Also I tried using base64 and it was NOT a help, it consistently took maybe 15% longer to send the string on my test image. It would make sense since the hex string always uses 8 characters to describe a pixel, the base64 sometimes uses only 4, but more often than not uses 10.

Either way if Roku would like to add the ability to at least URLtransfer a bitmap thats - you know - already created - i won’t stop them.

“squirreltown” wrote:
Either way if Roku would like to add the ability to at least URLtransfer a bitmap thats - you know - already created - i won’t stop them.
I’d prefer, and have requested multiple times, the ability to create a PNG on the device from an roBitmap. The libraries are already there, so it shouldn’t require a major update, but to date, it doesn’t seem to have gained any traction.