xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Opened 10 years ago

Closed 10 years ago

Last modified 9 months ago

#94 closed enhancement (fixed)

h264 frame encoding

Reported by: Antoine Martin Owned by: ahuillet
Priority: minor Milestone: 0.2
Component: core Version: 0.1.0
Keywords: Cc:

Description

Using xprax264 and some cython glue code it shouldn't be too hard to get x264 encoded bytes from an rgb24 screen grab.

Code to follow.

Attachments (9)

xpra-x264.patch (10.8 KB) - added by Antoine Martin 10 years ago.
adds an x264 encoder/decoder library, cython glue and makes distutils build it
xpra-x264-r2.patch (14.6 KB) - added by Antoine Martin 10 years ago.
v2 of the patch with support for up to 32 contexts
xpra-x264-r3.patch (26.3 KB) - added by Antoine Martin 10 years ago.
patch with all the changes to client/server/main
xpra-x264-r4.2.patch (27.0 KB) - added by Antoine Martin 10 years ago.
now using proper cython classes to simplify code
xpra-x264-r5.patch (27.4 KB) - added by Antoine Martin 10 years ago.
many fixes, this almost works
xpra-x264-r6.patch (27.6 KB) - added by Antoine Martin 10 years ago.
fix for rowstride, very close..
xpra-x264-setup-Cython-win32.patch (4.0 KB) - added by Antoine Martin 10 years ago.
to try to tell py2exe to invoke Cython on the x264 bindings
x264_win32.patch (1.0 KB) - added by ahuillet 10 years ago.
This patch enables x264 building under win32.
py2exe-xpra.log (45.2 KB) - added by Antoine Martin 10 years ago.
log from a successful py2exe build

Download all attachments as: .zip

Change History (30)

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264.patch added

adds an x264 encoder/decoder library, cython glue and makes distutils build it

comment:1 Changed 10 years ago by ahuillet

Updated the library:
https://github.com/ahuillet/xprax264/

We need to create one encoder per window, ie. one encoder context per window.

init_encoder(width, height) will now return a pointer to an opaque structure (the encoder context). Create one for each window.

Same goes for init_decoder on the client side.

Then, compression is done by passing the context as the first argument to compress_image. Same goes for decompression.

Note that you cannot compress a subpart of the image: the full window must be encoded.

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264-r2.patch added

v2 of the patch with support for up to 32 contexts

comment:2 Changed 10 years ago by Antoine Martin

The patch is a bit ugly, I couldn't find a way of doing dicts (or maps) in cython, so I used python maps to map to the index of a Cython/C array.
Also, I had to use (void *) instead of (x264lib_ctx *) to avoid compilation errors.
Apart from that, it seems to work.

Note: please update to latest trunk which has moved the actual window drawing code to xpra/window_backing.py - you may just implement it for PixmapBacking (gtk2) if you like.
Then add code like this to xpra/scripts/main.py's encoding section:

try:
    from xpra.x264.codec
    ENCODINGS.append("x264")
except:
    pass

Either to the non is_gtk3() codepath, or to the common case if drawing is implemented for both.

Version 0, edited 10 years ago by Antoine Martin (next)

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264-r3.patch added

patch with all the changes to client/server/main

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264-r4.2.patch added

now using proper cython classes to simplify code

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264-r5.patch added

many fixes, this almost works

Changed 10 years ago by Antoine Martin

Attachment: xpra-x264-r6.patch added

fix for rowstride, very close..

comment:3 Changed 10 years ago by Antoine Martin

Status: newaccepted

committed in r642, remaining issues:

  • use rgb24 for small windows (since rgb24 has to be supported for x264)
  • client does not close the decoders when windows go away (ouch!)
  • server encoder close called multiple times if we have resized (must cancel old on_close callback)
  • fix compilation warnings
  • enable x264 logging if xpra debug is on (via new argument to init(w,h)? or separate call?)

etc.

Last edited 10 years ago by Antoine Martin (previous) (diff)

comment:4 Changed 10 years ago by ahuillet

Todo:

  • evaluate bandwidth as compared to TurboVNC with similar visual quality
  • evaluate CPU usage in the same context
  • evaluate compression latency
  • dynamically change compression presets based on compression latency
  • measure decompression latency
  • add hooks for rate control (set target bandwidth)
  • add hooks to change color subsampling (currently hardcoded to 4x YUV420)
  • check if automatic lossless refresh is needed or if ratecontrol does it for free
  • dominate the world
Last edited 10 years ago by ahuillet (previous) (diff)

comment:5 Changed 10 years ago by Antoine Martin

Owner: changed from Antoine Martin to ahuillet
Status: acceptedassigned
  • r648 now fallsback to rgb264 for OR windows
  • r650 ensures we clean the encoder just once (and removes verbose logging)
  • r651 ensures we clean the decoder whenever the window it draws goes away

Changed 10 years ago by Antoine Martin

to try to tell py2exe to invoke Cython on the x264 bindings

