diff options
Diffstat (limited to 'kopete/protocols/msn/webcam/libmimic/decode.c')
-rw-r--r-- | kopete/protocols/msn/webcam/libmimic/decode.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/kopete/protocols/msn/webcam/libmimic/decode.c b/kopete/protocols/msn/webcam/libmimic/decode.c new file mode 100644 index 00000000..83067562 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/decode.c @@ -0,0 +1,311 @@ +/* Copyright (C) 2005 Ole Andr� Vadla Ravn�s <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "mimic-private.h" + + +static gboolean decode(MimCtx *ctx, gboolean is_pframe); + +/** + * Decode a MIMIC-encoded frame into RGB data. + * + * @param ctx the mimic context + * @param input_buffer buffer containing the MIMIC-encoded frame to decode + * @param output_buffer buffer that will receive the decoded frame in RGB 24-bpp packed pixel top-down format + * (use #mimic_get_property to determine the required buffer size, as well as frame width and height) + * @returns #TRUE on success + */ +gboolean mimic_decode_frame(MimCtx *ctx, + const guchar *input_buffer, + guchar *output_buffer) +{ + gboolean result, is_pframe; + guchar *input_y, *input_cr, *input_cb; + gint width, height; + + /* + * Some sanity checks. + */ + if (ctx == NULL || input_buffer == NULL || output_buffer == NULL) + { + return FALSE; + } + + if (!ctx->decoder_initialized) + { + return FALSE; + } + + /* + * Get frame dimensions. + */ + width = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 4))); + height = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 6))); + + /* + * Resolution changing is not supported. + */ + if (width != ctx->frame_width || + height != ctx->frame_height) + { + return FALSE; + } + + /* + * Increment frame counter. + */ + ctx->frame_num++; + + /* + * Initialize state. + */ + ctx->quality = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 2))); + is_pframe = GUINT32_FROM_LE(*((guint32 *) (input_buffer + 12))); + ctx->num_coeffs = input_buffer[16]; + + ctx->data_buffer = (gchar *) (input_buffer + 20); + ctx->data_index = 0; + ctx->cur_chunk_len = 16; + ctx->read_odd = FALSE; + + /* + * Decode frame. + */ + if (!(is_pframe && ctx->prev_frame_buf == NULL)) + result = decode(ctx, is_pframe); + else + { + result = FALSE; + } + + /* + * Perform YUV 420 to RGB conversion. + */ + input_y = ctx->cur_frame_buf; + input_cr = ctx->cur_frame_buf + ctx->y_size; + input_cb = ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size; + + _yuv_to_rgb(input_y, + input_cb, + input_cr, + output_buffer, + ctx->frame_width, + ctx->frame_height); + + return result; +} + +/* + * decode_main + * + * Main decoding loop. + */ +static gboolean decode(MimCtx *ctx, gboolean is_pframe) +{ + gint y, x, i, j, chrom_ch, *bptr, base_offset, offset; + gint dct_block[64]; + guchar *src, *dst, *p; + guint32 bit; + + /* + * Clear Cr and Cb planes. + */ + p = ctx->cur_frame_buf + ctx->y_size; + memset(p, 128, 2 * ctx->crcb_size); + + /* + * Decode Y plane. + */ + for (y = 0; y < ctx->num_vblocks_y; y++) { + + base_offset = ctx->y_stride * 8 * y; + + src = ctx->prev_frame_buf + base_offset; + dst = ctx->cur_frame_buf + base_offset; + + for (x = 0; x < ctx->num_hblocks_y; x++) { + + /* Check for a change condition in the current block. */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + else + bit = 0; + + if (bit == 0) { + + /* Yes: Is the new content the same as it was in one of + * the 15 last frames preceding the previous? */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + + if (bit == 0) { + + /* No: decode it. */ + + if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) { + + return FALSE; + } + + _idct_dequant_block(ctx, dct_block, 0); + + bptr = dct_block; + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + for (j = 0; j < 8; j++) { + guint v; + + if (bptr[j] <= 255) + v = (bptr[j] >= 0) ? bptr[j] : 0; + else + v = 255; + + *(dst + offset + j) = v; + } + + bptr += 8; + } + } else { + guint32 backref; + + /* Yes: read the backreference (4 bits) and copy. */ + + backref = _read_bits(ctx, 4); + + p = ctx->buf_ptrs[(ctx->ptr_index + backref) % 16]; + p += base_offset + (x * 8); + + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + memcpy(dst + offset, p + offset, 8); + } + } + } else { + + /* No change no worries: just copy from the previous frame. */ + + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + memcpy(dst + offset, src + offset, 8); + } + } + + src += 8; + dst += 8; + } + } + + /* + * Decode Cr and Cb planes. + */ + for (chrom_ch = 0; chrom_ch < 2; chrom_ch++) { + + base_offset = ctx->y_size + (ctx->crcb_size * chrom_ch); + + for (y = 0; y < ctx->num_vblocks_cbcr; y++) { + guint num_rows = 8; + + /* The last row of blocks in chrominance for 160x120 resolution + * is half the normal height and must be accounted for. */ + if (y + 1 == ctx->num_vblocks_cbcr && ctx->frame_height % 16 != 0) + num_rows = 4; + + offset = base_offset + (ctx->crcb_stride * 8 * y); + + src = ctx->prev_frame_buf + offset; + dst = ctx->cur_frame_buf + offset; + + for (x = 0; x < ctx->num_hblocks_cbcr; x++) { + + /* Check for a change condition in the current block. */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + else + bit = 1; + + if (bit == 1) { + + /* Yes: decode it. */ + + if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) { + + /* Corrupted frame: clear Cr and Cb planes and return. */ + p = ctx->cur_frame_buf + ctx->y_size; + memset(p, 128, ctx->crcb_size * 2); + + return FALSE; + } + + _idct_dequant_block(ctx, dct_block, 1); + + for (i = 0; i < num_rows; i++) { + p = dst + (ctx->crcb_stride * i); + + for (j = 0; j < 8; j++) + p[j] = dct_block[(i * 8) + j]; + } + + } else { + + /* No change no worries: just copy from the previous frame. */ + + for (i = 0; i < num_rows; i++) { + offset = ctx->crcb_stride * i; + + memcpy(dst + offset, src + offset, 8); + } + } + + src += 8; + dst += 8; + } + } + } + + /* + * Make a copy of the current frame and store in + * the circular pointer list of 16 entries. + */ + ctx->prev_frame_buf = ctx->buf_ptrs[ctx->ptr_index]; + memcpy(ctx->prev_frame_buf, ctx->cur_frame_buf, + ctx->y_size + (ctx->crcb_size * 2)); + + if (--ctx->ptr_index < 0) + ctx->ptr_index = 15; + + /* + * Perform deblocking on all planes. + */ + _deblock(ctx->cur_frame_buf, + ctx->y_stride, ctx->y_row_count); + + _deblock(ctx->cur_frame_buf + ctx->y_size, + ctx->crcb_stride, ctx->crcb_row_count); + + _deblock(ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size, + ctx->crcb_stride, ctx->crcb_row_count); + + return TRUE; +} + |