jpeg.c 11.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * jpeg.c: jpeg decoder module making use of libjpeg.
 *****************************************************************************
4
 * Copyright (C) 2013-2014 VLC authors and VideoLAN
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Authors: Maxim Bublis <b@codemonkey.ru>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
#include <jpeglib.h>
#include <setjmp.h>

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/* JPEG_SYS_COMMON_MEMBERS:
 * members common to encoder and decoder descriptors
 */
#define JPEG_SYS_COMMON_MEMBERS                             \
/**@{*/                                                     \
    /* libjpeg error handler manager */                     \
    struct jpeg_error_mgr err;                              \
                                                            \
    /* setjmp buffer for internal libjpeg error handling */ \
    jmp_buf setjmp_buffer;                                  \
                                                            \
    vlc_object_t *p_obj;                                    \
                                                            \
/**@}*/                                                     \

Maxim Bublis's avatar
Maxim Bublis committed
48 49 50 51 52 53
#define ENC_CFG_PREFIX "sout-jpeg-"
#define ENC_QUALITY_TEXT N_("Quality level")
#define ENC_QUALITY_LONGTEXT N_("Quality level " \
    "for encoding (this can enlarge or reduce output image size).")


54 55 56 57 58 59 60 61 62 63
/*
 * jpeg common descriptor
 */
struct jpeg_sys_t
{
    JPEG_SYS_COMMON_MEMBERS
};

typedef struct jpeg_sys_t jpeg_sys_t;

64 65 66 67 68
/*
 * jpeg decoder descriptor
 */
struct decoder_sys_t
{
69
    JPEG_SYS_COMMON_MEMBERS
70

71
    struct jpeg_decompress_struct p_jpeg;
72 73 74 75 76 77 78
};

static int  OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);

static picture_t *DecodeBlock(decoder_t *, block_t **);

Maxim Bublis's avatar
Maxim Bublis committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
/*
 * jpeg encoder descriptor
 */
struct encoder_sys_t
{
    JPEG_SYS_COMMON_MEMBERS

    struct jpeg_compress_struct p_jpeg;

    int i_blocksize;
    int i_quality;
};

static const char * const ppsz_enc_options[] = {
    "quality",
    NULL
};

static int  OpenEncoder(vlc_object_t *);
static void CloseEncoder(vlc_object_t *);

static block_t *EncodeBlock(encoder_t *, picture_t *);

102 103 104 105 106 107
/*
 * Module descriptor
 */
vlc_module_begin()
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_VCODEC)
Maxim Bublis's avatar
Maxim Bublis committed
108
    /* decoder main module */
109 110 111 112
    set_description(N_("JPEG image decoder"))
    set_capability("decoder", 1000)
    set_callbacks(OpenDecoder, CloseDecoder)
    add_shortcut("jpeg")
Maxim Bublis's avatar
Maxim Bublis committed
113 114 115 116 117 118 119 120 121 122

    /* encoder submodule */
    add_submodule()
    add_shortcut("jpeg")
    set_section(N_("Encoding"), NULL)
    set_description(N_("JPEG image encoder"))
    set_capability("encoder", 1000)
    set_callbacks(OpenEncoder, CloseEncoder)
    add_integer_with_range(ENC_CFG_PREFIX "quality", 95, 0, 100,
                           ENC_QUALITY_TEXT, ENC_QUALITY_LONGTEXT, true)
123 124
vlc_module_end()

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

/*
 * Exit error handler for libjpeg
 */
static void user_error_exit(j_common_ptr p_jpeg)
{
    jpeg_sys_t *p_sys = (jpeg_sys_t *)p_jpeg->err;
    p_sys->err.output_message(p_jpeg);
    longjmp(p_sys->setjmp_buffer, 1);
}

/*
 * Emit message error handler for libjpeg
 */
static void user_error_message(j_common_ptr p_jpeg)
{
    char error_msg[JMSG_LENGTH_MAX];

    jpeg_sys_t *p_sys = (jpeg_sys_t *)p_jpeg->err;
    p_sys->err.format_message(p_jpeg, error_msg);
    msg_Err(p_sys->p_obj, "%s", error_msg);
}

148 149 150 151 152 153 154 155 156 157 158 159 160
/*
 * Probe the decoder and return score
 */