comment:6 Changed 10 years ago by Antoine Martin

r660 re-enables x264 for OR windows - still seems to work here

comment:7 Changed 10 years ago by Antoine Martin

For reference, this is what one needs to do to build on OSX (from a jhbuild shell), we add Cython, yasm, ffmpeg and x264:

# Cython:
curl -O http://cython.org/release/Cython-0.15.1.tar.gz
tar -zxvf Cython-0.15.1.tar.gz 
cd Cython-0.15.1
python ./setup.py install

# yasm:
curl -O http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz
tar -zxvf yasm-1.2.0.tar.gz 
cd yasm-1.2.0
./configure --prefix=${JHBUILD_PREFIX} --libdir=${JHBUILD_PREFIX}/lib --build=i386-darwin
make & make install

# ffmpeg:
curl -O http://ffmpeg.org/releases/ffmpeg-0.10.2.tar.bz2
tar -jxf ffmpeg-0.10.2.tar.bz2 
cd ffmpeg-0.10.2
./configure --libdir=${JHBUILD_PREFIX}/lib --prefix=${JHBUILD_PREFIX}  --enable-shared --disable-static
make & make install

# x264:
curl -O ftp://ftp.videolan.org/pub/x264/snapshots/last_x264.tar.bz2
tar -jxf last_x264.tar.bz2
cd x264-snapshot-*
./configure --libdir=/Users/MacAdmin/gtk/inst/lib --prefix=/Users/MacAdmin/gtk/inst --enable-shared --disable-static --enable-pic
make & make_install

Then build xpra as usual:

python setup.py install

comment:8 Changed 10 years ago by Antoine Martin

For win32, you probably need this patch to Cython's Cython/Distutils/extension.py:

@@ -16,7 +16,6 @@ except ImportError:
     warnings = None
 
 class Extension(_Extension.Extension):
