Skip to content

Commit

Permalink
#147: support all CSC modes:
Browse files Browse the repository at this point in the history
* expose csc constants using our own constants defined in codec_constants.py
* gl_window_backing can then calculate the values to use for dividing the texture dimensions

git-svn-id: https://xpra.org/svn/Xpra/trunk@2010 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Oct 31, 2012
1 parent 70d268b commit e037f57
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 85 deletions.
9 changes: 9 additions & 0 deletions src/xpra/codec_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# coding=utf8
# This file is part of Parti.
# Copyright (C) 2012 Antoine Martin <[email protected]>
# Parti is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

YUV420P = 0
YUV422P = 1
YUV444P = 2
13 changes: 6 additions & 7 deletions src/xpra/gl_client_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@
gl_minor = int(glGetString(GL_VERSION)[2])
if gl_major<=1 and gl_minor<1:
raise ImportError("** OpenGL output requires OpenGL version 1.1 or greater, not %s.%s" % (gl_major, gl_minor))
log.info("found valid OpenGL: %s.%s", gl_major, gl_minor)
log("found valid OpenGL: %s.%s", gl_major, gl_minor)

#this allows us to do CSC via OpenGL:
#see http://www.opengl.org/registry/specs/ARB/fragment_program.txt
use_openGL_CSC = glInitFragmentProgramARB()
if not use_openGL_CSC:
if not glInitFragmentProgramARB():
raise ImportError("** OpenGL output requires glInitFragmentProgramARB")
finally:
gldrawable.gl_end()
Expand All @@ -47,26 +46,26 @@
class GLClientWindow(ClientWindow):

def __init__(self, client, wid, x, y, w, h, metadata, override_redirect, client_properties, auto_refresh_delay):
log.info("GLClientWindow(..)")
log("GLClientWindow(..)")
self._configured = False
ClientWindow.__init__(self, client, wid, x, y, w, h, metadata, override_redirect, client_properties, auto_refresh_delay)
self.add(self._backing.glarea)

def do_configure_event(self, event):
log.info("do_configure_event(%s)", event)
log("GL do_configure_event(%s)", event)
self._configured = True
ClientWindow.do_configure_event(self, event)
w, h = self.get_size()
self._backing.init(w, h)

def do_expose_event(self, event):
log.info("do_expose_event(%s) area=%s, mapped=%s", event, event.area, self.flags() & gtk.MAPPED)
log("GL do_expose_event(%s) area=%s, mapped=%s", event, event.area, self.flags() & gtk.MAPPED)
if not (self.flags() & gtk.MAPPED):
return
self._backing.render()

def new_backing(self, w, h):
log.info("new_backing(%s, %s)", w, h)
log("GL new_backing(%s, %s)", w, h)
#self._backing = new_backing(self._id, w, h, self._backing, self._client.supports_mmap, self._client.mmap)
w = max(1, w)
h = max(1, h)
Expand Down
133 changes: 58 additions & 75 deletions src/xpra/gl_window_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from wimpiggy.log import Logger
log = Logger()

from xpra.codec_constants import YUV420P, YUV422P, YUV444P
from xpra.gl_colorspace_conversions import GL_COLORSPACE_CONVERSIONS
from xpra.window_backing import PixmapBacking
from OpenGL.GL import GL_PROJECTION, GL_MODELVIEW, GL_VERTEX_ARRAY, \
Expand Down Expand Up @@ -47,8 +48,10 @@ def __init__(self, wid, w, h, mmap_enabled, mmap):
self.glarea.set_size_request(w, h)
self.glarea.show()
self.textures = None # OpenGL texture IDs
self.yuv420_shader = None
self.yuv_shader = None
self.pixel_format = None
self.size = 0, 0

self.current_mode = GLPixmapBacking.MODE_UNINITIALIZED

def init(self, w, h):
Expand All @@ -58,15 +61,16 @@ def init(self, w, h):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()

self.yuv420_shader = None
self.yuv_shader = None

# Re-create textures
self.pixel_format = None
self.current_mode = GLPixmapBacking.MODE_UNINITIALIZED

if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")

log.info("GL Pixmap backing size: %d x %d", w, h)
log("GL Pixmap backing size: %d x %d", w, h)
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
Expand All @@ -82,14 +86,14 @@ def init(self, w, h):
drawable.gl_end()

def render(self):
log.info("GL render")
log("GL render")
self.render_image()
self.glarea.window.invalidate_rect(self.glarea.allocation, False)
# Update window synchronously (fast).
self.glarea.window.process_updates(False)

