* Add support for more DLNA video profiles.

* Fix bug where non-DLNA formats would still get parsed into DLNA profiles.
This commit is contained in:
Justin Maggard 2011-02-15 01:36:09 +00:00
parent 87e71c443a
commit be1c1c5644

View File

@ -43,6 +43,16 @@
#include "sql.h"
#include "log.h"
#ifndef FF_PROFILE_H264_BASELINE
#define FF_PROFILE_H264_BASELINE 66
#endif
#ifndef FF_PROFILE_H264_MAIN
#define FF_PROFILE_H264_MAIN 77
#endif
#ifndef FF_PROFILE_H264_HIGH
#define FF_PROFILE_H264_HIGH 100
#endif
#define FLAG_TITLE 0x00000001
#define FLAG_ARTIST 0x00000002
#define FLAG_ALBUM 0x00000004
@ -76,6 +86,7 @@ enum audio_profiles {
/* This function shamelessly copied from libdlna */
#define MPEG_TS_SYNC_CODE 0x47
#define MPEG_TS_PACKET_LENGTH 188 /* prepends 4 bytes to TS packet */
#define MPEG_TS_PACKET_LENGTH_DLNA 192 /* prepends 4 bytes to TS packet */
int
dlna_timestamp_is_present(const char * filename)
@ -93,15 +104,13 @@ dlna_timestamp_is_present(const char * filename)
{
if (buffer[i + MPEG_TS_PACKET_LENGTH_DLNA] == MPEG_TS_SYNC_CODE)
{
if (buffer[i] == 0x00 && buffer [i+1] == 0x00 &&
buffer [i+2] == 0x00 && buffer [i+3] == 0x00)
{
if (buffer[i+MPEG_TS_PACKET_LENGTH] == 0x00 &&
buffer[i+MPEG_TS_PACKET_LENGTH+1] == 0x00 &&
buffer[i+MPEG_TS_PACKET_LENGTH+2] == 0x00 &&
buffer[i+MPEG_TS_PACKET_LENGTH+3] == 0x00)
break;
}
else
{
return 1;
}
}
}
}
@ -125,13 +134,6 @@ is_tivo_file(const char * path)
}
#endif
/* This function taken from libavutil (ffmpeg), because it's not included with all versions of libavutil. */
int
get_fourcc(const char *s)
{
return (s[0]) + (s[1]<<8) + (s[2]<<16) + (s[3]<<24);
}
void
check_for_captions(const char * path, sqlite_int64 detailID)
{
@ -368,7 +370,7 @@ GetAudioMetadata(const char * path, char * name)
}
if( song.dlna_pn )
asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01", song.dlna_pn);
asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", song.dlna_pn);
if( song.year )
asprintf(&m.date, "%04d-01-01", song.year);
asprintf(&m.duration, "%d:%02d:%02d.%03d",
@ -376,10 +378,9 @@ GetAudioMetadata(const char * path, char * name)
(song.song_length/60000%60),
(song.song_length/1000%60),
(song.song_length%1000));
m.title = song.title;
if( m.title && *m.title )
if( song.title && *song.title )
{
m.title = trim(m.title);
m.title = trim(song.title);
if( (esc_tag = escape_tag(m.title)) )
{
free_flags |= FLAG_TITLE;
@ -394,34 +395,38 @@ GetAudioMetadata(const char * path, char * name)
{
if( song.contributor[i] && *song.contributor[i] )
{
m.artist = trim(song.contributor[i]);
if( strlen(m.artist) > 48 )
m.creator = trim(song.contributor[i]);
if( strlen(m.creator) > 48 )
{
free_flags |= FLAG_ARTIST;
m.artist = strdup("Various Artists");
m.creator = strdup("Various Artists");
}
else if( (esc_tag = escape_tag(m.artist)) )
else if( (esc_tag = escape_tag(m.creator)) )
{
free_flags |= FLAG_ARTIST;
m.artist = esc_tag;
m.creator = esc_tag;
}
m.creator = m.artist;
m.artist = m.creator;
break;
}
}
/* If there is a band associated with the album, use it for virtual containers. */
if( (i != ROLE_BAND) && song.contributor[ROLE_BAND] && *song.contributor[ROLE_BAND] )
if( (i != ROLE_BAND) && (i != ROLE_ALBUMARTIST) )
{
m.creator = trim(song.contributor[ROLE_BAND]);
if( strlen(m.creator) > 48 )
if( song.contributor[ROLE_BAND] && *song.contributor[ROLE_BAND] )
{
free_flags |= FLAG_CREATOR;
m.creator = strdup("Various Artists");
}
else if( (esc_tag = escape_tag(m.creator)) )
{
free_flags |= FLAG_CREATOR;
m.creator = esc_tag;
i = ROLE_BAND;
m.artist = trim(song.contributor[i]);
if( strlen(m.artist) > 48 )
{
free_flags |= FLAG_CREATOR;
m.artist = strdup("Various Artists");
}
else if( (esc_tag = escape_tag(m.artist)) )
{
free_flags |= FLAG_CREATOR;
m.artist = esc_tag;
}
}
}
if( song.album && *song.album )
@ -639,15 +644,19 @@ GetVideoMetadata(const char * path, char * name)
AVFormatContext *ctx;
int audio_stream = -1, video_stream = -1;
enum audio_profiles audio_profile = PROFILE_AUDIO_UNKNOWN;
tsinfo_t *ts;
ts_timestamp_t ts_timestamp = NONE;
char fourcc[4];
int off;
int duration, hours, min, sec, ms;
aac_object_type_t aac_type = AAC_INVALID;
sqlite_int64 album_art = 0;
char nfo[PATH_MAX], *ext;
struct song_metadata video;
metadata_t m;
uint32_t free_flags = 0xFFFFFFFF;
memset(&m, '\0', sizeof(m));
// memset(&free_flags, 0xFF, sizeof(free_flags));
memset(&video, '\0', sizeof(video));
//DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing video %s...\n", name);
if ( stat(path, &file) != 0 )
@ -807,7 +816,33 @@ GetVideoMetadata(const char * path, char * name)
ms = (int)(ctx->duration / (AV_TIME_BASE/1000) % 1000);
asprintf(&m.duration, "%d:%02d:%02d.%03d", hours, min, sec, ms);
}
/* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers -- not AVI. */
/* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers. Skip DLNA parsing for everything else. */
if( strcmp(ctx->iformat->name, "avi") == 0 )
{
asprintf(&m.mime, "video/x-msvideo");
if( ctx->streams[video_stream]->codec->codec_id == CODEC_ID_MPEG4 )
{
fourcc[0] = ctx->streams[video_stream]->codec->codec_tag & 0xff;
fourcc[1] = ctx->streams[video_stream]->codec->codec_tag>>8 & 0xff;
fourcc[2] = ctx->streams[video_stream]->codec->codec_tag>>16 & 0xff;
fourcc[3] = ctx->streams[video_stream]->codec->codec_tag>>24 & 0xff;
if( memcmp(fourcc, "XVID", 4) == 0 ||
memcmp(fourcc, "DX50", 4) == 0 ||
memcmp(fourcc, "DIVX", 4) == 0 )
asprintf(&m.creator, "DiVX");
}
}
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 &&
ends_with(path, ".mov") )
asprintf(&m.mime, "video/quicktime");
else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
asprintf(&m.mime, "video/x-matroska");
else if( strcmp(ctx->iformat->name, "flv") == 0 )
asprintf(&m.mime, "video/x-flv");
if( m.mime )
goto video_no_dlna;
switch( ctx->streams[video_stream]->codec->codec_id )
{
case CODEC_ID_MPEG1VIDEO:
@ -822,48 +857,27 @@ GetVideoMetadata(const char * path, char * name)
}
break;
case CODEC_ID_MPEG2VIDEO:
m.dlna_pn = malloc(64);
off = sprintf(m.dlna_pn, "MPEG_");
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 TS\n", video_stream, basename(path), m.resolution);
char res;
tsinfo_t * ts = ctx->priv_data;
if( ts->packet_size == 192 )
off += sprintf(m.dlna_pn+off, "TS_");
if( (ctx->streams[video_stream]->codec->width >= 1280) &&
(ctx->streams[video_stream]->codec->height >= 720) )
{
asprintf(&m.dlna_pn, "MPEG_TS_HD_NA;%s", dlna_no_conv);
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
off += sprintf(m.dlna_pn+off, "HD_NA");
}
else if( ts->packet_size == 188 )
{
if( (ctx->streams[video_stream]->codec->width >= 1280) &&
(ctx->streams[video_stream]->codec->height >= 720) )
res = 'H';
else
res = 'S';
asprintf(&m.dlna_pn, "MPEG_TS_%cD_NA_ISO;%s", res, dlna_no_conv);
asprintf(&m.mime, "video/mpeg");
}
}
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 PS\n", video_stream, basename(path), m.resolution);
char region[5];
if( (ctx->streams[video_stream]->codec->height == 576) ||
(ctx->streams[video_stream]->codec->height == 288) )
strcpy(region, "PAL");
else
strcpy(region, "NTSC");
asprintf(&m.dlna_pn, "MPEG_PS_%s;%s", region, dlna_no_conv);
asprintf(&m.mime, "video/mpeg");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, basename(path), m.resolution);
}
break;
case CODEC_ID_H264:
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
{
tsinfo_t * ts = ctx->priv_data;
{
off += sprintf(m.dlna_pn+off, "SD_");
if( (ctx->streams[video_stream]->codec->height == 576) ||
(ctx->streams[video_stream]->codec->height == 288) )
off += sprintf(m.dlna_pn+off, "EU");
else
off += sprintf(m.dlna_pn+off, "NA");
}
ts = ctx->priv_data;
if( ts->packet_size == 192 )
{
if( dlna_timestamp_is_present(path) )
@ -871,127 +885,434 @@ GetVideoMetadata(const char * path, char * name)
else
ts_timestamp = EMPTY;
}
char res = '\0';
if( ctx->streams[video_stream]->codec->width <= 720 &&
ctx->streams[video_stream]->codec->height <= 576 &&
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
res = 'S';
else if( ctx->streams[video_stream]->codec->width <= 1920 &&
ctx->streams[video_stream]->codec->height <= 1152 &&
ctx->streams[video_stream]->codec->bit_rate <= 20000000 )
res = 'H';
if( res )
else if( ts->packet_size != 188 )
{
switch( audio_profile )
{
case PROFILE_AUDIO_MP3:
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_MPEG1_L3%s;%s",
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"", dlna_no_conv);
break;
case PROFILE_AUDIO_AC3:
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AC3%s;%s",
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"", dlna_no_conv);
break;
case PROFILE_AUDIO_AAC_MULT5:
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AAC_MULT5%s;%s",
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"", dlna_no_conv);
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for TS/AVC/%cD file %s\n", res, basename(path));
break;
}
if( m.dlna_pn && (ts_timestamp != NONE) )
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
DPRINTF(E_DEBUG, L_METADATA, "Invalid TS packet size [%s]\n", basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
}
else
switch( ts_timestamp )
{
DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%dx%d, %dbps]\n",
ctx->streams[video_stream]->codec->width,
ctx->streams[video_stream]->codec->height,
ctx->streams[video_stream]->codec->bit_rate);
case NONE:
asprintf(&m.mime, "video/mpeg");
off += sprintf(m.dlna_pn+off, "_ISO");
break;
case VALID:
off += sprintf(m.dlna_pn+off, "_T");
case EMPTY:
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
default:
break;
}
}
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 PS\n", video_stream, basename(path), m.resolution);
off += sprintf(m.dlna_pn+off, "PS_");
if( (ctx->streams[video_stream]->codec->height == 576) ||
(ctx->streams[video_stream]->codec->height == 288) )
off += sprintf(m.dlna_pn+off, "PAL");
else
off += sprintf(m.dlna_pn+off, "NTSC");
asprintf(&m.mime, "video/mpeg");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, basename(path), m.resolution);
free(m.dlna_pn);
m.dlna_pn = NULL;
}
if( m.dlna_pn )
sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
break;
case CODEC_ID_H264:
m.dlna_pn = malloc(128);
off = sprintf(m.dlna_pn, "AVC_");
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
{
off += sprintf(m.dlna_pn+off, "TS_");
switch( ctx->streams[video_stream]->codec->profile )
{
case FF_PROFILE_H264_BASELINE:
off += sprintf(m.dlna_pn+off, "BL_");
if( ctx->streams[video_stream]->codec->width <= 352 &&
ctx->streams[video_stream]->codec->height <= 288 &&
ctx->streams[video_stream]->codec->bit_rate <= 384000 )
{
off += sprintf(m.dlna_pn+off, "CIF15_");
}
else if( ctx->streams[video_stream]->codec->width <= 352 &&
ctx->streams[video_stream]->codec->height <= 288 &&
ctx->streams[video_stream]->codec->bit_rate <= 3000000 )
{
off += sprintf(m.dlna_pn+off, "CIF30_");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%s, %dx%d, %dbps : %s]\n",
m.dlna_pn,
ctx->streams[video_stream]->codec->width,
ctx->streams[video_stream]->codec->height,
ctx->streams[video_stream]->codec->bit_rate,
basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
}
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "Unknown AVC profile %d; assuming MP. [%s]\n",
ctx->streams[video_stream]->codec->profile, basename(path));
case FF_PROFILE_H264_MAIN:
off += sprintf(m.dlna_pn+off, "MP_");
if( ctx->streams[video_stream]->codec->width <= 720 &&
ctx->streams[video_stream]->codec->height <= 576 &&
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
{
off += sprintf(m.dlna_pn+off, "SD_");
}
else if( ctx->streams[video_stream]->codec->width <= 1920 &&
ctx->streams[video_stream]->codec->height <= 1152 &&
ctx->streams[video_stream]->codec->bit_rate <= 20000000 )
{
off += sprintf(m.dlna_pn+off, "HD_");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%s, %dx%d, %dbps : %s]\n",
m.dlna_pn,
ctx->streams[video_stream]->codec->width,
ctx->streams[video_stream]->codec->height,
ctx->streams[video_stream]->codec->bit_rate,
basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
}
break;
}
if( !m.dlna_pn )
break;
switch( audio_profile )
{
case PROFILE_AUDIO_MP3:
off += sprintf(m.dlna_pn+off, "MPEG1_L3");
break;
case PROFILE_AUDIO_AC3:
off += sprintf(m.dlna_pn+off, "AC3");
break;
case PROFILE_AUDIO_AAC_MULT5:
off += sprintf(m.dlna_pn+off, "AAC_MULT5");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file [%s]\n",
m.dlna_pn, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
if( !m.dlna_pn )
break;
ts = ctx->priv_data;
if( ts->packet_size == 192 )
{
if( dlna_timestamp_is_present(path) )
ts_timestamp = VALID;
else
ts_timestamp = EMPTY;
}
switch( ts_timestamp )
{
case NONE:
off += sprintf(m.dlna_pn+off, "_ISO");
break;
case VALID:
off += sprintf(m.dlna_pn+off, "_T");
case EMPTY:
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
default:
break;
}
}
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
{
/* AVC wrapped in MP4 only has SD profiles - 10 Mbps max */
if( ctx->streams[video_stream]->codec->width <= 720 &&
ctx->streams[video_stream]->codec->height <= 576 &&
ctx->streams[video_stream]->codec->bit_rate <= 10000000 &&
!ends_with(path, ".mov") )
if( ends_with(path, ".mov") )
{
switch( audio_profile )
DPRINTF(E_DEBUG, L_METADATA, "Skipping DLNA parsing for .mov file %s\n", path);
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
off += sprintf(m.dlna_pn+off, "MP4_");
switch( ctx->streams[video_stream]->codec->profile ) {
case FF_PROFILE_H264_BASELINE:
if( ctx->streams[video_stream]->codec->width <= 352 &&
ctx->streams[video_stream]->codec->height <= 288 )
{
case PROFILE_AUDIO_AC3:
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AC3;%s", dlna_no_conv);
break;
case PROFILE_AUDIO_AAC_MULT5:
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AAC_MULT5;%s", dlna_no_conv);
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for MP4/AVC/SD file %s\n", basename(path));
if( ctx->bit_rate < 600000 )
{
off += sprintf(m.dlna_pn+off, "BL_CIF15_");
}
else if( ctx->bit_rate < 5000000 )
{
off += sprintf(m.dlna_pn+off, "BL_CIF30_");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Unhandled MP4_BL bit rate on %s\n", basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
switch( audio_profile )
{
case PROFILE_AUDIO_AMR:
off += sprintf(m.dlna_pn+off, "AMR");
break;
case PROFILE_AUDIO_AAC:
off += sprintf(m.dlna_pn+off, "AAC_");
if( ctx->bit_rate < 540000 )
{
off += sprintf(m.dlna_pn+off, "540");
break;
}
else if( ctx->bit_rate < 940000 )
{
off += sprintf(m.dlna_pn+off, "940");
break;
}
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file %s\n",
m.dlna_pn, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
else if( ctx->streams[video_stream]->codec->width <= 720 &&
ctx->streams[video_stream]->codec->height <= 576 )
{
if( ctx->streams[video_stream]->codec->level == 30 &&
audio_profile == PROFILE_AUDIO_AAC &&
ctx->bit_rate <= 5000000 )
{
off += sprintf(m.dlna_pn+off, "BL_L3L_SD_AAC");
}
else if( ctx->streams[video_stream]->codec->level <= 31 &&
audio_profile == PROFILE_AUDIO_AAC &&
ctx->bit_rate <= 15000000 )
{
off += sprintf(m.dlna_pn+off, "BL_L31_HD_AAC");
}
}
else if( ctx->streams[video_stream]->codec->width <= 1280 &&
ctx->streams[video_stream]->codec->height <= 720 )
{
if( ctx->streams[video_stream]->codec->level <= 31 &&
audio_profile == PROFILE_AUDIO_AAC &&
ctx->bit_rate <= 15000000 )
{
off += sprintf(m.dlna_pn+off, "BL_L31_HD_AAC");
}
else if( ctx->streams[video_stream]->codec->level <= 32 &&
audio_profile == PROFILE_AUDIO_AAC &&
ctx->bit_rate <= 21000000 )
{
off += sprintf(m.dlna_pn+off, "BL_L32_HD_AAC");
}
}
if( strlen(m.dlna_pn) <= 8 )
{
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file %s\n",
m.dlna_pn, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
}
break;
case FF_PROFILE_H264_MAIN:
off += sprintf(m.dlna_pn+off, "MP_");
/* AVC MP4 SD profiles - 10 Mbps max */
if( ctx->streams[video_stream]->codec->width <= 720 &&
ctx->streams[video_stream]->codec->height <= 576 &&
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
{
sprintf(m.dlna_pn+off, "SD_");
switch( audio_profile )
{
case PROFILE_AUDIO_AC3:
off += sprintf(m.dlna_pn+off, "AC3");
break;
case PROFILE_AUDIO_AAC_MULT5:
off += sprintf(m.dlna_pn+off, "AAC_MULT5");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for MP4/AVC/SD file %s\n",
basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
else if( ctx->streams[video_stream]->codec->width <= 1280 &&
ctx->streams[video_stream]->codec->height <= 720 &&
ctx->streams[video_stream]->codec->bit_rate <= 15000000 &&
audio_profile == PROFILE_AUDIO_AAC )
{
off += sprintf(m.dlna_pn+off, "HD_720p_AAC");
}
else if( ctx->streams[video_stream]->codec->width <= 1920 &&
ctx->streams[video_stream]->codec->height <= 1080 &&
ctx->streams[video_stream]->codec->bit_rate <= 21000000 &&
audio_profile == PROFILE_AUDIO_AAC )
{
off += sprintf(m.dlna_pn+off, "HD_1080i_AAC");
}
if( strlen(m.dlna_pn) <= 11 )
{
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file %s\n",
m.dlna_pn, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
}
break;
case FF_PROFILE_H264_HIGH:
if( ctx->streams[video_stream]->codec->width <= 1920 &&
ctx->streams[video_stream]->codec->height <= 1080 &&
ctx->streams[video_stream]->codec->bit_rate <= 25000000 &&
audio_profile == PROFILE_AUDIO_AAC )
{
off += sprintf(m.dlna_pn+off, "HP_HD_AAC");
}
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "AVC profile [%d] not recognized for file %s\n",
ctx->streams[video_stream]->codec->profile, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
if( m.dlna_pn )
sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is h.264\n", video_stream, basename(path));
break;
case CODEC_ID_MPEG4:
if( ctx->streams[video_stream]->codec->codec_tag == get_fourcc("XVID") )
fourcc[0] = ctx->streams[video_stream]->codec->codec_tag & 0xff;
fourcc[1] = ctx->streams[video_stream]->codec->codec_tag>>8 & 0xff;
fourcc[2] = ctx->streams[video_stream]->codec->codec_tag>>16 & 0xff;
fourcc[3] = ctx->streams[video_stream]->codec->codec_tag>>24 & 0xff;
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is MPEG4 [%c%c%c%c/0x%X]\n",
video_stream, basename(path),
isprint(fourcc[0]) ? fourcc[0] : '_',
isprint(fourcc[1]) ? fourcc[1] : '_',
isprint(fourcc[2]) ? fourcc[2] : '_',
isprint(fourcc[3]) ? fourcc[3] : '_',
ctx->streams[video_stream]->codec->codec_tag);
if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s XViD\n", video_stream, basename(path), m.resolution);
asprintf(&m.artist, "DiVX");
}
else if( ctx->streams[video_stream]->codec->codec_tag == get_fourcc("DX50") )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s DiVX5\n", video_stream, basename(path), m.resolution);
asprintf(&m.artist, "DiVX");
}
else if( ctx->streams[video_stream]->codec->codec_tag == get_fourcc("DIVX") )
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is DiVX\n", video_stream, basename(path));
asprintf(&m.artist, "DiVX");
}
else if( ends_with(path, ".3gp") && (strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0) )
{
asprintf(&m.mime, "video/3gpp");
switch( audio_profile )
m.dlna_pn = malloc(128);
off = sprintf(m.dlna_pn, "MPEG4_P2_");
if( ends_with(path, ".3gp") )
{
case PROFILE_AUDIO_AAC:
asprintf(&m.dlna_pn, "MPEG4_P2_3GPP_SP_L0B_AAC;%s", dlna_no_conv);
break;
case PROFILE_AUDIO_AMR:
asprintf(&m.dlna_pn, "MPEG4_P2_3GPP_SP_L0B_AMR;%s", dlna_no_conv);
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for MPEG4-P2 3GP/0x%X file %s\n",
ctx->streams[audio_stream]->codec->codec_id, basename(path));
break;
asprintf(&m.mime, "video/3gpp");
switch( audio_profile )
{
case PROFILE_AUDIO_AAC:
off += sprintf(m.dlna_pn+off, "3GPP_SP_L0B_AAC");
break;
case PROFILE_AUDIO_AMR:
off += sprintf(m.dlna_pn+off, "3GPP_SP_L0B_AMR");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for MPEG4-P2 3GP/0x%X file %s\n",
ctx->streams[audio_stream]->codec->codec_id, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is MPEG4 [%X]\n", video_stream, basename(path), ctx->streams[video_stream]->codec->codec_tag);
else
{
if( ctx->bit_rate <= 1000000 &&
audio_profile == PROFILE_AUDIO_AAC )
{
off += sprintf(m.dlna_pn+off, "MP4_ASP_AAC");
}
else if( ctx->bit_rate <= 4000000 &&
ctx->streams[video_stream]->codec->width <= 640 &&
ctx->streams[video_stream]->codec->height <= 480 &&
audio_profile == PROFILE_AUDIO_AAC )
{
off += sprintf(m.dlna_pn+off, "MP4_SP_VGA_AAC");
}
else
{
DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%dx%d, %dbps]\n",
ctx->streams[video_stream]->codec->width,
ctx->streams[video_stream]->codec->height,
ctx->bit_rate);
free(m.dlna_pn);
m.dlna_pn = NULL;
}
}
if( m.dlna_pn )
sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
}
break;
case CODEC_ID_WMV3:
case CODEC_ID_VC1:
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is VC1\n", video_stream, basename(path));
char profile[5]; profile[0] = '\0';
asprintf(&m.mime, "video/x-ms-wmv");
if( (ctx->streams[video_stream]->codec->width <= 352) &&
(ctx->streams[video_stream]->codec->height <= 288) &&
(ctx->bit_rate/8 <= 384000) )
/* I'm not 100% sure this is correct, but it works on everything I could get my hands on */
if( ctx->streams[video_stream]->codec->extradata_size > 0 )
{
if( !((ctx->streams[video_stream]->codec->extradata[0] >> 3) & 1) )
ctx->streams[video_stream]->codec->level = 0;
if( !((ctx->streams[video_stream]->codec->extradata[0] >> 6) & 1) )
ctx->streams[video_stream]->codec->profile = 0;
}
case CODEC_ID_VC1:
m.dlna_pn = malloc(64);
off = sprintf(m.dlna_pn, "WMV");
DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is VC1\n", video_stream, basename(path));
asprintf(&m.mime, "video/x-ms-wmv");
if( (ctx->streams[video_stream]->codec->width <= 176) &&
(ctx->streams[video_stream]->codec->height <= 144) &&
(ctx->streams[video_stream]->codec->level == 0) )
{
off += sprintf(m.dlna_pn+off, "SPLL_");
switch( audio_profile )
{
case PROFILE_AUDIO_MP3:
asprintf(&m.dlna_pn, "WMVSPML_MP3;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "MP3");
break;
case PROFILE_AUDIO_WMA_BASE:
asprintf(&m.dlna_pn, "WMVSPML_BASE;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "BASE");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPLL/0x%X file %s\n", audio_profile, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
else if( (ctx->streams[video_stream]->codec->width <= 352) &&
(ctx->streams[video_stream]->codec->height <= 288) &&
(ctx->streams[video_stream]->codec->profile == 0) &&
(ctx->bit_rate/8 <= 384000) )
{
off += sprintf(m.dlna_pn+off, "SPML_");
switch( audio_profile )
{
case PROFILE_AUDIO_MP3:
off += sprintf(m.dlna_pn+off, "MP3");
break;
case PROFILE_AUDIO_WMA_BASE:
off += sprintf(m.dlna_pn+off, "BASE");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPML/0x%X file %s\n", audio_profile, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
@ -999,19 +1320,22 @@ GetVideoMetadata(const char * path, char * name)
(ctx->streams[video_stream]->codec->height <= 576) &&
(ctx->bit_rate/8 <= 10000000) )
{
off += sprintf(m.dlna_pn+off, "MED_");
switch( audio_profile )
{
case PROFILE_AUDIO_WMA_PRO:
asprintf(&m.dlna_pn, "WMVMED_PRO;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "PRO");
break;
case PROFILE_AUDIO_WMA_FULL:
asprintf(&m.dlna_pn, "WMVMED_FULL;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "FULL");
break;
case PROFILE_AUDIO_WMA_BASE:
asprintf(&m.dlna_pn, "WMVMED_BASE;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "BASE");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVMED/0x%X file %s\n", audio_profile, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
@ -1019,19 +1343,24 @@ GetVideoMetadata(const char * path, char * name)
(ctx->streams[video_stream]->codec->height <= 1080) &&
(ctx->bit_rate/8 <= 20000000) )
{
off += sprintf(m.dlna_pn+off, "HIGH_");
switch( audio_profile )
{
case PROFILE_AUDIO_WMA_PRO:
asprintf(&m.dlna_pn, "WMVHIGH_PRO;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "PRO");
break;
case PROFILE_AUDIO_WMA_FULL:
asprintf(&m.dlna_pn, "WMVHIGH_FULL;%s", dlna_no_conv);
off += sprintf(m.dlna_pn+off, "FULL");
break;
default:
DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVHIGH/0x%X file %s\n", audio_profile, basename(path));
free(m.dlna_pn);
m.dlna_pn = NULL;
break;
}
}
if( m.dlna_pn )
sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
break;
case CODEC_ID_MSMPEG4V3:
asprintf(&m.mime, "video/x-msvideo");
@ -1044,9 +1373,7 @@ GetVideoMetadata(const char * path, char * name)
{
if( strcmp(ctx->iformat->name, "avi") == 0 )
asprintf(&m.mime, "video/x-msvideo");
else if( strcmp(ctx->iformat->name, "mpegts") == 0 ||
strcmp(ctx->iformat->name, "mpeg") == 0 ||
strcmp(ctx->iformat->name, "mpegvideo") == 0 )
else if( strncmp(ctx->iformat->name, "mpeg", 4) == 0 )
asprintf(&m.mime, "video/mpeg");
else if( strcmp(ctx->iformat->name, "asf") == 0 )
asprintf(&m.mime, "video/x-ms-wmv");
@ -1055,15 +1382,44 @@ GetVideoMetadata(const char * path, char * name)
asprintf(&m.mime, "video/quicktime");
else
asprintf(&m.mime, "video/mp4");
else if( strcmp(ctx->iformat->name, "matroska") == 0 ||
strcmp(ctx->iformat->name, "matroska,webm") == 0 )
else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
asprintf(&m.mime, "video/x-matroska");
else if( strcmp(ctx->iformat->name, "flv") == 0 )
asprintf(&m.mime, "video/x-flv");
else
DPRINTF(E_WARN, L_METADATA, "%s: Unhandled format: %s\n", path, ctx->iformat->name);
}
if( strcmp(ctx->iformat->name, "asf") == 0 )
{
if( readtags((char *)path, &video, &file, "en_US", "asf") == 0 )
{
if( video.title && *video.title )
{
m.title = strdup(trim(video.title));
}
if( video.genre && *video.genre )
{
m.genre = strdup(trim(video.genre));
}
if( video.contributor[ROLE_TRACKARTIST] && *video.contributor[ROLE_TRACKARTIST] )
{
m.artist = strdup(trim(video.contributor[ROLE_TRACKARTIST]));
}
if( video.contributor[ROLE_ALBUMARTIST] && *video.contributor[ROLE_ALBUMARTIST] )
{
m.creator = strdup(trim(video.contributor[ROLE_ALBUMARTIST]));
}
else
{
m.creator = m.artist;
free_flags &= ~FLAG_CREATOR;
}
}
}
video_no_dlna:
av_close_input_file(ctx);
#ifdef TIVO_SUPPORT
if( ends_with(path, ".TiVo") && is_tivo_file(path) )
{
@ -1071,17 +1427,20 @@ GetVideoMetadata(const char * path, char * name)
asprintf(&m.mime, "video/x-tivo-mpeg");
}
#endif
album_art = find_album_art(path, NULL, 0);
if( !m.title )
m.title = strdup(name);
album_art = find_album_art(path, video.image, video.image_size);
freetags(&video);
ret = sql_exec(db, "INSERT into DETAILS"
" (PATH, SIZE, TIMESTAMP, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
" CREATOR, TITLE, COMMENT, DLNA_PN, MIME, ALBUM_ART) "
" TITLE, CREATOR, ARTIST, GENRE, COMMENT, DLNA_PN, MIME, ALBUM_ART) "
"VALUES"
" (%Q, %lld, %ld, %Q, %Q, %Q, %Q, %Q, %Q, %Q, '%q', %Q, %Q, '%q', %lld);",
" (%Q, %lld, %ld, %Q, %Q, %Q, %Q, %Q, %Q, '%q', %Q, %Q, %Q, %Q, %Q, '%q', %lld);",
path, file.st_size, file.st_mtime, m.duration,
m.date, m.channels,
m.bitrate, m.frequency, m.resolution, m.artist,
m.title ? m.title : name, m.comment, m.dlna_pn,
m.date, m.channels, m.bitrate, m.frequency, m.resolution,
m.title, m.creator, m.artist, m.genre, m.comment, m.dlna_pn,
m.mime, album_art);
if( ret != SQLITE_OK )
{