-    _Extension.Extension.__doc__ + \
     """pyrex_include_dirs : [string]
         list of directories to search for Pyrex header files (.pxd) (in
         Unix form for portability)

to avoid this error:

TypeError: unsupported operand type(s) for +: 'NoneType' and 'str' in setup.py

Then some magic incantations to tell Visual Studio where to find the libraries and headers, otherwise you get the very unhelpful error:

error: command '"C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe"' failed with exit status 2
Last edited 10 years ago by Antoine Martin (previous) (diff)

comment:9 Changed 10 years ago by ahuillet

The actual error messages seem to be in fact logged to src/py2exe-xpra.log.

They are a bunch of syntax errors in x264lib.h because the include paths are incorrect.

Last edited 10 years ago by ahuillet (previous) (diff)

comment:10 Changed 10 years ago by ahuillet

Latest patch attached to this ticket makes py2exe succeed. The paths to ffmpeg are hardcoded however.

Changed 10 years ago by ahuillet

Attachment: x264_win32.patch added

This patch enables x264 building under win32.

comment:11 Changed 10 years ago by ahuillet

Rebased patch on latest rev. It builds fine under win32 but "xpra attach --encoding x264" refuses the x264 argument.

comment:12 Changed 10 years ago by Antoine Martin

r675 fixes the build for win32.

One troubling issue though: I can connect using x264 from win32 to a Linux server but on the second connection the server will crash hard.. memleak?

comment:13 Changed 10 years ago by ahuillet

A memleak won't trigger a crash, and I'm curious that it is the *server* crashing. The client crashing would be less surprising: can you confirm that the encoder context is *per window* and not *per window per client*?

Naturally it would be easier in the second case.

comment:14 Changed 10 years ago by Antoine Martin

It crashes on avcodec_close() which is called from clean_decoder() in x264lib.c, itself called from Encoder.clean().
We only clean when the client disconnects (via self._on_close or when the dimensions change).
Is it possible somehow that we end up doing both? del encoders[wid] is supposed to remove the reference to the encoder.

(gdb) bt
#0  0x000000300a664604 in avcodec_close () from /usr/lib64/libavcodec.so.53
#1  0x00007fdff357490b in clean_decoder (ctx=0x7fdfdc0017e0) at xpra/x264/x264lib.c:106
#2  0x00007fdff35715da in __pyx_pf_4xpra_4x264_5codec_6xcoder_2clean (__pyx_v_self=
    <xpra.x264.codec.Encoder at remote 0x1754e50>, unused=0x0) at xpra/x264/codec.c:719
#3  0x00000032da6dfb13 in call_function (oparg=<optimized out>, pp_stack=0x7fff65856388)
    at /usr/src/debug/Python-2.7.2/Python/ceval.c:4074
#4  PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:2740
#5  0x00000032da6e15a5 in PyEval_EvalCodeEx (co=<optimized out>, globals=<optimized out>, locals=<optimized out>, 
    args=<optimized out>, argcount=0, kws=0x1a30438, kwcount=0, defs=0x0, defcount=0, closure=
    (<cell at remote 0x195d440>, <cell at remote 0x195dda8>, <cell at remote 0x195dc58>))
    at /usr/src/debug/Python-2.7.2/Python/ceval.c:3330
#6  0x00000032da6dfadb in fast_function (nk=<optimized out>, na=0, n=<optimized out>, pp_stack=0x7fff65856578, func=
    <function at remote 0x195cd70>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4186
#7  call_function (oparg=<optimized out>, pp_stack=0x7fff65856578) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4111
#8  PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:2740
#9  0x00000032da6e0580 in fast_function (nk=<optimized out>, na=1, n=<optimized out>, pp_stack=0x7fff658566b8, func=
    <function at remote 0x1839f50>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4176
#10 call_function (oparg=<optimized out>, pp_stack=0x7fff658566b8) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4111
#11 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:2740
#12 0x00000032da6e0580 in fast_function (nk=<optimized out>, na=3, n=<optimized out>, pp_stack=0x7fff658567f8, func=
    <function at remote 0x183dc08>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4176
#13 call_function (oparg=<optimized out>, pp_stack=0x7fff658567f8) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4111
#14 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:2740
#15 0x00000032da6e0580 in fast_function (nk=<optimized out>, na=3, n=<optimized out>, pp_stack=0x7fff65856938, func=
    <function at remote 0x183dcf8>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4176
#16 call_function (oparg=<optimized out>, pp_stack=0x7fff65856938) at /usr/src/debug/Python-2.7.2/Python/ceval.c:4111
#17 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/Python-2.7.2/Python/ceval.c:2740
#18 0x00000032da6e15a5 in PyEval_EvalCodeEx (co=<optimized out>, globals=<optimized out>, locals=<optimized out>, 
    args=<optimized out>, argcount=3, kws=0x0, kwcount=0, defs=0x7fdff48a1260, defcount=2, closure=0x0)
    at /usr/src/debug/Python-2.7.2/Python/ceval.c:3330
#19 0x00000032da66dc2c in function_call (func=<function at remote 0x7fdff48a2c08>, arg=
    (<Protocol(_read_queue=<Queue(unfinished_tasks=14, queue=<collections.deque at remote 0x18fb7c0>, maxsize=5, all_tasks_done=<_Condition(_Verbose__verbose=False, _Condition__lock=<thread.lock at remote 0x1754cd0>, acquire=<built-in method acquire of thread.lock object at remote 0x1754cd0>, _Condition__waiters=[], release=<built-in method release of thread.lock object at remote 0x1754cd0>) at remote 0x195b110>, mutex=<thread.lock at remote 0x1754cd0>, not_full=<_Condition(_Verbose__verbose=False, _Condition__lock=<thread.lock at remote 0x1754cd0>, acquire=<built-in method acquire of thread.lock object at remote 0x1754cd0>, _Condition__waiters=[], release=<built-in method release of thread.lock object at remote 0x1754cd0>) at remote 0x195b0d0>, not_empty=<_Condition(_Verbose__verbose=False, _Condition__lock=<thread.lock at remote 0x1754cd0>, acquire=<built-in method acquire of thread.lock object at remote 0x1754cd0>, _Condition__waiters=[], release=<built-in method release of thread.lock object at remote 0x1754c...(truncated), kw=0x0) at /usr/src/debug/Python-2.7.2/Objects/funcobject.c:526

comment:15 Changed 10 years ago by Antoine Martin

that's fixed in r678

comment:16 Changed 10 years ago by ahuillet

Trying to run under Windows:

cannot load x264: cannot import name codec

comment:17 Changed 10 years ago by Antoine Martin

You need to ensure that the paths in the build files are correct, if they are then py2exe will generate a loader for "codec.pyd" and include all the required DLLs itself.

Changed 10 years ago by Antoine Martin

Attachment: py2exe-xpra.log added

log from a successful py2exe build

comment:18 Changed 10 years ago by Antoine Martin

The important parts from the log file above:

creating python loader for extension 'xpra.x264.codec'
  (E:\xpra\src\xpra\x264\codec.pyd -> xpra.x264.codec.pyd)
*** copy extensions ***
(..)
copying E:\xpra\src\xpra\x264\codec.pyd -> E:\xpra\src\dist\xpra.x264.codec.pyd
*** copy dlls ***
(..)
copying Z:\ffmpeg-win32-shared\bin\avcodec-54.dll -> E:\xpra\src\dist
(..)
copying Z:\ffmpeg-win32-shared\bin\swscale-2.dll -> E:\xpra\src\dist
(..)
copying Z:\ffmpeg-win32-shared\bin\avutil-51.dll -> E:\xpra\src\dist

Assuming you have those, the win32 build should have x264 support.

comment:19 Changed 10 years ago by ahuillet

x264 under windows now works.

comment:20 Changed 10 years ago by Antoine Martin

Resolution: fixed
Status: assignedclosed

this is enough for the 0.2 release, the remaining issues are in #110

comment:21 Changed 9 months ago by migration script

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/94

Note: See TracTickets for help on using tickets.