static int OpenDecoder(vlc_object_t *p_this)
{
    decoder_t *p_dec = (decoder_t *)p_this;

    if (p_dec->fmt_in.i_codec != VLC_CODEC_JPEG)
    {
        return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store the decoder's structure */
161 162
    decoder_sys_t *p_sys = malloc(sizeof(decoder_sys_t));
    if (p_sys == NULL)
163 164 165 166
    {
        return VLC_ENOMEM;
    }

167 168 169 170 171 172 173
    p_dec->p_sys = p_sys;

    p_sys->p_obj = p_this;

    p_sys->p_jpeg.err = jpeg_std_error(&p_sys->err);
    p_sys->err.error_exit = user_error_exit;
    p_sys->err.output_message = user_error_message;
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

    /* Set output properties */
    p_dec->fmt_out.i_cat = VIDEO_ES;

    /* Set callbacks */
    p_dec->pf_decode_video = DecodeBlock;

    return VLC_SUCCESS;
}

/*
 * This function must be fed with a complete compressed frame.
 */
static picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block)
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    block_t *p_block;
    picture_t *p_pic = 0;

    JSAMPARRAY p_row_pointers = NULL;

    if (!pp_block || !*pp_block)
    {
        return NULL;
    }

    p_block = *pp_block;

202
    if (p_block->i_flags & BLOCK_FLAG_DISCONTINUITY)
203 204
    {
        block_Release(p_block);
205
        *pp_block = NULL;
206 207 208 209 210 211 212 213 214
        return NULL;
    }

    /* libjpeg longjmp's there in case of error */
    if (setjmp(p_sys->setjmp_buffer))
    {
        goto error;
    }

215 216 217
    jpeg_create_decompress(&p_sys->p_jpeg);
    jpeg_mem_src(&p_sys->p_jpeg, p_block->p_buffer, p_block->i_buffer);
    jpeg_read_header(&p_sys->p_jpeg, TRUE);
218

219
    p_sys->p_jpeg.out_color_space = JCS_RGB;
220

221
    jpeg_start_decompress(&p_sys->p_jpeg);
222 223 224

    /* Set output properties */
    p_dec->fmt_out.i_codec = VLC_CODEC_RGB24;
225 226
    p_dec->fmt_out.video.i_visible_width  = p_dec->fmt_out.video.i_width  = p_sys->p_jpeg.output_width;
    p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height = p_sys->p_jpeg.output_height;
227 228 229 230 231 232 233 234 235 236 237 238 239 240
    p_dec->fmt_out.video.i_sar_num = 1;
    p_dec->fmt_out.video.i_sar_den = 1;
    p_dec->fmt_out.video.i_rmask = 0x000000ff;
    p_dec->fmt_out.video.i_gmask = 0x0000ff00;
    p_dec->fmt_out.video.i_bmask = 0x00ff0000;

    /* Get a new picture */
    p_pic = decoder_NewPicture(p_dec);
    if (!p_pic)
    {
        goto error;
    }

    /* Decode picture */
241
    p_row_pointers = malloc(sizeof(JSAMPROW) * p_sys->p_jpeg.output_height);
242 243 244 245
    if (!p_row_pointers)
    {
        goto error;
    }
246
    for (unsigned i = 0; i < p_sys->p_jpeg.output_height; i++) {
247 248 249
        p_row_pointers[i] = p_pic->p->p_pixels + p_pic->p->i_pitch * i;
    }

250
    while (p_sys->p_jpeg.output_scanline < p_sys->p_jpeg.output_height)
251
    {
252 253 254
        jpeg_read_scanlines(&p_sys->p_jpeg,
                p_row_pointers + p_sys->p_jpeg.output_scanline,
                p_sys->p_jpeg.output_height - p_sys->p_jpeg.output_scanline);
255 256
    }

257 258
    jpeg_finish_decompress(&p_sys->p_jpeg);
    jpeg_destroy_decompress(&p_sys->p_jpeg);
259 260 261 262 263
    free(p_row_pointers);

    p_pic->date = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : p_block->i_dts;

    block_Release(p_block);
264 265
    *pp_block = NULL;

266 267 268 269
    return p_pic;

error:

270
    jpeg_destroy_decompress(&p_sys->p_jpeg);
271 272 273
    free(p_row_pointers);

    block_Release(p_block);
274 275
    *pp_block = NULL;

276 277 278 279 280 281 282 283 284 285 286 287 288
    return NULL;
}

/*
 * jpeg decoder destruction
 */
static void CloseDecoder(vlc_object_t *p_this)
{
    decoder_t *p_dec = (decoder_t *)p_this;
    decoder_sys_t *p_sys = p_dec->p_sys;

    free(p_sys);
}
Maxim Bublis's avatar
Maxim Bublis committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334

/*
 * Probe the encoder and return score
 */
