vlc | branch: master | Wayne McDougall <[email protected]> | Wed Apr 20 22:39:11 2016 +1200| [16b5785805eba9e778ede9deda08d76635b9e710] | committer: Jean-Baptiste Kempf
Automatically orient JPEG image based on orientation flag, if present Signed-off-by: Jean-Baptiste Kempf <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=16b5785805eba9e778ede9deda08d76635b9e710 --- NEWS | 1 + modules/codec/jpeg.c | 225 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 225 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 5233b81..dd37984 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,7 @@ Decoder: supporting H.263, H.264/MPEG-4 AVC, MPEG-4 Part 2, and DV depending on device and OS version * Support for the OggSpots video codec + * JPEG images correctly oriented using embedded orientation tag, if present Demuxers: * Support HD-DVD .evo (H.264, VC-1, MPEG-2, PCM, AC-3, E-AC3, MLP, DTS) diff --git a/modules/codec/jpeg.c b/modules/codec/jpeg.c index fdb91c7..981d077 100644 --- a/modules/codec/jpeg.c +++ b/modules/codec/jpeg.c @@ -1,7 +1,7 @@ /***************************************************************************** * jpeg.c: jpeg decoder module making use of libjpeg. ***************************************************************************** - * Copyright (C) 2013-2014 VLC authors and VideoLAN + * Copyright (C) 2013-2014,2016 VLC authors and VideoLAN * * Authors: Maxim Bublis <[email protected]> * @@ -182,6 +182,220 @@ static int OpenDecoder(vlc_object_t *p_this) } /* + * 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 endian parameter tells us which. + * 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 + +LOCAL( unsigned short ) +de_get16( void * ptr, uint endian ) { + unsigned short val; + + memcpy( &val, ptr, sizeof( val ) ); + if ( endian == G_BIG_ENDIAN ) + { + #ifndef WORDS_BIGENDIAN + val = bswap16( val ); + #endif + } + else + { + #ifdef WORDS_BIGENDIAN + val = bswap16( val ); + #endif + } + return val; +} + +LOCAL( unsigned int ) +de_get32( void * ptr, uint endian ) { + unsigned int val; + + memcpy( &val, ptr, sizeof( val ) ); + if ( endian == G_BIG_ENDIAN ) + { + #ifndef WORDS_BIGENDIAN + val = bswap32( val ); + #endif + } + else + { + #ifdef WORDS_BIGENDIAN + val = bswap32( val ); + #endif + } + return val; +} + +/* + * Look through the meta data in the libjpeg decompress structure to determine + * if an EXIF Orientation tag is present. If so return its value (1-8), + * otherwise return 0 + * + * 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 +*/ +LOCAL( int ) +jpeg_GetOrientation( j_decompress_ptr cinfo ) +{ + + 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 */ + + jpeg_saved_marker_ptr exif_marker; /* Location of the Exif APP1 marker */ + jpeg_saved_marker_ptr cmarker; /* Location to check for Exif APP1 marker */ + + const char leth[] = { 0x49, 0x49, 0x2a, 0x00 }; /* Little endian TIFF header */ + const char beth[] = { 0x4d, 0x4d, 0x00, 0x2a }; /* Big endian TIFF header */ + + #define EXIF_JPEG_MARKER 0xE1 + #define EXIF_IDENT_STRING "Exif\000\000" + #define EXIF_ORIENT_TAGID 0x112 + + /* check for Exif marker (also called the APP1 marker) */ + exif_marker = NULL; + cmarker = cinfo->marker_list; + + while ( cmarker ) + { + if ( cmarker->marker == EXIF_JPEG_MARKER ) + { + /* The Exif APP1 marker should contain a unique + identification string ("Exif\0\0"). Check for it. */ + if ( !memcmp( cmarker->data, EXIF_IDENT_STRING, 6 ) ) + { + exif_marker = cmarker; + } + } + cmarker = cmarker->next; + } + + /* Did we find the Exif APP1 marker? */ + if ( exif_marker == NULL ) + return 0; + + /* Do we have enough data? */ + if ( exif_marker->data_length < 32 ) + return 0; + + /* Check for TIFF header and catch endianess */ + 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 endianess. + 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( &exif_marker->data[i], leth, 4 ) == 0 ) + { + endian = G_LITTLE_ENDIAN; + } + /* Big endian TIFF header */ + else + if ( memcmp( &exif_marker->data[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 0; + + /* Read out the offset pointer to IFD0 */ + offset = de_get32( &exif_marker->data[i] + 4, endian ); + i = i + offset; + + /* Check that we still are within the buffer and can read the tag count */ + + if ( ( i + 2 ) > exif_marker->data_length ) + return 0; + + /* 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( &exif_marker->data[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 ( ( i + tags * 12 ) > exif_marker->data_length ) + return 0; + + /* Check through IFD0 for tags of interest */ + while ( tags-- ) + { + tag_type = de_get16( &exif_marker->data[i], endian ); + /* Is this the orientation tag? */ + if ( tag_type == EXIF_ORIENT_TAGID ) + { + type = de_get16( &exif_marker->data[i + 2], endian ); + count = de_get32( &exif_marker->data[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 0; + + /* Return the orientation value. Within the 12-byte block, the + pointer to the actual data is at offset 8. */ + ret = de_get16( &exif_marker->data[i + 8], endian ); + return ( ret <= 8 ) ? ret : 0; + } + /* move the pointer to the next 12-byte tag field. */ + i = i + 12; + } + + return 0; /* No EXIF Orientation tag found */ +} + +/* * This function must be fed with a complete compressed frame. */ static picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block) @@ -214,6 +428,7 @@ static picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block) 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; @@ -230,6 +445,14 @@ static picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block) p_dec->fmt_out.video.i_gmask = 0x0000ff00; p_dec->fmt_out.video.i_bmask = 0x00ff0000; + 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 ); + } + /* Get a new picture */ p_pic = decoder_NewPicture(p_dec); if (!p_pic) _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
