/* main context */ static gboolean display_frame(gpointer video_decoder) { SpiceGstDecoder *decoder = (SpiceGstDecoder*)video_decoder; SpiceGstFrame *gstframe; GstCaps *caps; gint width, height; GstStructure *s; GstBuffer *buffer; GstMapInfo mapinfo; g_mutex_lock(&decoder->queues_mutex); decoder->timer_id = 0; gstframe = g_queue_pop_head(decoder->display_queue); g_mutex_unlock(&decoder->queues_mutex); /* If the queue is empty we don't even need to reschedule */ g_return_val_if_fail(gstframe, G_SOURCE_REMOVE); if (!gstframe->sample) { spice_warning("got a frame without a sample!"); goto error; } caps = gst_sample_get_caps(gstframe->sample); if (!caps) { spice_warning("GStreamer error: could not get the caps of the sample"); goto error; } s = gst_caps_get_structure(caps, 0); if (!gst_structure_get_int(s, "width", &width) || !gst_structure_get_int(s, "height", &height)) { spice_warning("GStreamer error: could not get the size of the frame"); goto error; } buffer = gst_sample_get_buffer(gstframe->sample); if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) { spice_warning("GStreamer error: could not map the buffer"); goto error; } stream_display_frame(decoder->base.stream, gstframe->frame, width, height, mapinfo.data); gst_buffer_unmap(buffer, &mapinfo); error: free_gst_frame(gstframe); schedule_frame(decoder); return G_SOURCE_REMOVE; }
/* main context */ static gboolean mjpeg_decoder_decode_frame(gpointer video_decoder) { MJpegDecoder *decoder = (MJpegDecoder*)video_decoder; gboolean back_compat = decoder->base.stream->channel->priv->peer_hdr.major_version == 1; JDIMENSION width, height; uint8_t *dest; uint8_t *lines[4]; jpeg_read_header(&decoder->mjpeg_cinfo, 1); width = decoder->mjpeg_cinfo.image_width; height = decoder->mjpeg_cinfo.image_height; if (decoder->out_size < width * height * 4) { g_free(decoder->out_frame); decoder->out_size = width * height * 4; decoder->out_frame = g_malloc(decoder->out_size); } dest = decoder->out_frame; #ifdef JCS_EXTENSIONS // requires jpeg-turbo if (back_compat) decoder->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX; else decoder->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX; #else #warning "You should consider building with libjpeg-turbo" decoder->mjpeg_cinfo.out_color_space = JCS_RGB; #endif #ifndef SPICE_QUALITY decoder->mjpeg_cinfo.dct_method = JDCT_IFAST; decoder->mjpeg_cinfo.do_fancy_upsampling = FALSE; decoder->mjpeg_cinfo.do_block_smoothing = FALSE; decoder->mjpeg_cinfo.dither_mode = JDITHER_ORDERED; #endif // TODO: in theory should check cinfo.output_height match with our height jpeg_start_decompress(&decoder->mjpeg_cinfo); /* rec_outbuf_height is the recommended size of the output buffer we * pass to libjpeg for optimum performance */ if (decoder->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) { jpeg_abort_decompress(&decoder->mjpeg_cinfo); g_return_val_if_reached(G_SOURCE_REMOVE); } while (decoder->mjpeg_cinfo.output_scanline < decoder->mjpeg_cinfo.output_height) { /* only used when JCS_EXTENSIONS is undefined */ G_GNUC_UNUSED unsigned int lines_read; for (unsigned int j = 0; j < decoder->mjpeg_cinfo.rec_outbuf_height; j++) { lines[j] = dest; #ifdef JCS_EXTENSIONS dest += 4 * width; #else dest += 3 * width; #endif } lines_read = jpeg_read_scanlines(&decoder->mjpeg_cinfo, lines, decoder->mjpeg_cinfo.rec_outbuf_height); #ifndef JCS_EXTENSIONS { uint8_t *s = lines[0]; uint32_t *d = SPICE_ALIGNED_CAST(uint32_t *, s); if (back_compat) { for (unsigned int j = lines_read * width; j > 0; ) { j -= 1; // reverse order, bad for cache? d[j] = s[j * 3 + 0] | s[j * 3 + 1] << 8 | s[j * 3 + 2] << 16; } } else { for (unsigned int j = lines_read * width; j > 0; ) { j -= 1; // reverse order, bad for cache? d[j] = s[j * 3 + 0] << 16 | s[j * 3 + 1] << 8 | s[j * 3 + 2]; } } } #endif dest = &(decoder->out_frame[decoder->mjpeg_cinfo.output_scanline * width * 4]); } jpeg_finish_decompress(&decoder->mjpeg_cinfo); /* Display the frame and dispose of it */ stream_display_frame(decoder->base.stream, decoder->cur_frame, width, height, SPICE_UNKNOWN_STRIDE, decoder->out_frame); free_spice_frame(decoder->cur_frame); decoder->cur_frame = NULL; decoder->timer_id = 0; /* Schedule the next frame */ mjpeg_decoder_schedule(decoder); return G_SOURCE_REMOVE; }