* Don't trust EXIF to tell us the accurate resolution; instead, parse the JPEG data ourselves.

* Add on-the-fly downscaling for JPEGs too large to fit the JPEG_LRG DLNA profile.
This commit is contained in:
Justin Maggard 2009-10-13 01:20:19 +00:00
parent ecec767f17
commit 1ef627ac3c
5 changed files with 99 additions and 35 deletions

4
README
View File

@ -1,9 +1,9 @@
MiniDLNA project
(c) 2009 Justin Maggard
Parts (c) 2006-2007 Thomas Bernard
Portions (c) 2006-2007 Thomas Bernard
webpage: http://sourceforge.net/projects/minidlna/
This directory contain the MiniDLNA daemon software.
This directory contains the MiniDLNA daemon software.
This software is subject to the conditions detailed in
the LICENCE file provided with this distribution.

View File

@ -33,10 +33,17 @@
#include <sys/types.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <endian.h>
#include "image_utils.h"
#include "log.h"
#if __BYTE_ORDER == __LITTLE_ENDIAN
# define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) )
#else
# define SWAP16(w) (w)
#endif
#define JPEG_QUALITY 96
#define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF)
@ -222,6 +229,59 @@ put_pix_alpha_replace(image *pimage, int32_t x, int32_t y, pix col)
pimage->buf[(y * pimage->width) + x] = col;
}
int
image_get_jpeg_resolution(const char * path, int * width, int * height)
{
FILE *img;
unsigned char buf[8];
u_int16_t offset;
int ret = 1;
img = fopen(path, "r");
if( !img )
return(-1);
fread(&buf, 2, 1, img);
if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
{
fclose(img);
return(-1);
}
memset(&buf, 0, sizeof(buf));
while( !feof(img) )
{
while( buf[0] != 0xFF && !feof(img) )
fread(&buf, 1, 1, img);
while( buf[0] == 0xFF && !feof(img) )
fread(&buf, 1, 1, img);
if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
{
fread(&buf, 7, 1, img);
*width = 0;
*height = 0;
memcpy(height, buf+3, 2);
*height = SWAP16(*height);
memcpy(width, buf+5, 2);
*width = SWAP16(*width);
ret = 0;
break;
}
else
{
offset = 0;
fread(&buf, 2, 1, img);
memcpy(&offset, buf, 2);
offset = SWAP16(offset) - 2;
fseek(img, offset, SEEK_CUR);
}
}
fclose(img);
return ret;
}
image *
image_new(int32_t width, int32_t height)
{

View File

@ -20,6 +20,9 @@ typedef struct {
void
image_free(image *pimage);
int
image_get_jpeg_resolution(const char * path, int * width, int * height);
image *
image_new_from_jpeg(const char * path, int is_file, const char * ptr, int size);

View File

@ -378,17 +378,6 @@ GetImageMetadata(const char * path, char * name)
if( !ed )
goto no_exifdata;
tag = EXIF_TAG_PIXEL_X_DIMENSION;
e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], tag);
if( e )
width = atoi( exif_entry_get_value(e, b, sizeof(b)) );
tag = EXIF_TAG_PIXEL_Y_DIMENSION;
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
if( e )
height = atoi( exif_entry_get_value(e, b, sizeof(b)) );
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height);
tag = EXIF_TAG_DATE_TIME_ORIGINAL;
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_DATE_TIME)) ) {
@ -448,8 +437,8 @@ GetImageMetadata(const char * path, char * name)
exif_data_unref(ed);
no_exifdata:
/* If EXIF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */
if( !width || !height )
/* If SOF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */
if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height )
{
infile = fopen(path, "r");
cinfo.err = jpeg_std_error(&jerr);
@ -466,6 +455,7 @@ no_exifdata:
jpeg_destroy_decompress(&cinfo);
fclose(infile);
}
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height);
if( !width || !height )
{

View File

@ -715,30 +715,41 @@ callback(void *args, int argc, char **argv, char **azColName)
"&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID, ext);
#if 1 //JPEG_RESIZE
if( dlna_pn && (!strncmp(dlna_pn, "JPEG_L", 6) || !strncmp(dlna_pn, "JPEG_M", 6)) ) {
int srcw = atoi(strsep(&resolution, "x"));
int srch = atoi(resolution);
int dstw = 640;
int dsth = ((((640<<10)/srcw)*srch)>>10);
if( dsth > 480 ) {
dsth = 480;
dstw = (((480<<10)/srch) * srcw>>10);
if( *mime == 'i' ) {
int reqw = 0, reqh = 0;
if( dlna_pn && (!strncmp(dlna_pn, "JPEG_L", 6) || !strncmp(dlna_pn, "JPEG_M", 6)) ) {
reqw = 640;
reqh = 480;
}
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
ret = sprintf(str_buf, "&lt;res ");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( passed_args->filter & FILTER_RES_RESOLUTION ) {
ret = sprintf(str_buf, "resolution=\"%dx%d\" ", dstw, dsth);
else if( !dlna_pn ) {
reqw = 4096;
reqh = 4096;
}
if( reqw ) {
int srcw = atoi(strsep(&resolution, "x"));
int srch = atoi(resolution);
int dstw = reqw;
int dsth = ((((reqw<<10)/srcw)*srch)>>10);
if( dsth > reqh ) {
dsth = reqh;
dstw = (((reqh<<10)/srch) * srcw>>10);
}
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
ret = sprintf(str_buf, "&lt;res ");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( passed_args->filter & FILTER_RES_RESOLUTION ) {
ret = sprintf(str_buf, "resolution=\"%dx%d\" ", dstw, dsth);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:DLNA.ORG_PN=JPEG_%s\"&gt;"
"http://%s:%d/Resized/%s.jpg?width=%d,height=%d"
"&lt;/res&gt;",
mime, (reqw==640)?"SM":"LRG", lan_addr[0].str, runtime_vars.port,
detailID, dstw, dsth);
}
ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Resized/%s.jpg?width=%d,height=%d"
"&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, runtime_vars.port,
detailID, dstw, dsth);
}
#endif
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);