def do_video_paint(self, coding, img_data, x, y, width, height, options, callbacks):
log.info("do_video_paint: options=%s, decoder=%s", options, type(self._video_decoder))
log("do_video_paint: options=%s, decoder=%s", options, type(self._video_decoder))
err, rowstrides, img_data = self._video_decoder.decompress_image_to_yuv(img_data, options)
success = err==0 and img_data and len(img_data)==3
if not success:
Expand All @@ -98,112 +102,91 @@ def do_video_paint(self, coding, img_data, x, y, width, height, options, callbac
self.fire_paint_callbacks(callbacks, False)
return
def do_paint():
self.update_texture_yuv420(img_data, x, y, width, height, rowstrides)
csc_pixel_format = options.get("csc_pixel_format", -1)
pixel_format = self._video_decoder.get_pixel_format(csc_pixel_format)
self.update_texture_yuv(img_data, x, y, width, height, rowstrides, pixel_format)
self.render_image()
self.fire_paint_callbacks(callbacks, True)
gobject.idle_add(do_paint)

def update_texture_yuv420(self, img_data, x, y, width, height, rowstrides):
def get_subsampling_divs(self, pixel_format):
if pixel_format==YUV420P:
return 1, 2, 2
elif pixel_format==YUV422P:
return 1, 2, 1
elif pixel_format==YUV444P:
return 1, 1, 1
raise Exception("invalid pixel format: %s" % pixel_format)

def update_texture_yuv(self, img_data, x, y, width, height, rowstrides, pixel_format):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()
window_width, window_height = self.size
if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")
assert self.textures is not None

if self.current_mode == GLPixmapBacking.MODE_UNINITIALIZED:
log.info("Creating new YUV textures")

if self.pixel_format is None or self.pixel_format!=pixel_format:
self.pixel_format = pixel_format
divs = self.get_subsampling_divs(pixel_format)
log("GL creating new YUV textures for pixel format %s using divs=%s", pixel_format, divs)
# Create textures of the same size as the window's
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width, window_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

log.info("Assigning fragment program")
for texture, index in ((GL_TEXTURE0, 0), (GL_TEXTURE1, 1), (GL_TEXTURE2, 2)):
div = divs[index]
glActiveTexture(texture)
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[index])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
mag_filter = GL_NEAREST
if div>1:
mag_filter = GL_LINEAR
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, mag_filter)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/div, window_height/div, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0)

log("Assigning fragment program")
glEnable(GL_FRAGMENT_PROGRAM_ARB)
if not self.yuv420_shader:
self.yuv420_shader = [ 1 ]
glGenProgramsARB(1, self.yuv420_shader)
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])
if not self.yuv_shader:
self.yuv_shader = [ 1 ]
glGenProgramsARB(1, self.yuv_shader)
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv_shader[0])
prog = GL_COLORSPACE_CONVERSIONS
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, len(prog), prog)
log.error(glGetString(GL_PROGRAM_ERROR_STRING_ARB))
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])

self.current_mode = GLPixmapBacking.MODE_YUV
else:
assert self.current_mode == GLPixmapBacking.MODE_YUV
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv_shader[0])

# Clamp width and height to the actual texture size
if x + width > window_width:
width = window_width - x
if y + height > window_height:
height = window_height - y

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[0])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[0])

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[1])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[1])

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[2])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[2])
divs = self.get_subsampling_divs(pixel_format)
for texture, index in ((GL_TEXTURE0, 0), (GL_TEXTURE1, 1), (GL_TEXTURE2, 2)):
div = divs[index]
glActiveTexture(texture)
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[index])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[index])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/div, height/div, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[index])

drawable.gl_end()

def render_image(self):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()
log.info("render_image() size=%s, using %s and %s", self.size, drawable, context)
log("GL render_image() size=%s", self.size)
w, h = self.size
if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")
assert self.current_mode == GLPixmapBacking.MODE_YUV
glEnable(GL_FRAGMENT_PROGRAM_ARB)
glBegin(GL_QUADS);
glMultiTexCoord2i(GL_TEXTURE0, 0, 0);
glMultiTexCoord2i(GL_TEXTURE1, 0, 0);
glMultiTexCoord2i(GL_TEXTURE2, 0, 0);
glVertex2i(0, 0);

glMultiTexCoord2i(GL_TEXTURE0, 0, h);
glMultiTexCoord2i(GL_TEXTURE1, 0, h/2);
glMultiTexCoord2i(GL_TEXTURE2, 0, h/2);
glVertex2i(0, h);

glMultiTexCoord2i(GL_TEXTURE0, w, h);
glMultiTexCoord2i(GL_TEXTURE1, w/2, h/2);
glMultiTexCoord2i(GL_TEXTURE2, w/2, h/2);
glVertex2i(w, h);

