Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/azatoth/minidlna.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Maggard <jmaggard@users.sourceforge.net>2011-02-15 04:36:09 +0300
committerJustin Maggard <jmaggard@users.sourceforge.net>2011-02-15 04:36:09 +0300
commit89f7231cb017b9e32ae9c76922efa980a3090cf2 (patch)
treee52cef7dc748760beededa42a86d31b2e00dfa92
parentfd1b82ae35c893ca4900e6e45f8baebba78b0f40 (diff)
* Add support for more DLNA video profiles.
* Fix bug where non-DLNA formats would still get parsed into DLNA profiles.
-rw-r--r--metadata.c661
1 files changed, 510 insertions, 151 deletions
diff --git a/metadata.c b/metadata.c
index de05905..484ea66 100644
--- a/metadata.c
+++ b/metadata.c
@@ -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,162 @@ 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 )
+ else
{
- if( (ctx->streams[video_stream]->codec->width >= 1280) &&
- (ctx->streams[video_stream]->codec->height >= 720) )
- res = 'H';
+ 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
- res = 'S';
- asprintf(&m.dlna_pn, "MPEG_TS_%cD_NA_ISO;%s", res, dlna_no_conv);
- asprintf(&m.mime, "video/mpeg");
+ off += sprintf(m.dlna_pn+off, "NA");
+ }
+ ts = ctx->priv_data;
+ if( ts->packet_size == 192 )
+ {
+ if( dlna_timestamp_is_present(path) )
+ ts_timestamp = VALID;
+ else
+ ts_timestamp = EMPTY;
+ }
+ else if( ts->packet_size != 188 )
+ {
+ DPRINTF(E_DEBUG, L_METADATA, "Invalid TS packet size [%s]\n", basename(path));
+ free(m.dlna_pn);
+ m.dlna_pn = NULL;
+ }
+ switch( ts_timestamp )
+ {
+ 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);
- char region[5];
+ off += sprintf(m.dlna_pn+off, "PS_");
if( (ctx->streams[video_stream]->codec->height == 576) ||
(ctx->streams[video_stream]->codec->height == 288) )
- strcpy(region, "PAL");
+ off += sprintf(m.dlna_pn+off, "PAL");
else
- strcpy(region, "NTSC");
- asprintf(&m.dlna_pn, "MPEG_PS_%s;%s", region, dlna_no_conv);
+ 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 )
{
- tsinfo_t * ts = ctx->priv_data;
+ 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) )
@@ -871,127 +1020,299 @@ 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 )
+ switch( ts_timestamp )
{
- 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) )
+ 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
+ }
+ else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
+ {
+ if( ends_with(path, ".mov") )
{
- 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);
+ 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 )
+ {
+ 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;
}
}
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
+ 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:
+ 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 )
{
- /* 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") )
+ m.dlna_pn = malloc(128);
+ off = sprintf(m.dlna_pn, "MPEG4_P2_");
+
+ if( ends_with(path, ".3gp") )
{
+ asprintf(&m.mime, "video/3gpp");
switch( audio_profile )
{
- case PROFILE_AUDIO_AC3:
- asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AC3;%s", dlna_no_conv);
+ case PROFILE_AUDIO_AAC:
+ off += sprintf(m.dlna_pn+off, "3GPP_SP_L0B_AAC");
break;
- case PROFILE_AUDIO_AAC_MULT5:
- asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AAC_MULT5;%s", dlna_no_conv);
+ 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 MP4/AVC/SD file %s\n", basename(path));
+ 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
+ {
+ 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);
}
- 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") )
- {
- 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") )
+ case CODEC_ID_WMV3:
+ /* 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 )
{
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is DiVX\n", video_stream, basename(path));
- asprintf(&m.artist, "DiVX");
+ 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;
}
- else if( ends_with(path, ".3gp") && (strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 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) )
{
- asprintf(&m.mime, "video/3gpp");
+ off += sprintf(m.dlna_pn+off, "SPLL_");
switch( audio_profile )
{
- case PROFILE_AUDIO_AAC:
- asprintf(&m.dlna_pn, "MPEG4_P2_3GPP_SP_L0B_AAC;%s", dlna_no_conv);
+ case PROFILE_AUDIO_MP3:
+ off += sprintf(m.dlna_pn+off, "MP3");
break;
- case PROFILE_AUDIO_AMR:
- asprintf(&m.dlna_pn, "MPEG4_P2_3GPP_SP_L0B_AMR;%s", dlna_no_conv);
+ case PROFILE_AUDIO_WMA_BASE:
+ off += sprintf(m.dlna_pn+off, "BASE");
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));
+ 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
- {
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is MPEG4 [%X]\n", video_stream, basename(path), ctx->streams[video_stream]->codec->codec_tag);
- }
- 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) )
+ 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:
- 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 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 )
{