The following situation happened, the Axis 292 Network CCTV Video decoder was damaged during one of the thunderstorms with lightnings. The device was properly grounded (to the separate from lightning rod) and installed in the basement. However, this Network CCTV VD was used to remotly monitore the values on the heating contol post.

The host system of the control device is based on Raspberry Pi model B which is running Raspbian GNU/Linux. The informtaion is rendered on the screen using OpenVG. So, it is possible to capture the contents of the framebuffer in order to see what is rendered on the screen.

I was not sure if there was enough computing power left because the control system utilizing 45% CPU in peak. Surfing the web, searching for the possible options, I found github/telepi which could do the trick.

README.md TelePi - remote streaming for your Raspberry Pi

TelePi is an attempt to build a better remote desktop solution for the Raspberry Pi by utilising the features of the platform. Dispmanx is used to capture the exactly what >appears on screen and the hardware H.264 encoder to used to compress the video in real-time to allow for a smoother, higher framerate experience compared to traditional >solutions such as VNC and remote X windows. It also brings the benefit of being compatible with all Raspberry Pi applications including Minecraft.

In addition to the streaming feature, TelePi can record the raw H.264 stream to a file making it useful for recording videos for tutorials and demostrations without the need for >external hardware.

The latest commit was 8a2a15d on Dec 1, 2014.

The good thing about this approach is that both VLC and telepi does not require root privilegies.

If you decide to use this approach and patched code, I am not responsible for the possible damages to your device, data etc (if any chance that it can happen).

Testing the compiled code, overall, demonstrated that this approach is fine enough. Below could be found instructions how to setup a software to stream the contents of the framebuffer on the Raspberry Pi Linux.

  1. Obtaining the code and dependencies
    #compile the ilclient because it is a dependancy
    cd /opt/vc/src/hello_pi/libs/ilclient
    make
    #cloning the repo to home dir
    cd ~
    git clone https://github.com/DougGore/telepi.git
    cd telepi
  2. Patching code and Makefile In the Makefile add -DOMX_SKIP64BIT so it should look like this:
    all:
    gcc telepi.c encode.c -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -DOMX_SKIP64BIT -Wno-psabi -I /opt/vc/include/ -I /opt/vc/include/interface/vcos/pthreads/ -I /opt/vc/include/interface/vmcs_host/linux/ -I /opt/vc/src/hello_pi/libs/ilclient/ -L /opt/vc/lib -L /opt/vc/src/hello_pi/libs/ilclient -lpthread -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lilclient -o telepi

    Also, each time it will be decided to modify the bitrate or framerate, it will be needed to modify the code and recompile it. In order to avoid it, the following code can be modified:
    In telepi.c substitute two functions

