You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/msn/webcam/libmimic/mimic.c

335 lines
8.8 KiB

/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
*
* 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"
/**
* Creates a new instance and returns a pointer to the new context
* that can be used for either encoding or decoding by calling
* #mimic_encoder_init or #mimic_decoder_init.
*
* #mimic_close is called to free any resources associated with
* the context once done.
*
* @returns a new mimic context
*/
MimCtx *mimic_open()
{
MimCtx *ctx;
ctx = g_new0(MimCtx, 1);
ctx->encoder_initialized = FALSE;
ctx->decoder_initialized = FALSE;
return ctx;
}
/**
* Frees any resources associated with the given context.
*
* @param ctx the mimic context to free
*/
void mimic_close(MimCtx *ctx)
{
if (ctx->encoder_initialized || ctx->decoder_initialized) {
gint i;
g_free(ctx->cur_frame_buf);
for (i = 0; i < 16; i++)
g_free(ctx->buf_ptrs[i]);
}
g_free(ctx);
}
/*
* mimic_init
*
* Internal helper-function used to initialize
* a given context.
*/
static void mimic_init(MimCtx *ctx, gint width, gint height)
{
gint bufsize, i;
/*
* Dimensions-related.
*/
ctx->frame_width = width;
ctx->frame_height = height;
ctx->y_stride = ctx->frame_width;
ctx->y_row_count = ctx->frame_height;
ctx->y_size = ctx->y_stride * ctx->y_row_count;
ctx->crcb_stride = ctx->y_stride / 2;
ctx->crcb_row_count = ctx->y_row_count / 2;
ctx->crcb_size = ctx->crcb_stride * ctx->crcb_row_count;
ctx->num_vblocks_y = ctx->frame_height / 8;
ctx->num_hblocks_y = ctx->frame_width / 8;
ctx->num_vblocks_cbcr = ctx->frame_height / 16;
ctx->num_hblocks_cbcr = ctx->frame_width / 16;
if (ctx->frame_height % 16 != 0)
ctx->num_vblocks_cbcr++;
/*
* Initialize state.
*/
ctx->frame_num = 0;
ctx->ptr_index = 15;
ctx->num_coeffs = 28;
/*
* Allocate memory for buffers.
*/
ctx->cur_frame_buf = g_new(guchar, (320 * 240 * 3) / 2);
bufsize = ctx->y_size + (ctx->crcb_size * 2);
for (i = 0; i < 16; i++)
ctx->buf_ptrs[i] = g_new(guchar, bufsize);
/*
* Initialize vlc lookup used by decoder.
*/
_initialize_vlcdec_lookup(ctx->vlcdec_lookup);
}
/**
* Initialize the mimic encoder and prepare for encoding by
* initializing internal state and allocating resources as
* needed.
*
* After initializing use #mimic_get_property to determine
* the size of the output buffer needed for calls to
* #mimic_encode_frame. Use #mimic_set_property to set
* encoding quality.
*
* Note that once a given context has been initialized
* for either encoding or decoding it is not possible
* to initialize it again.
*
* @param ctx the mimic context to initialize
* @param resolution a #MimicResEnum used to specify the resolution
* @returns #TRUE on success
*/
gboolean mimic_encoder_init(MimCtx *ctx, const MimicResEnum resolution)
{
gint width, height;
/* Check if we've been initialized before. */
if (ctx->encoder_initialized || ctx->decoder_initialized)
return FALSE;
/* Check resolution. */
if (resolution == MIMIC_RES_LOW) {
width = 160;
height = 120;
} else if (resolution == MIMIC_RES_HIGH) {
width = 320;
height = 240;
} else {
return FALSE;
}
/* Initialize! */
mimic_init(ctx, width, height);
/* Set a default quality setting. */
ctx->quality = ENCODER_QUALITY_DEFAULT;
ctx->encoder_initialized = TRUE;
return TRUE;
}
/**
* Initialize the mimic decoder. The frame passed in frame_buffer
* is used to determine the resolution so that the internal state
* can be prepared and resources allocated accordingly. Note that
* the frame passed has to be a keyframe.
*
* After initializing use #mimic_get_property to determine required
* buffer-size, resolution, quality, etc.
*
* Note that once a given context has been initialized
* for either encoding or decoding it is not possible
* to initialize it again.
*
* @param ctx the mimic context to initialize
* @param frame_buffer buffer containing the first frame to decode
* @returns #TRUE on success
*/
gboolean mimic_decoder_init(MimCtx *ctx, const guchar *frame_buffer)
{
gint width, height;
gboolean is_keyframe;
/* Check if we've been initialized before and that
* frame_buffer is not NULL. */
if (ctx->encoder_initialized || ctx->decoder_initialized ||
frame_buffer == NULL)
{
return FALSE;
}
/* Check resolution. */
width = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 4)));
height = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 6)));
if (!(width == 160 && height == 120) && !(width == 320 && height == 240))
return FALSE;
/* Check that we're initialized with a keyframe. */
is_keyframe = (GUINT32_FROM_LE(*((guint32 *) (frame_buffer + 12))) == 0);
if (!is_keyframe)
return FALSE;
/* Get quality setting (in case we get queried for it before decoding). */
ctx->quality = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 2)));
/* Initialize! */
mimic_init(ctx, width, height);
ctx->decoder_initialized = TRUE;
return TRUE;
}
/**
* Get a property from a given mimic context. The context
* has to be initialized.
*
* Currently the following properties are defined:
* - "buffer_size"
* - Required output buffer size
* - "width"
* - Frame width
* - "height"
* - Frame height
* - "quality"
* - Encoder: Encoding quality used
* - Decoder: Decoding quality of the last known frame
*
* @param ctx the mimic context to retrieve the property from
* @param name of the property to retrieve the current value of
* @param data pointer to the data that will receive the retrieved value
* @returns #TRUE on success
*/
gboolean mimic_get_property(MimCtx *ctx, const gchar *name, gpointer data)
{
/* Either the encoder or the decoder has to be initialized. */
if (!ctx->encoder_initialized && !ctx->decoder_initialized)
return FALSE;
if (ctx->encoder_initialized) {
if (strcmp(name, "buffer_size") == 0) {
*((gint *) data) = ENCODER_BUFFER_SIZE;
return TRUE;
}
} else { /* decoder_initialized */
if (strcmp(name, "buffer_size") == 0) {
*((gint *) data) = ctx->frame_width * ctx->frame_height * 3;
return TRUE;
}
}
if (strcmp(name, "width") == 0) {
*((gint *) data) = ctx->frame_width;
return TRUE;
} else if (strcmp(name, "height") == 0) {
*((gint *) data) = ctx->frame_height;
return TRUE;
} else if (strcmp(name, "quality") == 0) {
*((gint *) data) = ctx->quality;
return TRUE;
}
return FALSE;
}
/**
* Set a property in a given mimic context. The context
* has to be initialized.
*
* Currently the following properties are defined:
* - "quality"
* - Encoding quality used by encoder.
*
* @param ctx the mimic context to set a property in
* @param name of the property to set to a new value
* @param data pointer to the data that contains the new value
* @returns #TRUE on success
*/
gboolean mimic_set_property(MimCtx *ctx, const gchar *name, gpointer data)
{
/* Either the encoder or the decoder has to be initialized. */
if (!ctx->encoder_initialized && !ctx->decoder_initialized)
return FALSE;
if (ctx->encoder_initialized) {
if (strcmp(name, "quality") == 0) {
gint new_quality = *((gint *) data);
if (new_quality < ENCODER_QUALITY_MIN ||
new_quality > ENCODER_QUALITY_MAX)
{
return FALSE;
}
ctx->quality = new_quality;
return TRUE;
}
} else { /* decoder_initialized */ }
return FALSE;
}
/*
* _clamp_value
*
* Internal helper-function used to clamp a given
* value to the range [ 0, 255 ].
*/
guchar _clamp_value(gint value)
{
if (value < 0)
return 0;
else if (value > 255)
return 255;
else
return value;
}