glMultiTexCoord2i(GL_TEXTURE0, w, 0);
glMultiTexCoord2i(GL_TEXTURE1, w/2, 0);
glMultiTexCoord2i(GL_TEXTURE2, w/2, 0);
glVertex2i(w, 0);
divs = self.get_subsampling_divs(self.pixel_format)
for x,y in ((0, 0), (0, h), (w, h), (w, 0)):
for texture, index in ((GL_TEXTURE0, 0), (GL_TEXTURE1, 1), (GL_TEXTURE2, 2)):
div = divs[index]
glMultiTexCoord2i(texture, x/div, y/div)
glVertex2i(x, y)
glEnd()

drawable.swap_buffers()
Expand Down
6 changes: 6 additions & 0 deletions src/xpra/vpx/codec.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os
from libc.stdlib cimport free
from xpra.codec_constants import YUV420P

cdef extern from "Python.h":
ctypedef int Py_ssize_t
Expand Down Expand Up @@ -109,6 +110,11 @@ cdef class Decoder(xcoder):
strides = [outstrides[0], outstrides[1], outstrides[2]]
return i, strides, out

def get_pixel_format(self, csc_pixel_format):
#we only support 420 at present
assert csc_pixel_format==-1
return YUV420P

def decompress_image_to_rgb(self, input, options):
cdef uint8_t *yuvplanes[3]
cdef uint8_t *dout
Expand Down
4 changes: 4 additions & 0 deletions src/xpra/x264/codec.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ cdef extern from "x264lib.h":
int compress_image(x264lib_ctx *ctx, x264_picture_t *pic_in, uint8_t **out, int *outsz, int quality_override) nogil
int get_encoder_pixel_format(x264lib_ctx *ctx)
int get_encoder_quality(x264lib_ctx *ctx)
int get_pixel_format(int csc_format)

x264lib_ctx* init_decoder(int width, int height, int csc_fmt)
void set_decoder_csc_format(x264lib_ctx *context, int csc_fmt)
Expand Down Expand Up @@ -132,6 +133,9 @@ cdef class Decoder(xcoder):
strides = [outstrides[0], outstrides[1], outstrides[2]]
return i, strides, out

def get_pixel_format(self, csc_pixel_format):
return get_pixel_format(csc_pixel_format)

def decompress_image_to_rgb(self, input, options):
cdef uint8_t *yuvplanes[3]
cdef uint8_t *dout
Expand Down
18 changes: 16 additions & 2 deletions src/xpra/x264/x264lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,26 @@ int get_csc_format_for_x264_format(int i_csp)
return PIX_FMT_YUV444P;
#endif
else {
fprintf(stderr, "invalid pixel format: %i\n", i_csp);
return -1;
printf("invalid pixel format: %i", i_csp);
}
}
#endif

//Given a csc colour sampling constant,
//return our own generic csc constant (see codec_constants.py)
int get_pixel_format(int csc)
{
if (csc == PIX_FMT_YUV420P || csc < 0)
return 0;
else if (csc == PIX_FMT_YUV422P)
return 1;
else if (csc == PIX_FMT_YUV444P)
return 2;
else
return -1;
}

int get_csc_algo_for_quality(int initial_quality) {
//always use the best quality as lower quality options
//do not offer a significant speed improvement
Expand Down Expand Up @@ -343,7 +357,7 @@ int compress_image(struct x264lib_ctx *ctx, x264_picture_t *pic_in, uint8_t **ou
return 0;
}
#else
x264_picture_t* csc_image_rgb2yuv(struct x264lib_ctx *ctx, const uint8_t *in, int stride)
x264_picture_t* csc_image_rgb2yuv(struct x264lib_ctx *ctx, const uint8_t *in, int stride)
{
return NULL;
}
Expand Down
5 changes: 4 additions & 1 deletion src/xpra/x264/x264lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ int get_encoder_pixel_format(struct x264lib_ctx *ctx);
/** Expose current quality setting so we can tell the client how good the frame was */
int get_encoder_quality(struct x264lib_ctx *ctx);

/** Returns the pixel format using our own generic codec_constants */
int get_pixel_format(int csc);

/** Create an encoding context for images of a given size. */
struct x264lib_ctx *init_encoder(int width, int height, int initial_quality, int supports_csc_option);

Expand Down Expand Up @@ -80,7 +83,7 @@ int compress_image(struct x264lib_ctx *ctx, x264_picture_t *pic_in, uint8_t **ou
/** Decompress an image using the given context.
@param in: Input buffer, format is H264.
@param size: Input size.
@param out: Will be filled to point to the output data in planar YUV420 format (3 planes). This data will be freed automatically upon next call to the decoder.
@param out: Will be filled to point to the output data in planar YUV420 format (3 planes). This data will be freed automatically upon next call to the decoder.
@param outsize: Output size.
@param outstride: Output strides (3 planes).
*/
Expand Down

0 comments on commit e037f57

Please sign in to comment.