static int
video_encode_test(char * bitrate, char * framerate, char *outputfilename)
{
   int status = 0;
   int framenumber = 0;
   FILE *outf;

   int ret;

   OMX_ERRORTYPE r;

   VC_IMAGE_TYPE_T type = VC_IMAGE_BGR888;

   fprintf(stderr, "Open display[%i]...\n", screen );
   display = vc_dispmanx_display_open( screen );

   ret = vc_dispmanx_display_get_info(display, &info);
   assert(ret == 0);
   fprintf(stderr, "Display is %d x %d\n", info.width, info.height );

   resource = vc_dispmanx_resource_create( type,
                                           info.width,
                                           info.height,
                                           &vc_image_ptr );

   fprintf(stderr, "VC image ptr: 0x%X\n", vc_image_ptr);

   ret = vc_dispmanx_rect_set(&rect, 0, 0, info.width, info.height);
   assert(ret == 0);

   encode_init(&video_encode);
     // patch starts
    char * end;
    int32_t fr = 25;
    fr = strtol(framerate, &end, 10);
    if (fr <= 0)
    {
        printf("framerate can not be less or equal zero\r\n");
        fr = 25;
    }
   encode_config_format(video_encode, info.width, info.height, fr);
   encode_config_encoding(video_encode, OMX_VIDEO_CodingAVC);

    uint32_t br = strtoul(bitrate, &end, 10);
    if (br == 0)
    {
        printf("bitrate can not be null\r\n");
        br = 16000000UL;
    }
   encode_config_bitrate(video_encode, br/*16000000*/ /* 8388608 */);
// patch ends-------
   //encode_config_low_latency(video_encode, true);

   encode_config_activate(video_encode);

   if (outputfilename[0] == '-')
   {
      outf = stdout;
   }
   else
   {
      outf = fopen(outputfilename, "wb");
   }

   if (outf == NULL)
   {
      fprintf(stderr, "Failed to open '%s' for writing video\n", outputfilename);
      exit(1);
   }

   fprintf(stderr, "looping for buffers...\n");
   do
   {
      buf = ilclient_get_input_buffer(video_encode, 200, 1);

      if (buf != NULL)
      {
         /* fill it */
         take_snapshot(buf->pBuffer, &buf->nFilledLen);
         framenumber++;

         if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf) != OMX_ErrorNone)
         {
            fprintf(stderr, "Error emptying buffer!\n");
         }

        out = ilclient_get_output_buffer(video_encode, 201, 1);

        r = OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out);
        if (r != OMX_ErrorNone)
         {
           fprintf(stderr, "Error filling buffer: %x\n", r);
        }

        if (out != NULL)
         {
            /*
           if (out->nFlags & OMX_BUFFERFLAG_CODECCONFIG)
            {
               int i;
               for (i = 0; i < out->nFilledLen; i++)
                  printf("%x ", out->pBuffer[i]);
               printf("\n");
            }
            */

            r = fwrite(out->pBuffer, 1, out->nFilledLen, outf);
            if (r != out->nFilledLen)
            {
               fprintf(stderr, "fwrite: Error emptying buffer: %d!\n", r);
            }
            else
            {
               //fprintf(stderr, "Writing frame %d/%d\n", framenumber, NUMFRAMES);
            }

            fflush(outf);

            out->nFilledLen = 0;
         }
         else
         {
            fprintf(stderr, "Not getting it :(\n");
         }

      }
      else
      {
         fprintf(stderr, "Doh, no buffers for me!\n");
      }

   } while (framenumber < NUMFRAMES);

   fclose(outf);

   fprintf(stderr, "Teardown.\n");

   encode_deinit(video_encode);

   return status;
}

int
main(int argc, char **argv)
{
   fprintf(stderr, "TelePi - Raspberry Pi remote viewer\n\n");
//patch starts------
   if (argc < 4) {
      fprintf(stderr, "Usage:\n");
      fprintf(stderr, "Record to file: %s bitrate framerate <filename>\n", argv[0]);
      fprintf(stderr, "Output to stdout: %s bitrate framerate <filename>\n", argv[0]);

      fprintf(stderr, "\nStream to a remote computer:\n");
      fprintf(stderr, "%s - | netcat <remote_ip> 5001\n\n", argv[0]);
      exit(1);
   }

   bcm_host_init();

   return video_encode_test(argv[1], argv[2], argv[3]);
 //patch ends-----
}

The new usage syntax is:

telepi <bitrate> <framerate> <filename>  
#i.e telepi 1000000 25 -

Now, invoke make

make

Usage

When executing telepi with filename as argument '-' the output from the telepi is sent to stdout in order to use it in pipeline. The output of telepi can be streamed or multicasted or whatever. In the example below, there is demonstrated insecure method of how to setup an insecure stream server using cVLC:

#using port 8090, no password, no encryption (good approach for intranet, bad for streaming over internet)
./telepi 100000 15 - | cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264

The result: The VLC is connected to the created video stream over HTTP. The right window (ssh session to host) is debug output of the cVLC.

top CPU utilization.

Next Post Previous Post