/* Copyright (C) 2005 Ole André Vadla Ravnås * * 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 #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; }