mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-06 20:27:43 +00:00
Initialize _kill_mark so we don't get spurious valgrind warnings
This commit is contained in:
parent
18485cef06
commit
de4306af33
@ -264,6 +264,7 @@ VideoStream::VideoStream(RedClient& client, Canvas& canvas, DisplayChannel& chan
|
||||
, _clip (NULL)
|
||||
, _frames_head (0)
|
||||
, _frames_tail (0)
|
||||
, _kill_mark (0)
|
||||
, _uncompressed_data (NULL)
|
||||
, _update_mark (0)
|
||||
, _update_time (0)
|
||||
|
||||
@ -113,13 +113,15 @@ void MJpegDecoder::convert_scanline(void)
|
||||
|
||||
row = (uint32_t *)(_frame + _y * _stride);
|
||||
s = _scanline;
|
||||
|
||||
/* TODO after major bump.
|
||||
We need to check for the old major and for backwards compat
|
||||
a) swap r and b
|
||||
b) to-yuv with right values and then from-yuv with old wrong values
|
||||
*/
|
||||
|
||||
for (x = 0; x < _width; x++) {
|
||||
/* This switches the order of red and blue.
|
||||
This is not accidental, but for backwards
|
||||
compatibility, since a bug in the old
|
||||
ffmpeg-using mjpeg code got these switched
|
||||
due to endianness issues. */
|
||||
c = s[0] | s[1] << 8 | s[2] << 16;
|
||||
c = s[0] << 16 | s[1] << 8 | s[2];
|
||||
s += 3;
|
||||
*row++ = c;
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
|
||||
#ifdef __GNUG__
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "@AVCODEC_PREFIX@avcodec.h"
|
||||
|
||||
#ifdef __GNUG__
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
|
||||
extern "C" {
|
||||
#include "libavcodec/avcodec.h"
|
||||
}
|
||||
14
configure.ac
14
configure.ac
@ -144,19 +144,6 @@ AC_SUBST(CELT051_LIBS)
|
||||
AC_SUBST(CELT051_LIBDIR)
|
||||
SPICE_REQUIRES+=" celt051 >= 0.5.1.1"
|
||||
|
||||
PKG_CHECK_MODULES(FFMPEG, libavcodec libavutil)
|
||||
AC_SUBST(FFMPEG_CFLAGS)
|
||||
AC_SUBST(FFMPEG_LIBS)
|
||||
FFMPEG_LIBDIR=`pkg-config --variable=libdir libavutil`
|
||||
AC_SUBST(FFMPEG_LIBDIR)
|
||||
SPICE_REQUIRES+=" libavcodec libavutil"
|
||||
|
||||
saved_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $FFMPEG_CFLAGS"
|
||||
AC_CHECK_HEADER(avcodec.h, AVCODEC_PREFIX="", AVCODEC_PREFIX="libavcodec/")
|
||||
CPPFLAGS="$saved_CPPFLAGS"
|
||||
AC_SUBST([AVCODEC_PREFIX])
|
||||
|
||||
PKG_CHECK_MODULES(ALSA, alsa)
|
||||
AC_SUBST(ALSA_CFLAGS)
|
||||
AC_SUBST(ALSA_LIBS)
|
||||
@ -349,7 +336,6 @@ AC_OUTPUT([
|
||||
Makefile
|
||||
spice-server.pc
|
||||
common/Makefile
|
||||
common/linux/ffmpeg_inc.h
|
||||
server/Makefile
|
||||
client/Makefile
|
||||
client/x11/Makefile
|
||||
|
||||
@ -5,7 +5,7 @@ INCLUDES = \
|
||||
-I$(top_srcdir)/common \
|
||||
-I$(top_srcdir)/common/linux \
|
||||
$(PROTOCOL_CFLAGS) \
|
||||
$(FFMPEG_CFLAGS) \
|
||||
$(JPEG_CFLAGS) \
|
||||
$(PIXMAN_CFLAGS) \
|
||||
$(GL_CFLAGS) \
|
||||
$(LOG4CPP_CFLAGS) \
|
||||
@ -42,7 +42,7 @@ libspice_server_la_LDFLAGS = \
|
||||
|
||||
libspice_server_la_LIBADD = \
|
||||
$(GL_LIBS) \
|
||||
$(FFMPEG_LIBS) \
|
||||
$(JPEG_LIBS) \
|
||||
$(PIXMAN_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(CELT051_LIBS) \
|
||||
@ -66,6 +66,8 @@ libspice_server_la_SOURCES = \
|
||||
glz_encoder_dictionary.h \
|
||||
glz_encoder_dictionary_protected.h \
|
||||
glz_encoder.h \
|
||||
mjpeg_encoder.h \
|
||||
mjpeg_encoder.c \
|
||||
red_bitmap_utils.h \
|
||||
red_client_cache.h \
|
||||
red_client_shared_cache.h \
|
||||
@ -77,7 +79,6 @@ libspice_server_la_SOURCES = \
|
||||
stat.h \
|
||||
red_worker.c \
|
||||
red_worker.h \
|
||||
red_yuv.h \
|
||||
snd_worker.c \
|
||||
snd_worker.h \
|
||||
red_channel.h \
|
||||
|
||||
125
server/mjpeg_encoder.c
Normal file
125
server/mjpeg_encoder.c
Normal file
@ -0,0 +1,125 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "red_common.h"
|
||||
#include "mjpeg_encoder.h"
|
||||
#include <jpeglib.h>
|
||||
|
||||
struct MJpegEncoder {
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
uint8_t *frame;
|
||||
int first_frame;
|
||||
int quality;
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
};
|
||||
|
||||
MJpegEncoder *mjpeg_encoder_new(int width, int height)
|
||||
{
|
||||
MJpegEncoder *enc;
|
||||
|
||||
enc = spice_new0(MJpegEncoder, 1);
|
||||
|
||||
enc->first_frame = TRUE;
|
||||
enc->width = width;
|
||||
enc->height = height;
|
||||
enc->stride = width * 3;
|
||||
enc->quality = 70;
|
||||
if (enc->stride < width) {
|
||||
abort();
|
||||
}
|
||||
enc->frame = spice_malloc_n(enc->stride, height);
|
||||
|
||||
enc->cinfo.err = jpeg_std_error(&enc->jerr);
|
||||
|
||||
jpeg_create_compress(&enc->cinfo);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
void mjpeg_encoder_destroy(MJpegEncoder *encoder)
|
||||
{
|
||||
jpeg_destroy_compress(&encoder->cinfo);
|
||||
free(encoder->frame);
|
||||
free(encoder);
|
||||
}
|
||||
|
||||
uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder)
|
||||
{
|
||||
return encoder->frame;
|
||||
}
|
||||
size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
|
||||
{
|
||||
return encoder->stride;
|
||||
}
|
||||
|
||||
void init_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
boolean empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void term_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
|
||||
uint8_t *buffer, size_t buffer_len)
|
||||
{
|
||||
struct jpeg_destination_mgr destmgr;
|
||||
uint8_t *frame;
|
||||
int n;
|
||||
|
||||
destmgr.next_output_byte = buffer;
|
||||
destmgr.free_in_buffer = buffer_len;
|
||||
destmgr.init_destination = init_destination;
|
||||
destmgr.empty_output_buffer = empty_output_buffer;
|
||||
destmgr.term_destination = term_destination;
|
||||
|
||||
encoder->cinfo.dest = &destmgr;
|
||||
|
||||
encoder->cinfo.image_width = encoder->width;
|
||||
encoder->cinfo.image_height = encoder->height;
|
||||
encoder->cinfo.input_components = 3;
|
||||
encoder->cinfo.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(&encoder->cinfo);
|
||||
jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
|
||||
jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
|
||||
|
||||
frame = encoder->frame;
|
||||
while (encoder->cinfo.next_scanline < encoder->cinfo.image_height) {
|
||||
n = jpeg_write_scanlines(&encoder->cinfo, &frame, 1);
|
||||
if (n == 0) { /* Not enough space */
|
||||
jpeg_abort_compress(&encoder->cinfo);
|
||||
return 0;
|
||||
}
|
||||
frame += encoder->stride;
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&encoder->cinfo);
|
||||
|
||||
encoder->first_frame = FALSE;
|
||||
return destmgr.next_output_byte - buffer;
|
||||
}
|
||||
35
server/mjpeg_encoder.h
Normal file
35
server/mjpeg_encoder.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_MJPEG_ENCODER
|
||||
#define _H_MJPEG_ENCODER
|
||||
|
||||
#include "red_common.h"
|
||||
|
||||
typedef struct MJpegEncoder MJpegEncoder;
|
||||
|
||||
MJpegEncoder *mjpeg_encoder_new(int width, int height);
|
||||
void mjpeg_encoder_destroy(MJpegEncoder *encoder);
|
||||
|
||||
uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder);
|
||||
size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder);
|
||||
int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
|
||||
uint8_t *buffer, size_t buffer_len);
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,3 +1,4 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
@ -38,7 +39,6 @@
|
||||
#include "cairo_canvas.h"
|
||||
#include "gl_canvas.h"
|
||||
#include "ogl_ctx.h"
|
||||
#include "ffmpeg_inc.h"
|
||||
#include "quic.h"
|
||||
#include "lz.h"
|
||||
#include "glz_encoder_dictionary.h"
|
||||
@ -46,6 +46,7 @@
|
||||
#include "stat.h"
|
||||
#include "reds.h"
|
||||
#include "ring.h"
|
||||
#include "mjpeg_encoder.h"
|
||||
|
||||
//#define COMPRESS_STAT
|
||||
//#define DUMP_BITMAP
|
||||
@ -412,13 +413,10 @@ struct Stream {
|
||||
int height;
|
||||
SpiceRect dest_area;
|
||||
#endif
|
||||
MJpegEncoder *mjpeg_encoder;
|
||||
int top_down;
|
||||
Stream *next;
|
||||
RingItem link;
|
||||
AVCodecContext *av_ctx;
|
||||
AVFrame *av_frame;
|
||||
uint8_t* frame_buf;
|
||||
uint8_t* frame_buf_end;
|
||||
int bit_rate;
|
||||
};
|
||||
|
||||
@ -1017,8 +1015,6 @@ typedef struct RedWorker {
|
||||
SpiceVirtMapping preload_group_virt_mapping;
|
||||
} RedWorker;
|
||||
|
||||
pthread_mutex_t avcodec_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
|
||||
static void red_current_flush(RedWorker *worker, int surface_id);
|
||||
static void display_channel_push(RedWorker *worker);
|
||||
@ -2486,12 +2482,9 @@ static void red_release_stream(RedWorker *worker, Stream *stream)
|
||||
#else
|
||||
ring_remove(&stream->link);
|
||||
#endif
|
||||
pthread_mutex_lock(&avcodec_lock);
|
||||
avcodec_close(stream->av_ctx);
|
||||
pthread_mutex_unlock(&avcodec_lock);
|
||||
av_free(stream->av_ctx);
|
||||
av_free(stream->av_frame);
|
||||
free(stream->frame_buf);
|
||||
if (stream->mjpeg_encoder) {
|
||||
mjpeg_encoder_destroy(stream->mjpeg_encoder);
|
||||
}
|
||||
red_free_stream(worker, stream);
|
||||
}
|
||||
}
|
||||
@ -2814,7 +2807,7 @@ static int get_bit_rate(int width, int height)
|
||||
return bit_rate;
|
||||
}
|
||||
|
||||
static void red_dispaly_create_stream(DisplayChannel *display, Stream *stream)
|
||||
static void red_display_create_stream(DisplayChannel *display, Stream *stream)
|
||||
{
|
||||
StreamAgent *agent = &display->stream_agents[stream - display->base.worker->streams_buf];
|
||||
stream->refs++;
|
||||
@ -2831,54 +2824,12 @@ static void red_dispaly_create_stream(DisplayChannel *display, Stream *stream)
|
||||
red_pipe_add(&display->base, &agent->create_item);
|
||||
}
|
||||
|
||||
static AVCodecContext *red_init_video_encoder(int width, int height)
|
||||
{
|
||||
AVCodec *codec;
|
||||
AVCodecContext *ctx;
|
||||
int r;
|
||||
|
||||
codec = avcodec_find_encoder(CODEC_ID_MJPEG);
|
||||
|
||||
if (!codec) {
|
||||
red_printf("codec not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ctx = avcodec_alloc_context())) {
|
||||
red_printf("alloc ctx failed");
|
||||
return NULL;
|
||||
}
|
||||
ctx->bit_rate = get_bit_rate(width, height);
|
||||
ASSERT(width % 2 == 0);
|
||||
ASSERT(height % 2 == 0);
|
||||
ctx->width = width;
|
||||
ctx->height = height;
|
||||
ctx->time_base = (AVRational){1, MAX_FPS};
|
||||
ctx->gop_size = 10;
|
||||
ctx->max_b_frames = 0;
|
||||
ctx->pix_fmt = PIX_FMT_YUVJ420P;
|
||||
|
||||
pthread_mutex_lock(&avcodec_lock);
|
||||
r = avcodec_open(ctx, codec);
|
||||
pthread_mutex_unlock(&avcodec_lock);
|
||||
if (r < 0) {
|
||||
red_printf("avcodec open failed");
|
||||
av_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void red_create_stream(RedWorker *worker, Drawable *drawable)
|
||||
{
|
||||
AVCodecContext *av_ctx;
|
||||
Stream *stream;
|
||||
AVFrame *frame;
|
||||
uint8_t* frame_buf;
|
||||
SpiceRect* src_rect;
|
||||
int stream_width;
|
||||
int stream_height;
|
||||
int pict_size;
|
||||
|
||||
ASSERT(!drawable->stream);
|
||||
|
||||
@ -2891,24 +2842,7 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
|
||||
stream_width = SPICE_ALIGN(src_rect->right - src_rect->left, 2);
|
||||
stream_height = SPICE_ALIGN(src_rect->bottom - src_rect->top, 2);
|
||||
|
||||
if (!(av_ctx = red_init_video_encoder(stream_width, stream_height))) {
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
if (!(frame = avcodec_alloc_frame())) {
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
if ((pict_size = avpicture_get_size(av_ctx->pix_fmt, stream_width, stream_height)) < 0) {
|
||||
goto error_3;
|
||||
}
|
||||
|
||||
frame_buf = spice_malloc(pict_size);
|
||||
|
||||
if (avpicture_fill((AVPicture *)frame, frame_buf, av_ctx->pix_fmt, stream_width,
|
||||
stream_height) < 0) {
|
||||
goto error_4;
|
||||
}
|
||||
stream->mjpeg_encoder = mjpeg_encoder_new(stream_width, stream_height);
|
||||
|
||||
ring_add(&worker->streams, &stream->link);
|
||||
stream->current = drawable;
|
||||
@ -2919,36 +2853,17 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
|
||||
stream->dest_area = drawable->qxl_drawable->bbox;
|
||||
#endif
|
||||
stream->refs = 1;
|
||||
stream->av_ctx = av_ctx;
|
||||
stream->bit_rate = av_ctx->bit_rate;
|
||||
stream->av_frame = frame;
|
||||
stream->frame_buf = frame_buf;
|
||||
stream->frame_buf_end = frame_buf + pict_size;
|
||||
stream->bit_rate = get_bit_rate(stream_width, stream_height);
|
||||
QXLImage *qxl_image = (QXLImage *)get_virt(worker, drawable->qxl_drawable->u.copy.src_bitmap,
|
||||
sizeof(QXLImage), drawable->group_id);
|
||||
stream->top_down = !!(qxl_image->bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
|
||||
drawable->stream = stream;
|
||||
|
||||
if (worker->display_channel) {
|
||||
red_dispaly_create_stream(worker->display_channel, stream);
|
||||
red_display_create_stream(worker->display_channel, stream);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error_4:
|
||||
free(frame_buf);
|
||||
|
||||
error_3:
|
||||
av_free(frame);
|
||||
|
||||
error_2:
|
||||
pthread_mutex_lock(&avcodec_lock);
|
||||
avcodec_close(av_ctx);
|
||||
pthread_mutex_unlock(&avcodec_lock);
|
||||
av_free(av_ctx);
|
||||
|
||||
error_1:
|
||||
red_free_stream(worker, stream);
|
||||
}
|
||||
|
||||
static void red_disply_start_streams(DisplayChannel *display_channel)
|
||||
@ -2958,7 +2873,7 @@ static void red_disply_start_streams(DisplayChannel *display_channel)
|
||||
|
||||
while ((item = ring_next(ring, item))) {
|
||||
Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
|
||||
red_dispaly_create_stream(display_channel, stream);
|
||||
red_display_create_stream(display_channel, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3100,7 +3015,6 @@ static inline int red_is_next_stream_frame(RedWorker *worker, Drawable *candidat
|
||||
static void reset_rate(StreamAgent *stream_agent)
|
||||
{
|
||||
Stream *stream = stream_agent->stream;
|
||||
AVCodecContext *new_ctx;
|
||||
int rate;
|
||||
|
||||
rate = get_bit_rate(stream->width, stream->height);
|
||||
@ -3108,19 +3022,7 @@ static void reset_rate(StreamAgent *stream_agent)
|
||||
return;
|
||||
}
|
||||
|
||||
int stream_width = SPICE_ALIGN(stream->width, 2);
|
||||
int stream_height = SPICE_ALIGN(stream->height, 2);
|
||||
|
||||
new_ctx = red_init_video_encoder(stream_width, stream_height);
|
||||
if (!new_ctx) {
|
||||
red_printf("craete ctx failed");
|
||||
return;
|
||||
}
|
||||
|
||||
avcodec_close(stream->av_ctx);
|
||||
av_free(stream->av_ctx);
|
||||
stream->av_ctx = new_ctx;
|
||||
stream->bit_rate = rate;
|
||||
/* MJpeg has no rate limiting anyway, so do nothing */
|
||||
}
|
||||
|
||||
static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
|
||||
@ -7058,17 +6960,148 @@ static void red_display_unshare_stream_buf(DisplayChannel *display_channel)
|
||||
{
|
||||
}
|
||||
|
||||
#define YUV32
|
||||
#include "red_yuv.h"
|
||||
#undef YUV32
|
||||
static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
|
||||
const SpiceBitmap *image,
|
||||
uint8_t *frame, size_t frame_stride,
|
||||
long phys_delta, int memslot_id, int id,
|
||||
Stream *stream, uint32_t group_id)
|
||||
{
|
||||
QXLDataChunk *chunk;
|
||||
uint32_t image_stride;
|
||||
uint8_t *frame_row;
|
||||
int offset;
|
||||
int i, x;
|
||||
|
||||
#define YUV24
|
||||
#include "red_yuv.h"
|
||||
#undef YUV24
|
||||
offset = 0;
|
||||
chunk = (QXLDataChunk *)(image->data + phys_delta);
|
||||
image_stride = image->stride;
|
||||
|
||||
#define YUV16
|
||||
#include "red_yuv.h"
|
||||
#undef YUV16
|
||||
const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
|
||||
for (i = 0; i < skip_lines; i++) {
|
||||
red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
|
||||
group_id);
|
||||
}
|
||||
|
||||
const int image_height = src->bottom - src->top;
|
||||
const int image_width = src->right - src->left;
|
||||
for (i = 0; i < image_height; i++) {
|
||||
uint32_t *src_line =
|
||||
(uint32_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
|
||||
if (!src_line) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
src_line += src->left;
|
||||
|
||||
frame_row = frame;
|
||||
for (x = 0; x < image_width; x++) {
|
||||
uint32_t pixel = *src_line++;
|
||||
*frame_row++ = (pixel >> 16) & 0xff;
|
||||
*frame_row++ = (pixel >> 8) & 0xff;
|
||||
*frame_row++ = (pixel >> 0) & 0xff;
|
||||
}
|
||||
|
||||
frame += frame_stride;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
|
||||
const SpiceBitmap *image,
|
||||
uint8_t *frame, size_t frame_stride,
|
||||
long phys_delta, int memslot_id, int id,
|
||||
Stream *stream, uint32_t group_id)
|
||||
{
|
||||
QXLDataChunk *chunk;
|
||||
uint32_t image_stride;
|
||||
uint8_t *frame_row;
|
||||
int offset;
|
||||
int i;
|
||||
|
||||
offset = 0;
|
||||
chunk = (QXLDataChunk *)(image->data + phys_delta);
|
||||
image_stride = image->stride;
|
||||
|
||||
const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
|
||||
for (i = 0; i < skip_lines; i++) {
|
||||
red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
|
||||
group_id);
|
||||
}
|
||||
|
||||
const int image_height = src->bottom - src->top;
|
||||
const int image_width = src->right - src->left;
|
||||
for (i = 0; i < image_height; i++) {
|
||||
uint8_t *src_line =
|
||||
(uint8_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
|
||||
if (!src_line) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
src_line += src->left * 3;
|
||||
|
||||
frame_row = frame;
|
||||
memcpy (frame_row, src_line, image_width * 3);
|
||||
frame += frame_stride;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int red_rgb16bpp_to_24 (RedWorker *worker, const SpiceRect *src,
|
||||
const SpiceBitmap *image,
|
||||
uint8_t *frame, size_t frame_stride,
|
||||
long phys_delta, int memslot_id, int id,
|
||||
Stream *stream, uint32_t group_id)
|
||||
{
|
||||
QXLDataChunk *chunk;
|
||||
uint32_t image_stride;
|
||||
uint8_t *frame_row;
|
||||
int offset;
|
||||
int i, x;
|
||||
|
||||
offset = 0;
|
||||
chunk = (QXLDataChunk *)(image->data + phys_delta);
|
||||
image_stride = image->stride;
|
||||
|
||||
const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
|
||||
for (i = 0; i < skip_lines; i++) {
|
||||
red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
|
||||
group_id);
|
||||
}
|
||||
|
||||
const int image_height = src->bottom - src->top;
|
||||
const int image_width = src->right - src->left;
|
||||
for (i = 0; i < image_height; i++) {
|
||||
uint16_t *src_line =
|
||||
(uint16_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
|
||||
if (!src_line) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
src_line += src->left;
|
||||
|
||||
frame_row = frame;
|
||||
for (x = 0; x < image_width; x++) {
|
||||
uint16_t pixel = *src_line++;
|
||||
*frame_row++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
|
||||
*frame_row++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
|
||||
*frame_row++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
|
||||
}
|
||||
|
||||
frame += frame_stride;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define PADDING 8 /* old ffmpeg padding */
|
||||
|
||||
static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable *drawable)
|
||||
{
|
||||
@ -7077,6 +7110,8 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
|
||||
RedChannel *channel;
|
||||
RedWorker* worker;
|
||||
unsigned long data;
|
||||
uint8_t *frame;
|
||||
size_t frame_stride;
|
||||
int n;
|
||||
|
||||
ASSERT(stream);
|
||||
@ -7088,7 +7123,7 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
|
||||
sizeof(QXLImage), drawable->group_id);
|
||||
|
||||
if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP ||
|
||||
(qxl_image->bitmap.flags & QXL_BITMAP_DIRECT)) {
|
||||
(qxl_image->bitmap.flags & QXL_BITMAP_DIRECT)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -7100,32 +7135,34 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
|
||||
}
|
||||
|
||||
data = qxl_image->bitmap.data;
|
||||
frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
|
||||
frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
|
||||
|
||||
switch (qxl_image->bitmap.format) {
|
||||
case SPICE_BITMAP_FMT_32BIT:
|
||||
if (!red_rgb_to_yuv420_32bpp(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, stream->av_frame,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
if (!red_rgb32bpp_to_24(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, frame, frame_stride,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SPICE_BITMAP_FMT_16BIT:
|
||||
if (!red_rgb_to_yuv420_16bpp(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, stream->av_frame,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
if (!red_rgb16bpp_to_24(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, frame, frame_stride,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SPICE_BITMAP_FMT_24BIT:
|
||||
if (!red_rgb_to_yuv420_24bpp(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, stream->av_frame,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
if (!red_rgb24bpp_to_24(worker, &drawable->qxl_drawable->u.copy.src_area,
|
||||
&qxl_image->bitmap, frame, frame_stride,
|
||||
get_virt_delta(worker, data, drawable->group_id),
|
||||
get_memslot_id(worker, data),
|
||||
stream - worker->streams_buf, stream, drawable->group_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
@ -7133,75 +7170,34 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
|
||||
red_printf_some(1000, "unsupported format %d", qxl_image->bitmap.format);
|
||||
return FALSE;
|
||||
}
|
||||
#if 1
|
||||
uint32_t min_buf_size = stream->av_ctx->width * stream->av_ctx->height * sizeof(uint32_t) / 2;
|
||||
min_buf_size += FF_MIN_BUFFER_SIZE;
|
||||
if (display_channel->send_data.stream_outbuf_size < min_buf_size) {
|
||||
uint8_t *new_buf;
|
||||
|
||||
new_buf = spice_malloc(min_buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
while ((n = mjpeg_encoder_encode_frame(stream->mjpeg_encoder,
|
||||
display_channel->send_data.stream_outbuf,
|
||||
display_channel->send_data.stream_outbuf_size - PADDING)) == 0) {
|
||||
uint8_t *new_buf;
|
||||
size_t new_size;
|
||||
|
||||
new_size = display_channel->send_data.stream_outbuf_size * 2;
|
||||
new_buf = spice_malloc(new_size);
|
||||
|
||||
red_display_unshare_stream_buf(display_channel);
|
||||
free(display_channel->send_data.stream_outbuf);
|
||||
display_channel->send_data.stream_outbuf = new_buf;
|
||||
display_channel->send_data.stream_outbuf_size = min_buf_size;
|
||||
display_channel->send_data.stream_outbuf_size = new_size;
|
||||
red_display_share_stream_buf(display_channel);
|
||||
}
|
||||
n = avcodec_encode_video(stream->av_ctx, display_channel->send_data.stream_outbuf,
|
||||
display_channel->send_data.stream_outbuf_size,
|
||||
stream->av_frame);
|
||||
|
||||
if (n <= 0) {
|
||||
red_printf("avcodec_encode_video failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (n > display_channel->send_data.stream_outbuf_size) {
|
||||
PANIC("buf error");
|
||||
}
|
||||
#else
|
||||
for (;;) {
|
||||
n = avcodec_encode_video(stream->av_ctx, display_channel->send_data.stream_outbuf,
|
||||
display_channel->send_data.stream_outbuf_size,
|
||||
stream->av_frame);
|
||||
if (n < 0) {
|
||||
uint8_t *new_buf;
|
||||
size_t max_size;
|
||||
size_t new_size;
|
||||
|
||||
max_size = stream->av_ctx->width * stream->av_ctx->height * sizeof(uint32_t);
|
||||
max_size += MAX(1024, max_size / 10);
|
||||
|
||||
if (display_channel->send_data.stream_outbuf_size == max_size) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
new_size = display_channel->send_data.stream_outbuf_size * 2;
|
||||
new_size = MIN(new_size, max_size);
|
||||
|
||||
new_buf = spice_malloc(new_size + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
red_printf("new streaming video buf size %u", new_size);
|
||||
red_display_unshare_stream_buf(display_channel);
|
||||
free(display_channel->send_data.stream_outbuf);
|
||||
display_channel->send_data.stream_outbuf = new_buf;
|
||||
display_channel->send_data.stream_outbuf_size = new_size;
|
||||
red_display_share_stream_buf(display_channel);
|
||||
continue;
|
||||
}
|
||||
ASSERT(n <= display_channel->send_data.stream_outbuf_size);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
channel->send_data.header.type = SPICE_MSG_DISPLAY_STREAM_DATA;
|
||||
|
||||
SpiceMsgDisplayStreamData* stream_data = &display_channel->send_data.u.stream_data;
|
||||
add_buf(channel, BUF_TYPE_RAW, stream_data, sizeof(SpiceMsgDisplayStreamData), 0, 0);
|
||||
add_buf(channel, BUF_TYPE_RAW, display_channel->send_data.stream_outbuf,
|
||||
n + FF_INPUT_BUFFER_PADDING_SIZE, 0, 0);
|
||||
n + PADDING, 0, 0);
|
||||
|
||||
stream_data->id = stream - worker->streams_buf;
|
||||
stream_data->multi_media_time = drawable->qxl_drawable->mm_time;
|
||||
stream_data->data_size = n;
|
||||
stream_data->ped_size = FF_INPUT_BUFFER_PADDING_SIZE;
|
||||
stream_data->ped_size = PADDING;
|
||||
display_begin_send_massage(display_channel, NULL);
|
||||
agent->lats_send_time = time_now;
|
||||
return TRUE;
|
||||
@ -8797,9 +8793,9 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
|
||||
display_channel->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
|
||||
red_display_init_streams(display_channel);
|
||||
|
||||
stream_buf_size = FF_MIN_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE;
|
||||
stream_buf_size = 32*1024;
|
||||
display_channel->send_data.stream_outbuf = spice_malloc(stream_buf_size);
|
||||
display_channel->send_data.stream_outbuf_size = FF_MIN_BUFFER_SIZE;
|
||||
display_channel->send_data.stream_outbuf_size = stream_buf_size;
|
||||
red_display_share_stream_buf(display_channel);
|
||||
red_display_init_glz_data(display_channel);
|
||||
worker->display_channel = display_channel;
|
||||
@ -9571,8 +9567,6 @@ void *red_worker_main(void *arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
avcodec_init();
|
||||
avcodec_register_all();
|
||||
red_init(&worker, (WorkerInitData *)arg);
|
||||
red_init_quic(&worker);
|
||||
red_init_lz(&worker);
|
||||
|
||||
159
server/red_yuv.h
159
server/red_yuv.h
@ -1,159 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(YUV32)
|
||||
#define PIXEL_SIZE 4
|
||||
#define R(pixel) (((uint8_t*)(pixel))[0])
|
||||
#define G(pixel) (((uint8_t*)(pixel))[1])
|
||||
#define B(pixel) (((uint8_t*)(pixel))[2])
|
||||
|
||||
#define FUNC_NAME(name) name##_32bpp
|
||||
|
||||
#elif defined(YUV24)
|
||||
#define PIXEL_SIZE 3
|
||||
#define R(pixel) (((uint8_t*)(pixel))[0])
|
||||
#define G(pixel) (((uint8_t*)(pixel))[1])
|
||||
#define B(pixel) (((uint8_t*)(pixel))[2])
|
||||
|
||||
#define FUNC_NAME(name) name##_24bpp
|
||||
|
||||
#elif defined(YUV16)
|
||||
#define PIXEL_SIZE 2
|
||||
#define PIX16(pixel) (*(uint16_t*)(pixel))
|
||||
|
||||
#define R(pixel) ((PIX16(pixel) << 3) & 0xff)
|
||||
#define G(pixel) ((PIX16(pixel) >> 2) & 0xff)
|
||||
#define B(pixel) ((PIX16(pixel) >> 7) & 0xff)
|
||||
|
||||
#define FUNC_NAME(name) name##_16bpp
|
||||
|
||||
#else
|
||||
#error "invalid format."
|
||||
#endif
|
||||
|
||||
#define Y(pixel) (((66 * R(pixel) + 129 * G(pixel) + 25 * B(pixel) + 128) >> 8) + 16)
|
||||
#define U(pixel) (((-38 * R(pixel) - 74 * G(pixel) + 112 * B(pixel) + 128) >> 8) + 128)
|
||||
#define V(pixel) (((112 * R(pixel) - 94 * G(pixel) - 18 * B(pixel) + 128) >> 8) + 128)
|
||||
|
||||
static inline void FUNC_NAME(red_rgb_to_yuv420_line)(const uint8_t* line0, const uint8_t* line1,
|
||||
const uint32_t width, uint8_t* y, uint8_t *u,
|
||||
uint8_t *v, int y_stride)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
|
||||
// Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
|
||||
// Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
|
||||
|
||||
for (i = 0; i < width / 2; i++) {
|
||||
*y = Y(line0);
|
||||
*(y + 1) = Y(line0 + PIXEL_SIZE);
|
||||
*(y + y_stride) = Y(line1);
|
||||
*(y + y_stride + 1) = Y(line1 + PIXEL_SIZE);
|
||||
|
||||
u[i] = (U(line0) + U(line0 + PIXEL_SIZE) + U(line1) + U(line1 + PIXEL_SIZE)) / 4;
|
||||
v[i] = (V(line0) + V(line0 + PIXEL_SIZE) + V(line1) + V(line1 + PIXEL_SIZE)) / 4;
|
||||
|
||||
line0 += 2 * PIXEL_SIZE;
|
||||
line1 += 2 * PIXEL_SIZE;
|
||||
y += 2;
|
||||
}
|
||||
|
||||
if ((width & 1)) {
|
||||
*y = Y(line0);
|
||||
*(y + 1) = *y;
|
||||
*(y + y_stride) = Y(line1);
|
||||
*(y + y_stride + 1) = *(y + y_stride);
|
||||
u[i] = (U(line0) + U(line1)) / 2;
|
||||
v[i] = (V(line0) + V(line1)) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int FUNC_NAME(red_rgb_to_yuv420)(RedWorker *worker, const SpiceRect *src,
|
||||
const SpiceBitmap *image, AVFrame *frame,
|
||||
long phys_delta, int memslot_id, int id,
|
||||
Stream *stream, uint32_t group_id)
|
||||
{
|
||||
QXLDataChunk *chunk;
|
||||
uint32_t image_stride;
|
||||
int y_stride;
|
||||
uint8_t* y;
|
||||
uint8_t* u;
|
||||
uint8_t* v;
|
||||
int offset;
|
||||
int i;
|
||||
|
||||
y = frame->data[0];
|
||||
u = frame->data[1];
|
||||
v = frame->data[2];
|
||||
y_stride = frame->linesize[0];
|
||||
|
||||
offset = 0;
|
||||
chunk = (QXLDataChunk *)(image->data + phys_delta);
|
||||
image_stride = image->stride;
|
||||
|
||||
const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
|
||||
for (i = 0; i < skip_lines; i++) {
|
||||
red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
|
||||
group_id);
|
||||
}
|
||||
|
||||
const int image_hight = src->bottom - src->top;
|
||||
const int image_width = src->right - src->left;
|
||||
for (i = 0; i < image_hight / 2; i++) {
|
||||
uint8_t* line0 = red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
uint8_t* line1 = red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
|
||||
if (!line0 || !line1) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
line0 += src->left * PIXEL_SIZE;
|
||||
line1 += src->left * PIXEL_SIZE;
|
||||
|
||||
FUNC_NAME(red_rgb_to_yuv420_line)(line0, line1, image_width, y, u, v, y_stride);
|
||||
|
||||
y += 2 * y_stride;
|
||||
u += frame->linesize[1];
|
||||
v += frame->linesize[2];
|
||||
}
|
||||
|
||||
if ((image_hight & 1)) {
|
||||
uint8_t* line = red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
|
||||
memslot_id, group_id);
|
||||
if (!line) {
|
||||
return FALSE;
|
||||
}
|
||||
line += src->left * PIXEL_SIZE;
|
||||
FUNC_NAME(red_rgb_to_yuv420_line)(line, line, image_width, y, u, v, y_stride);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#undef R
|
||||
#undef G
|
||||
#undef B
|
||||
#undef Y
|
||||
#undef U
|
||||
#undef V
|
||||
#undef FUNC_NAME
|
||||
#undef PIXEL_SIZE
|
||||
#undef PIX16
|
||||
|
||||
Loading…
Reference in New Issue
Block a user