Skip to content

Commit

Permalink
#3457 add decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Feb 14, 2022
1 parent 4ca3f08 commit 5be3c6e
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 10 deletions.
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,9 @@ def nvcc_compile(cmd):
add_cython_ext("xpra.codecs.avif.encoder",
["xpra/codecs/avif/encoder.pyx"],
**avif_pkgconfig)
add_cython_ext("xpra.codecs.avif.decoder",
["xpra/codecs/avif/decoder.pyx"],
**avif_pkgconfig)


#swscale and avcodec2 use libav_common/av_log:
Expand Down
4 changes: 3 additions & 1 deletion xpra/client/mixins/encodings.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def get_core_encodings():
"""
#we always support rgb:
core_encodings = ["rgb24", "rgb32"]
for codec in ("dec_pillow", "dec_webp", "dec_jpeg"):
for codec in ("dec_pillow", "dec_webp", "dec_jpeg", "dec_avif"):
if has_codec(codec):
c = get_codec(codec)
encs = c.get_encodings()
Expand Down Expand Up @@ -107,6 +107,8 @@ def init(self, opts):
if "webp" in ae:
#try to load the fast webp decoder:
load_codec("dec_webp")
if "avif" in ae:
load_codec("dec_avif")
vh = getVideoHelper()
vh.set_modules(video_decoders=opts.video_decoders, csc_modules=opts.csc_modules or NO_GFX_CSC_OPTIONS)
vh.init()
Expand Down
12 changes: 12 additions & 0 deletions xpra/client/window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def __init__(self, wid : int, window_alpha : bool):
self.jpeg_decoder = get_codec("dec_jpeg")
self.webp_decoder = get_codec("dec_webp")
self.spng_decoder = get_codec("dec_spng")
self.avif_decoder = get_codec("dec_avif")
self.draw_needs_refresh = True
self.repaint_all = REPAINT_ALL
self.mmap = None
Expand Down Expand Up @@ -451,6 +452,15 @@ def do_paint_jpeg(self, rgb_format, img_data, x, y, width, height, options, call
self.idle_add(self.do_paint_rgb, rgb_format, img_data,
x, y, w, h, width, height, rowstride, options, callbacks)

def paint_avif(self, img_data, x, y, width, height, options, callbacks):
img = self.avif_decoder.decompress(img_data, options)
rgb_format = img.get_pixel_format()
img_data = img.get_pixels()
rowstride = img.get_rowstride()
w = img.get_width()
h = img.get_height()
self.idle_add(self.do_paint_rgb, rgb_format, img_data,
x, y, w, h, width, height, rowstride, options, callbacks)

def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
# can be called from any thread
Expand Down Expand Up @@ -806,6 +816,8 @@ def draw_region(self, x, y, width, height, coding, img_data, rowstride, options,
self.paint_jpeg(img_data, x, y, width, height, options, callbacks)
elif self.jpeg_decoder and coding=="jpega":
self.paint_jpega(img_data, x, y, width, height, options, callbacks)
elif self.avif_decoder and coding=="avif":
self.paint_avif(img_data, x, y, width, height, options, callbacks)
elif coding == "webp":
self.paint_webp(img_data, x, y, width, height, options, callbacks)
elif self.spng_decoder and coding=="png":
Expand Down
78 changes: 70 additions & 8 deletions xpra/codecs/avif/avif.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,6 @@ cdef extern from "avif/avif.h":
void avifImageFreePlanes(avifImage * image, uint32_t planes) #Ignores already-freed planes
void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, uint32_t planes)

ctypedef enum avifRGBFormat:
AVIF_RGB_FORMAT_RGB
AVIF_RGB_FORMAT_RGBA
AVIF_RGB_FORMAT_ARGB
AVIF_RGB_FORMAT_BGR
AVIF_RGB_FORMAT_BGRA
AVIF_RGB_FORMAT_ABGR

uint32_t avifRGBFormatChannelCount(avifRGBFormat format)
avifBool avifRGBFormatHasAlpha(avifRGBFormat format)

Expand Down Expand Up @@ -331,6 +323,76 @@ cdef extern from "avif/avif.h":
uint32_t addImageFlags)
avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output)

ctypedef struct avifDecoderData:
pass
enum avifDecoderSource:
AVIF_DECODER_SOURCE_AUTO
AVIF_DECODER_SOURCE_PRIMARY_ITEM
AVIF_DECODER_SOURCE_TRACKS
ctypedef struct avifImageTiming:
uint64_t timescale # timescale of the media (Hz)
double pts # presentation timestamp in seconds (ptsInTimescales / timescale)
uint64_t ptsInTimescales # presentation timestamp in "timescales"
double duration # in seconds (durationInTimescales / timescale)
uint64_t durationInTimescales # duration in "timescales"
ctypedef struct avifIOStats:
size_t colorOBUSize
size_t alphaOBUSize
ctypedef struct avifIO:
pass
ctypedef struct avifDecoder:
# Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
avifCodecChoice codecChoice
int maxThreads # Defaults to 1
# avifs can have multiple sets of images in them. This specifies which to decode.
# Set this via avifDecoderSetSource().
avifDecoderSource requestedSource

# All decoded image data; owned by the decoder. All information in this image is incrementally
# added and updated as avifDecoder*() functions are called. After a successful call to
# avifDecoderParse(), all values in decoder->image (other than the planes/rowBytes themselves)
# will be pre-populated with all information found in the outer AVIF container, prior to any
# AV1 decoding. If the contents of the inner AV1 payload disagree with the outer container,
# these values may change after calls to avifDecoderRead*(),avifDecoderNextImage(), or
# avifDecoderNthImage().
#
# The YUV and A contents of this image are likely owned by the decoder, so be sure to copy any
# data inside of this image before advancing to the next image or reusing the decoder. It is
# legal to call avifImageYUVToRGB() on this in between calls to avifDecoderNextImage(), but use
# avifImageCopy() if you want to make a complete, permanent copy of this image's YUV content or
# metadata.
avifImage * image
# Counts and timing for the current image in an image sequence. Uninteresting for single image files.
int imageIndex # 0-based
int imageCount # Always 1 for non-sequences
avifImageTiming imageTiming
uint64_t timescale # timescale of the media (Hz)
double duration # in seconds (durationInTimescales / timescale)
uint64_t durationInTimescales # duration in "timescales"
# This is true when avifDecoderParse() detects an alpha plane. Use this to find out if alpha is
# present after a successful call to avifDecoderParse(), but prior to any call to
# avifDecoderNextImage() or avifDecoderNthImage(), as decoder->image->alphaPlane won't exist yet.
avifBool alphaPresent
# Enable any of these to avoid reading and surfacing specific data to the decoded avifImage.
# These can be useful if your avifIO implementation heavily uses AVIF_RESULT_WAITING_ON_IO for
# streaming data, as some of these payloads are (unfortunately) packed at the end of the file,
# which will cause avifDecoderParse() to return AVIF_RESULT_WAITING_ON_IO until it finds them.
# If you don't actually leverage this data, it is best to ignore it here.
avifBool ignoreExif
avifBool ignoreXMP
# stats from the most recent read, possibly 0s if reading an image sequence
avifIOStats ioStats
# Use one of the avifDecoderSetIO*() functions to set this
avifIO * io
# Internals used by the decoder
avifDecoderData * data

avifDecoder * avifDecoderCreate()
avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size)
avifResult avifDecoderParse(avifDecoder * decoder)
avifResult avifDecoderNextImage(avifDecoder * decoder)
void avifDecoderDestroy(avifDecoder * decoder)

ctypedef enum avifResult:
AVIF_RESULT_OK
AVIF_RESULT_UNKNOWN_ERROR
Expand Down
2 changes: 1 addition & 1 deletion xpra/codecs/avif/encoder.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def encode(coding, image, options=None):
log("avifEncoderFinish()=%i", r)
check(r, "Failed to finish encode")

client_options = {}
client_options = {"alpha" : pixel_format.find("A")>=0}
cdata = avifOutput.data[:avifOutput.size]
log("avif: got %i bytes", avifOutput.size)
may_save_image("avif", cdata)
Expand Down

0 comments on commit 5be3c6e

Please sign in to comment.