Sunday, March 11, 2012

A video showing video on the Samsung photoframe

Using the Python programs from this site I demonstrate with a video recorded by digital camera from a Samsung SPF-87H Digital Photo Frame. The quality of the video shown here on the blog is awful in color and resolution, while on the photoframe itself both are excellent. But at least this clip shows that the video plays smoothly through all scenes.


The setup used a virtual frame buffer, so this setting can also be used for in a headless client. In a terminal give these commands :

Xvfb :99 -screen 0 800x480x16 &
DISPLAY=:99 ./videoframe &
DISPLAY=:99 mplayer -fs /path/to/bbbunny_720p_h264.mov
This creates a virtual frame buffer xserver as #99 with a screen resolution the same as the photoframe (800x480, change to match your frame if needed, here and also in the script ), and in it starts the Python videoframe script (see below), and uses mplayer to play a movie in full screen mode. This was then recorded with a digital camera from the photoframe, and uploaded to this post.

The videoframe script records some frame and transfer rates. Here is an excerpt from the final scenes (each line is an average over 50 frames, i.e. 2-3 seconds):
Frames per second: 18.68, Megabytes per second: 0.84
Frames per second: 17.93, Megabytes per second: 0.88
Frames per second: 17.80, Megabytes per second: 0.87
Frames per second: 17.78, Megabytes per second: 0.87
Frames per second: 17.97, Megabytes per second: 0.88
Frames per second: 18.02, Megabytes per second: 0.89
Frames per second: 17.89, Megabytes per second: 0.88
Frames per second: 17.96, Megabytes per second: 0.88
Frames per second: 15.99, Megabytes per second: 0.96
Frames per second: 17.12, Megabytes per second: 0.89
Frames per second: 16.23, Megabytes per second: 0.96
Frames per second: 16.66, Megabytes per second: 0.94
Frames per second: 17.87, Megabytes per second: 0.88
Frames per second: 17.79, Megabytes per second: 0.90
Frames per second: 16.01, Megabytes per second: 0.98
Frames per second: 19.45, Megabytes per second: 0.80
Frames per second: 22.04, Megabytes per second: 0.69
Frames per second: 22.18, Megabytes per second: 0.68
Frames per second: 21.53, Megabytes per second: 0.71
Frames per second: 18.49, Megabytes per second: 0.88
Frames per second: 18.18, Megabytes per second: 0.88
Frames per second: 17.78, Megabytes per second: 0.90
Frames per second: 18.45, Megabytes per second: 0.83
Frames per second: 19.07, Megabytes per second: 0.83
Frames per second: 17.95, Megabytes per second: 0.88
Frames per second: 18.36, Megabytes per second: 0.85
Frames per second: 20.55, Megabytes per second: 0.74
Frames per second: 21.25, Megabytes per second: 0.70
Frames per second: 20.64, Megabytes per second: 0.74
Depending on the complexity of the picture to be jpg coded, the observed variation in fps ranges from 11 ... 27fps. In this setup the cpu is a 6 year old Intel Core2 T7200 2.0GHz, running at ~50% load (its cpu-mark is 1150; for reference: today's Intel Core i5-2500 has a cpu-mark of 6750). As noticed earlier, the bottleneck appears to be the frame itself. The speed of movements within the movie scenes does NOT play a role for the transfer rate, as always a single screenshot is taken and processed. However, fine structures (grass, hair, fur,...) which make for big jpg files slow the frame rate down.

The python code for videoframe is shown below the line. See code in other posts below for more detailed comments on parts of the script.

Update:
the command:
pmap.save(buffer, 'jpeg')
is the same as:
pmap.save(buffer, 'jpeg', quality = -1)
which sets the quality to its default setting of 75. Quality ranges from 0 (=very poor) to 100 (=very good). The save command itself is not faster at poorer settings, but the resulting picture size is smaller, and thus transfer speed over the USB bus increases, allowing higher frame rates! Quality settings of 60 are usually good enough; certainly for video.

see reference in source code:
http://cep.xor.aps.anl.gov/software/qt4-x11-4.2.2-browser/d0/d0e/qjpeghandler_8cpp-source.html#l00897
00959         int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
/Update
_________________________________________________________________________________
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# Program: videoframe
#
# This videoframe program plays videos on the 'Samsung SPF-87H Digital Photo Frame'
# by taking rapid snapshots from a video playing on a screen and transfers them as jpeg
# pictures to the photo frame
#
# It is an application of the sshot2frame program found on the same
# website as this program
# Read that post to understand details not commented here
# Copyright (C) ullix

import sys
import struct
import usb.core

# additional imports are required
from PyQt4 import QtGui, QtCore
import time

device = "SPF87H Mini Monitor"
dev = usb.core.find(idVendor=0x04e8, idProduct=0x2034)

if dev is None:
    print "Could not find", device, " - using screen\n"
    frame = False
else:
    frame = True
    print "Found", device
    dev.ctrl_transfer(0xc0, 4 )  

app  = QtGui.QApplication(sys.argv)

fd = open("shot.log","a", 0)

# Enter into a loop to repeatedly take screenshots and send them to the frame
start  = time.time()
frames = 0
mbyte = 0

while True:
    # take a screenshot and store into a pixmap
    # the screen was set to 800x480, so it already matches the photoframe
    # dimensions, and no further processing is necessary
    pmap = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId())
   
    # create a buffer object and store the pixmap in it as if it were a jpeg file   
    buffer = QtCore.QBuffer()
    buffer.open(QtCore.QIODevice.WriteOnly)
    pmap.save(buffer, 'jpeg')
   
    # now get the just saved "file" data into a string, which we will send to the frame
    pic = buffer.data().__str__()
   
    if not frame:
        print "no photoframe found; exiting"
        sys.exit()
    else:
        rawdata = b"\xa5\x5a\x18\x04" + struct.pack('<I', len(pic)) + b"\x48\x00\x00\x00" + pic
        pad = 16384 - (len(rawdata) % 16384)
        tdata = rawdata + pad * b'\x00'
        tdata = tdata + b'\x00'
        endpoint = 0x02
        bytes_written = dev.write(endpoint, tdata )
        mbyte += bytes_written

    frames += 1  

    # write out info every 50 frames
    if frames % 50 == 0:
        runtime = time.time() -start
        fd.write("Frames per second: {0:0.2f}, Megabytes per second: {1:0.2f}\n".format( frames / runtime, mbyte/runtime /1000000.))
        start  = time.time()
        frames = 0
        mbyte = 0
     

1 comment: