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.
335 lines
8.8 KiB
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;
|
|
}
|
|
|