static int OpenEncoder(vlc_object_t *p_this)
{
    encoder_t *p_enc = (encoder_t *)p_this;

    config_ChainParse(p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg);

    if (p_enc->fmt_out.i_codec != VLC_CODEC_JPEG)
    {
        return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store encoder's structure */
    encoder_sys_t *p_sys = malloc(sizeof(encoder_sys_t));
    if (p_sys == NULL)
    {
        return VLC_ENOMEM;
    }

    p_enc->p_sys = p_sys;

    p_sys->p_obj = p_this;

    p_sys->p_jpeg.err = jpeg_std_error(&p_sys->err);
    p_sys->err.error_exit = user_error_exit;
    p_sys->err.output_message = user_error_message;

    p_sys->i_quality = var_GetInteger(p_enc, ENC_CFG_PREFIX "quality");
    p_sys->i_blocksize = 3 * p_enc->fmt_in.video.i_visible_width * p_enc->fmt_in.video.i_visible_height;

    p_enc->fmt_in.i_codec = VLC_CODEC_J420;
    p_enc->pf_encode_video = EncodeBlock;

    return VLC_SUCCESS;
}

/*
 * EncodeBlock
 */
static block_t *EncodeBlock(encoder_t *p_enc, picture_t *p_pic)
{
    encoder_sys_t *p_sys = p_enc->p_sys;

335 336 337 338
    if (unlikely(!p_pic))
    {
        return NULL;
    }
Maxim Bublis's avatar
Maxim Bublis committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
    block_t *p_block = block_Alloc(p_sys->i_blocksize);
    if (p_block == NULL)
    {
        return NULL;
    }

    JSAMPIMAGE p_row_pointers = NULL;

    /* libjpeg longjmp's there in case of error */
    if (setjmp(p_sys->setjmp_buffer))
    {
        goto error;
    }

    jpeg_create_compress(&p_sys->p_jpeg);
    jpeg_mem_dest(&p_sys->p_jpeg, &p_block->p_buffer, &p_block->i_buffer);

    p_sys->p_jpeg.image_width = p_enc->fmt_in.video.i_visible_width;
    p_sys->p_jpeg.image_height = p_enc->fmt_in.video.i_visible_height;
    p_sys->p_jpeg.input_components = 3;
    p_sys->p_jpeg.in_color_space = JCS_YCbCr;

    jpeg_set_defaults(&p_sys->p_jpeg);
    jpeg_set_colorspace(&p_sys->p_jpeg, JCS_YCbCr);

    p_sys->p_jpeg.raw_data_in = TRUE;
365
#if JPEG_LIB_VERSION >= 70
Maxim Bublis's avatar
Maxim Bublis committed
366
    p_sys->p_jpeg.do_fancy_downsampling = FALSE;
367
#endif
Maxim Bublis's avatar
Maxim Bublis committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

    jpeg_set_quality(&p_sys->p_jpeg, p_sys->i_quality, TRUE);

    jpeg_start_compress(&p_sys->p_jpeg, TRUE);

    /* Encode picture */
    p_row_pointers = malloc(sizeof(JSAMPARRAY) * p_pic->i_planes);
    if (p_row_pointers == NULL)
    {
        goto error;
    }

    for (int i = 0; i < p_pic->i_planes; i++)
    {
        p_row_pointers[i] = malloc(sizeof(JSAMPROW) * p_sys->p_jpeg.comp_info[i].v_samp_factor * DCTSIZE);
    }

    while (p_sys->p_jpeg.next_scanline < p_sys->p_jpeg.image_height)
    {
        for (int i = 0; i < p_pic->i_planes; i++)
        {
            int i_offset = p_sys->p_jpeg.next_scanline * p_sys->p_jpeg.comp_info[i].v_samp_factor / p_sys->p_jpeg.max_v_samp_factor;

            for (int j = 0; j < p_sys->p_jpeg.comp_info[i].v_samp_factor * DCTSIZE; j++)
            {
                p_row_pointers[i][j] = p_pic->p[i].p_pixels + p_pic->p[i].i_pitch * (i_offset + j);
            }
        }
        jpeg_write_raw_data(&p_sys->p_jpeg, p_row_pointers, p_sys->p_jpeg.max_v_samp_factor * DCTSIZE);
    }

    jpeg_finish_compress(&p_sys->p_jpeg);
    jpeg_destroy_compress(&p_sys->p_jpeg);

    for (int i = 0; i < p_pic->i_planes; i++)
    {
        free(p_row_pointers[i]);
    }
    free(p_row_pointers);

408 409
    p_block->i_dts = p_block->i_pts = p_pic->date;

Maxim Bublis's avatar
Maxim Bublis committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    return p_block;

error:
    jpeg_destroy_compress(&p_sys->p_jpeg);

    if (p_row_pointers != NULL)
    {
        for (int i = 0; i < p_pic->i_planes; i++)
        {
            free(p_row_pointers[i]);
        }
    }
    free(p_row_pointers);

    block_Release(p_block);

    return NULL;
}

/*
 * jpeg encoder destruction
 */
static void CloseEncoder(vlc_object_t *p_this)
{
    encoder_t *p_enc = (encoder_t *)p_this;
    encoder_sys_t *p_sys = p_enc->p_sys;

    free(p_sys);
}