1 2 3 4 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 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 335 336 337 338 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 365 366 367 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 408 409 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 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
|
/*****************************************************************************
* jpeg.c: jpeg decoder module making use of libjpeg.
*****************************************************************************
* Copyright (C) 2013-2014,2016 VLC authors and VideoLAN
*
* 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 <vlc_charset.h>
#include <jpeglib.h>
#include <setjmp.h>
/* 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; \
\
/**@}*/ \
#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).")
/*
* jpeg common descriptor
*/
struct jpeg_sys_t
{
JPEG_SYS_COMMON_MEMBERS
};
typedef struct jpeg_sys_t jpeg_sys_t;
/*
* jpeg decoder descriptor
*/
struct decoder_sys_t
{
JPEG_SYS_COMMON_MEMBERS
struct jpeg_decompress_struct p_jpeg;
};
static int OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);
static int DecodeBlock(decoder_t *, block_t *);
/*
* 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 *);
/*
* Module descriptor
*/
vlc_module_begin()
set_category(CAT_INPUT)
set_subcategory(SUBCAT_INPUT_VCODEC)
/* decoder main module */
set_description(N_("JPEG image decoder"))
set_capability("video decoder", 1000)
set_callbacks(OpenDecoder, CloseDecoder)
add_shortcut("jpeg")
/* 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)
vlc_module_end()
/*
* 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);
}
/*
* 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 */
decoder_sys_t *p_sys = malloc(sizeof(decoder_sys_t));
if (p_sys == NULL)
{
return VLC_ENOMEM;
}
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;
/* Set callbacks */
p_dec->pf_decode = DecodeBlock;
p_dec->fmt_out.i_codec = VLC_CODEC_RGB24;
p_dec->fmt_out.video.transfer = TRANSFER_FUNC_SRGB;
p_dec->fmt_out.video.space = COLOR_SPACE_SRGB;
p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_SRGB;
p_dec->fmt_out.video.b_color_range_full = true;
return VLC_SUCCESS;
}
/*
* The following two functions are used to return 16 and 32 bit values from
* the EXIF tag structure. That structure is borrowed from TIFF files and may be
* in big endian or little endian format. The boolean b_bigEndian parameter
* is TRUE if the EXIF data is in big endian format, and FALSE for little endian
* Case Little Endian EXIF tag / Little Endian machine
* - just memcpy the tag structure into the value to return
* Case Little Endian EXIF tag / Big Endian machine
* - memcpy the tag structure into value, and bswap it
* Case Little Endian EXIF tag / Big Endian machine
* - memcpy the tag structure into value, and bswap it
* Case Big Endian EXIF tag / Big Endian machine
* - just memcpy the tag structure into the value to return
*
* While there are byte manipulation functions in vlc_common.h, none of them
* address that format of the data supplied may be either big or little endian.
*
* The claim is made that this is the best way to do it. Can you do better?
*/
#define G_LITTLE_ENDIAN 1234
#define G_BIG_ENDIAN 4321
typedef unsigned int uint;
typedef unsigned short ushort;
static uint16_t
de_get16( const void * ptr, uint endian ) {
return (endian == G_BIG_ENDIAN) ? GetWBE(ptr) : GetWLE(ptr);
}
static uint32_t
de_get32( const void * ptr, uint endian ) {
return (endian == G_BIG_ENDIAN) ? GetDWBE(ptr) : GetDWLE(ptr);
}
static bool getRDFFloat(const char *psz_rdf, float *out, const char *psz_var)
{
char *p_start = strcasestr(psz_rdf, psz_var);
if (p_start == NULL)
return false;
size_t varlen = strlen(psz_var);
p_start += varlen;
char *p_end = NULL;
/* XML style */
if (p_start[0] == '>')
{
p_start += 1;
p_end = strchr(p_start, '<');
}
else if (p_start[0] == '=' && p_start[1] == '"')
{
p_start += 2;
p_end = strchr(p_start, '"');
}
if (unlikely(p_end == NULL || p_end == p_start + 1))
return false;
*out = us_strtof(p_start, NULL);
return true;
}
#define EXIF_JPEG_MARKER 0xE1
#define EXIF_XMP_STRING "http://ns.adobe.com/xap/1.0/\000"
/* read XMP metadata for projection according to
* https://developers.google.com/streetview/spherical-metadata */
static bool jpeg_ParseXMP(const uint8_t *p_buf, size_t i_buf,
video_format_t *fmt)
{
if(i_buf < 29)
return false;
/* Allocate temp, zero terminated buffer */
/* Fixme: custom memicmp */
/* or Fixme: Who's not compliant to XML attributes case ? */
char *psz_rdf = malloc(i_buf - 29 + 1);
if (unlikely(psz_rdf == NULL))
return false;
memcpy(psz_rdf, p_buf + 29, i_buf - 29);
psz_rdf[i_buf - 29] = '\0';
if (!strcasestr(psz_rdf, "ProjectionType=\"equirectangular\"") &&
!strcasestr(psz_rdf, "ProjectionType>equirectangular"))
return false;
fmt->projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
/* pose handling */
float value;
if (getRDFFloat(psz_rdf, &value, "PoseHeadingDegrees") &&
value >= 0.f && value <= 360.f)
fmt->pose.yaw = value;
if (getRDFFloat(psz_rdf, &value, "PosePitchDegrees"))
fmt->pose.pitch = value;
if (getRDFFloat(psz_rdf, &value, "PoseRollDegrees"))
fmt->pose.roll = value;
/* initial view */
if (getRDFFloat(psz_rdf, &value, "InitialViewHeadingDegrees"))
fmt->pose.yaw = value;
if (getRDFFloat(psz_rdf, &value, "InitialViewPitchDegrees"))
fmt->pose.pitch = value;
if (getRDFFloat(psz_rdf, &value, "InitialViewRollDegrees"))
fmt->pose.roll = value;
if (getRDFFloat(psz_rdf, &value, "InitialHorizontalFOVDegrees") &&
value >= FIELD_OF_VIEW_DEGREES_MIN && value <= FIELD_OF_VIEW_DEGREES_MAX)
fmt->pose.fov = value;
free(psz_rdf);
return true;
}
static void jpeg_FillProjection(j_decompress_ptr cinfo, video_format_t *fmt)
{
for (jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
cmarker != NULL;
cmarker = cmarker->next)
{
if(cmarker->marker == EXIF_JPEG_MARKER &&
cmarker->data_length >= 32 &&
!memcmp(cmarker->data, EXIF_XMP_STRING, 29) &&
jpeg_ParseXMP(cmarker->data, cmarker->data_length, fmt))
break; /* found projection marker */
}
}
/*
* Look through the meta data in the libjpeg decompress structure to determine
* if an EXIF Orientation tag is present. If so return true and sets orientation
* value (1-8), otherwise return false
*
* This function is based on the function get_orientation in io-jpeg.c, part of
* the GdkPixbuf library, licensed under LGPLv2+.
* Copyright (C) 1999 Michael Zucchi, The Free Software Foundation
*/
#define EXIF_IDENT_STRING "Exif\0" /* Exif\000\000 */
static bool jpeg_ParseExifApp1( const uint8_t *p_buf, size_t i_buf, int *orientation )
{
uint i; /* index into working buffer */
ushort tag_type; /* endianed tag type extracted from tiff header */
uint ret; /* Return value */
uint offset; /* de-endianed offset in various situations */
uint tags; /* number of tags in current ifd */
uint type; /* de-endianed type of tag */
uint count; /* de-endianed count of elements in a tag */
uint tiff = 0; /* offset to active tiff header */
uint endian = 0; /* detected endian of data */
const char leth[] = { 0x49, 0x49, 0x2a, 0x00 }; /* Little endian TIFF header */
const char beth[] = { 0x4d, 0x4d, 0x00, 0x2a }; /* Big endian TIFF header */
#define EXIF_ORIENT_TAGID 0x112
if (i_buf < 32)
return false;
/* Check for TIFF header and catch endianness */
i = 0;
/* Just skip data until TIFF header - it should be within 16 bytes from marker start.
Normal structure relative to APP1 marker -
0x0000: APP1 marker entry = 2 bytes
0x0002: APP1 length entry = 2 bytes
0x0004: Exif Identifier entry = 6 bytes
0x000A: Start of TIFF header (Byte order entry) - 4 bytes
- This is what we look for, to determine endianness.
0x000E: 0th IFD offset pointer - 4 bytes
exif_marker->data points to the first data after the APP1 marker
and length entries, which is the exif identification string.
The TIFF header should thus normally be found at i=6, below,
and the pointer to IFD0 will be at 6+4 = 10.
*/
while ( i < 16 )
{
/* Little endian TIFF header */
if ( memcmp( &p_buf[i], leth, 4 ) == 0 )
{
endian = G_LITTLE_ENDIAN;
}
/* Big endian TIFF header */
else
if ( memcmp( &p_buf[i], beth, 4 ) == 0 )
{
endian = G_BIG_ENDIAN;
}
/* Keep looking through buffer */
else
{
i++;
continue;
}
/* We have found either big or little endian TIFF header */
tiff = i;
break;
}
/* So did we find a TIFF header or did we just hit end of buffer? */
if ( tiff == 0 )
return false;
/* Read out the offset pointer to IFD0 */
offset = de_get32( &p_buf[i] + 4, endian );
i = i + offset;
/* Check that we still are within the buffer and can read the tag count */
if ( i > i_buf - 2 )
return false;
/* Find out how many tags we have in IFD0. As per the TIFF spec, the first
two bytes of the IFD contain a count of the number of tags. */
tags = de_get16( &p_buf[i], endian );
i = i + 2;
/* Check that we still have enough data for all tags to check. The tags
are listed in consecutive 12-byte blocks. The tag ID, type, size, and
a pointer to the actual value, are packed into these 12 byte entries. */
if ( tags * 12U > i_buf - i )
return false;
/* Check through IFD0 for tags of interest */
while ( tags-- )
{
tag_type = de_get16( &p_buf[i], endian );
/* Is this the orientation tag? */
if ( tag_type == EXIF_ORIENT_TAGID )
{
type = de_get16( &p_buf[i + 2], endian );
count = de_get32( &p_buf[i + 4], endian );
/* Check that type and count fields are OK. The orientation field
will consist of a single (count=1) 2-byte integer (type=3). */
if ( type != 3 || count != 1 )
return false;
/* Return the orientation value. Within the 12-byte block, the
pointer to the actual data is at offset 8. */
ret = de_get16( &p_buf[i + 8], endian );
*orientation = ( ret <= 8 ) ? ret : 0;
return true;
}
/* move the pointer to the next 12-byte tag field. */
i = i + 12;
}
return false; /* No EXIF Orientation tag found */
}
static int jpeg_GetOrientation( j_decompress_ptr cinfo )
{
int orientation = 0;
for (jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
cmarker != NULL;
cmarker = cmarker->next)
{
if(cmarker->marker == EXIF_JPEG_MARKER &&
cmarker->data_length >= 32 &&
!memcmp(cmarker->data, EXIF_IDENT_STRING, sizeof(EXIF_IDENT_STRING)) &&
jpeg_ParseExifApp1(cmarker->data, cmarker->data_length, &orientation))
break; /* found orientation marker */
}
return orientation;
}
/*
* This function must be fed with a complete compressed frame.
*/
static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
{
decoder_sys_t *p_sys = p_dec->p_sys;
picture_t *p_pic = 0;
JSAMPARRAY p_row_pointers = NULL;
if (!p_block) /* No Drain */
return VLCDEC_SUCCESS;
if (p_block->i_flags & BLOCK_FLAG_CORRUPTED )
{
block_Release(p_block);
return VLCDEC_SUCCESS;
}
/* libjpeg longjmp's there in case of error */
if (setjmp(p_sys->setjmp_buffer))
{
goto error;
}
jpeg_create_decompress(&p_sys->p_jpeg);
jpeg_mem_src(&p_sys->p_jpeg, p_block->p_buffer, p_block->i_buffer);
jpeg_save_markers( &p_sys->p_jpeg, EXIF_JPEG_MARKER, 0xffff );
jpeg_read_header(&p_sys->p_jpeg, TRUE);
p_sys->p_jpeg.out_color_space = JCS_RGB;
jpeg_start_decompress(&p_sys->p_jpeg);
/* Set output properties */
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;
p_dec->fmt_out.video.i_sar_num = 1;
p_dec->fmt_out.video.i_sar_den = 1;
int i_otag; /* Orientation tag has valid range of 1-8. 1 is normal orientation, 0 = unspecified = normal */
i_otag = jpeg_GetOrientation( &p_sys->p_jpeg );
if ( i_otag > 1 )
{
msg_Dbg( p_dec, "Jpeg orientation is %d", i_otag );
p_dec->fmt_out.video.orientation = ORIENT_FROM_EXIF( i_otag );
}
jpeg_FillProjection(&p_sys->p_jpeg, &p_dec->fmt_out.video);
/* Get a new picture */
if (decoder_UpdateVideoFormat(p_dec))
{
goto error;
}
p_pic = decoder_NewPicture(p_dec);
if (!p_pic)
{
goto error;
}
/* Decode picture */
p_row_pointers = vlc_alloc(p_sys->p_jpeg.output_height, sizeof(JSAMPROW));
if (!p_row_pointers)
{
goto error;
}
for (unsigned i = 0; i < p_sys->p_jpeg.output_height; i++) {
p_row_pointers[i] = p_pic->p->p_pixels + p_pic->p->i_pitch * i;
}
while (p_sys->p_jpeg.output_scanline < p_sys->p_jpeg.output_height)
{
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);
}
jpeg_finish_decompress(&p_sys->p_jpeg);
jpeg_destroy_decompress(&p_sys->p_jpeg);
free(p_row_pointers);
p_pic->date = p_block->i_pts > VLC_TICK_INVALID ? p_block->i_pts : p_block->i_dts;
block_Release(p_block);
decoder_QueueVideo( p_dec, p_pic );
return VLCDEC_SUCCESS;
error:
jpeg_destroy_decompress(&p_sys->p_jpeg);
free(p_row_pointers);
block_Release(p_block);
return VLCDEC_SUCCESS;
}
/*
* 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);
}
/*
* 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;
if (unlikely(!p_pic))
{
return NULL;
}
block_t *p_block = block_Alloc(p_sys->i_blocksize);
if (p_block == NULL)
{
return NULL;
}
JSAMPIMAGE p_row_pointers = NULL;
unsigned long size = p_block->i_buffer;
/* 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, &size);
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;
#if JPEG_LIB_VERSION >= 70
p_sys->p_jpeg.do_fancy_downsampling = FALSE;
#endif
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 = vlc_alloc(p_pic->i_planes, sizeof(JSAMPARRAY));
if (p_row_pointers == NULL)
{
goto error;
}
for (int i = 0; i < p_pic->i_planes; i++)
{
p_row_pointers[i] = vlc_alloc(p_sys->p_jpeg.comp_info[i].v_samp_factor, sizeof(JSAMPROW) * 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);
p_block->i_buffer = size;
p_block->i_dts = p_block->i_pts = p_pic->date;
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);
}
|