Permalink
Browse files

Determine mime type correctly for LPCM wav files for PS3.

Added lots of trace information to give insight in the decision making process.
  • Loading branch information...
1 parent 6a7fb20 commit 50b75f014da892157a36aa83ac6b3a5a8a48ac49 @Raptor399 Raptor399 committed Sep 8, 2013
@@ -191,7 +191,7 @@ TranscodeVideo = MPEGPSAC3
# TranscodeAudio: Profile to use for transcoding audio files
# Currently supported: "LPCM", "MP3" or "WAV".
# Default: LPCM
-TranscodeAudio = WAV
+TranscodeAudio = LPCM
# DefaultVBVBufSize: Whether or not to use the default DVD buffer size. Setting
# this to "false" means a greater bitrate and faster encoding, but it can
@@ -487,10 +487,21 @@ AutoExifRotate = true
# WMV
# - VC-1(WMA Standard V2)
#
+# Taken from: http://manuals.playstation.net/document/en/ps3/current/music/filetypes.html
+#
+# The following types of files can be played under (Music).
+# Memory Stick Audio Format(ATRAC)
+# MP3
+# - MPEG-1/2 Audio Layer3
+# - MP3 Surround
+# MP4(MPEG-4 AAC)
+# WAVE(Linear PCM)
+# WMA
+#
# ============================================================================
#
# Our PS3-specific notes:
-# None of the entries declare DTS as a supported audio codec.
+# None of the video format entries declares DTS as a supported audio codec.
# No H.264 for AVI files, plus specific MediaInfo attributes for better
# auto-detection and no gmc support.
# WMV files are supported, but not with 5.1 audio.
@@ -504,12 +515,14 @@ Supported = f:mp4 v:mp4|h264 a:aac m:vi
Supported = f:wmv v:wmv|vc1 a:wma m:video/x-ms-wmv n:2
# Supported audio formats:
-Supported = f:wav a:dts|lpcm n:6 s:48000 m:audio/wav
-Supported = f:wav n:2 s:48000 m:audio/wav
-Supported = f:mp3 n:2 m:audio/mpeg
-Supported = f:aac n:2 m:audio/x-m4a a:(?!alac).+
-Supported = f:wma n:2 m:audio/x-ms-wma
-Supported = f:atrac n:2 m:audio/x-oma
+Supported = f:wav a:dts n:6 s:48000 m:audio/wav
+Supported = f:wav a:lpcm n:6 s:48000 m:audio/l16
+Supported = f:wav n:2 s:48000 m:audio/wav
+Supported = f:mp3 n:2 m:audio/mpeg
+Supported = f:aac a:(?!alac).+ n:2 m:audio/x-m4a
+Supported = f:wma n:2 m:audio/x-ms-wma
+Supported = f:atrac n:2 m:audio/x-oma
+Supported = f:lpcm a:lpcm n:6 s:48000 m:audio/l16
# Supported image formats:
Supported = f:jpg m:image/jpeg
@@ -91,6 +91,7 @@
private String maxVideoWidth;
private String mimeType;
private String videoCodec;
+ private String supportLine;
SupportSpec() {
this.mimeType = MIMETYPE_AUTO;
@@ -193,34 +194,42 @@ public boolean match(
boolean matched = false;
if (format != null && !(matched = pFormat.matcher(format).matches())) {
+ logger.trace("Format \"{}\" failed to match support line {}", format, supportLine);
return false;
}
if (matched && videoCodec != null && pVideoCodec != null && !(matched = pVideoCodec.matcher(videoCodec).matches())) {
+ logger.trace("Video codec \"{}\" failed to match support line {}", videoCodec, supportLine);
return false;
}
if (matched && audioCodec != null && pAudioCodec != null && !(matched = pAudioCodec.matcher(audioCodec).matches())) {
+ logger.trace("Audio codec \"{}\" failed to match support line {}", audioCodec, supportLine);
return false;
}
if (matched && nbAudioChannels > 0 && iMaxNbChannels > 0 && nbAudioChannels > iMaxNbChannels) {
+ logger.trace("Number of channels \"{}\" failed to match support line {}", nbAudioChannels, supportLine);
return false;
}
if (matched && frequency > 0 && iMaxFrequency > 0 && frequency > iMaxFrequency) {
+ logger.trace("Frequency \"{}\" failed to match support line {}", frequency, supportLine);
return false;
}
if (matched && bitrate > 0 && iMaxBitrate > 0 && bitrate > iMaxBitrate) {
+ logger.trace("Bit rate \"{}\" failed to match support line {}", bitrate, supportLine);
return false;
}
if (matched && videoWidth > 0 && iMaxVideoWidth > 0 && videoWidth > iMaxVideoWidth) {
+ logger.trace("Video width \"{}\" failed to match support line {}", videoWidth, supportLine);
return false;
}
if (matched && videoHeight > 0 && iMaxVideoHeight > 0 && videoHeight > iMaxVideoHeight) {
+ logger.trace("Video height \"{}\" failed to match support line {}", videoHeight, supportLine);
return false;
}
@@ -239,6 +248,12 @@ public boolean match(
}
}
+ if (matched) {
+ logger.trace("Matched support line {}", supportLine);
+ } else {
+ logger.trace("Extras failed to match support line {}", supportLine);
+ }
+
return matched;
}
}
@@ -449,6 +464,8 @@ private SupportSpec parseSupportLine(String line) {
StringTokenizer st = new StringTokenizer(line, "\t ");
SupportSpec supportSpec = new SupportSpec();
+ supportSpec.supportLine = line;
+
while (st.hasMoreTokens()) {
String token = st.nextToken();
@@ -37,7 +37,10 @@
private final ConfigurationReader configurationReader;
private FormatConfiguration formatConfiguration;
private int rank;
+
+ /** Holds mime type aliases */
private final Map<String, String> mimes;
+
private final Map<String, String> DLNAPN;
// property values
@@ -582,85 +585,94 @@ public boolean isDLNALocalizationRequired() {
return getBoolean(DLNA_LOCALIZATION_REQUIRED, false);
}
- public String getMimeType(String mimetype) {
+ /**
+ * Determine the mime type specific for this renderer, given a generic mime
+ * type. This translation takes into account all configured "Supported"
+ * lines and mime type aliases for this renderer.
+ *
+ * @param matchedMimeType
+ * The mime type to look up. Special values are
+ * <code>HTTPResource.VIDEO_TRANSCODE</code> and
+ * <code>HTTPResource.AUDIO_TRANSCODE</code>, which will be
+ * translated to the mime type of the transcoding profile
+ * configured for this renderer.
+ * @return The mime type.
+ */
+ public String getMimeType(String mimeType) {
+ if (mimeType == null) {
+ return null;
+ }
+
+ String matchedMimeType = null;
+
if (isMediaParserV2()) {
- if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
+ // Use the supported information in the configuration to determine the mime type.
+ if (HTTPResource.VIDEO_TRANSCODE.equals(mimeType)) {
if (isTranscodeToMPEGTSAC3()) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
} else if (isTranscodeToWMV()) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA);
- }
- } else if (mimetype != null && mimetype.equals(HTTPResource.AUDIO_TRANSCODE)) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.LPCM, null, null);
-
- if (mimetype != null) {
- if (isTranscodeAudioTo441()) {
- mimetype += ";rate=44100;channels=2";
- } else {
- mimetype += ";rate=48000;channels=2";
- }
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA);
+ } else {
+ // Default video transcoding mime type
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
}
-
+ } else if (HTTPResource.AUDIO_TRANSCODE.equals(mimeType)) {
if (isTranscodeToWAV()) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.WAV, null, null);
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.WAV, null, null);
} else if (isTranscodeToMP3()) {
- mimetype = getFormatConfiguration().match(FormatConfiguration.MP3, null, null);
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.MP3, null, null);
+ } else {
+ // Default audio transcoding mime type
+ matchedMimeType = getFormatConfiguration().match(FormatConfiguration.LPCM, null, null);
+
+ if (matchedMimeType != null) {
+ if (isTranscodeAudioTo441()) {
+ matchedMimeType += ";rate=44100;channels=2";
+ } else {
+ matchedMimeType += ";rate=48000;channels=2";
+ }
+ }
}
}
-
- return mimetype;
}
- if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
- if (isTranscodeToWMV()) {
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA)
- : HTTPResource.WMV_TYPEMIME;
- } else if (isTranscodeToMPEGTSAC3()) {
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3)
- : HTTPResource.MPEG_TYPEMIME;
- } else { // default: MPEGPSAC3
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3)
- : HTTPResource.MPEG_TYPEMIME;
- }
- } else if (HTTPResource.AUDIO_TRANSCODE.equals(mimetype)) {
- if (isTranscodeToWAV()) {
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.WAV, null, null)
- : HTTPResource.AUDIO_WAV_TYPEMIME;
- } else if (isTranscodeToMP3()) {
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.MP3, null, null)
- : HTTPResource.AUDIO_MP3_TYPEMIME;
- } else { // default: LPCM
- mimetype = isMediaParserV2()
- ? getFormatConfiguration().match(FormatConfiguration.LPCM, null, null)
- : HTTPResource.AUDIO_LPCM_TYPEMIME;
-
- if (isTranscodeAudioTo441()) {
- mimetype += ";rate=44100;channels=2";
+ if (matchedMimeType == null) {
+ // No match found, try without media parser v2
+ if (HTTPResource.VIDEO_TRANSCODE.equals(mimeType)) {
+ if (isTranscodeToWMV()) {
+ matchedMimeType = HTTPResource.WMV_TYPEMIME;
} else {
- mimetype += ";rate=48000;channels=2";
+ // Default video transcoding mime type
+ matchedMimeType = HTTPResource.MPEG_TYPEMIME;
+ }
+ } else if (HTTPResource.AUDIO_TRANSCODE.equals(mimeType)) {
+ if (isTranscodeToWAV()) {
+ matchedMimeType = HTTPResource.AUDIO_WAV_TYPEMIME;
+ } else if (isTranscodeToMP3()) {
+ matchedMimeType = HTTPResource.AUDIO_MP3_TYPEMIME;
+ } else {
+ // Default audio transcoding mime type
+ matchedMimeType = HTTPResource.AUDIO_LPCM_TYPEMIME;
+
+ if (isTranscodeAudioTo441()) {
+ matchedMimeType += ";rate=44100;channels=2";
+ } else {
+ matchedMimeType += ";rate=48000;channels=2";
+ }
}
}
+ }
- if (isTranscodeToMP3()) {
- mimetype = HTTPResource.AUDIO_MP3_TYPEMIME;
- }
-
- if (isTranscodeToWAV()) {
- mimetype = HTTPResource.AUDIO_WAV_TYPEMIME;
- }
+ if (matchedMimeType == null) {
+ matchedMimeType = mimeType;
}
- if (mimes.containsKey(mimetype)) {
- return mimes.get(mimetype);
+ // Apply renderer specific mime type aliases
+ if (mimes.containsKey(matchedMimeType)) {
+ return mimes.get(matchedMimeType);
}
- return mimetype;
+ return matchedMimeType;
}
/**
@@ -270,12 +270,45 @@ public String getAudioCodec() {
}
/**
- * Returns the identifying name for the audio properties.
+ * Returns a string containing all identifying audio properties.
*
- * @return The name.
+ * @return The properties string.
*/
public String toString() {
- return "Audio: " + getAudioCodec() + " / lang: " + getLang() + " / flavor: " + getFlavor() + " / ID: " + getId();
+ StringBuilder result = new StringBuilder();
+ result.append("id: ");
+ result.append(getId());
+ result.append(", lang: ");
+ result.append(getLang());
+ result.append(", flavor: ");
+ result.append(getFlavor());
+ result.append(", audio codec: ");
+ result.append(getAudioCodec());
+ result.append(", sample frequency:");
+ result.append(getSampleFrequency());
+
+ if (getAudioProperties() != null) {
+ result.append(", number of channels: ");
+ result.append(getAudioProperties().getNumberOfChannels());
+ }
+
+ result.append(", bits per sample: ");
+ result.append(getBitsperSample());
+
+ if (getArtist() != null) {
+ result.append(", artist: ");
+ result.append(getArtist());
+ result.append(", album: ");
+ result.append(getAlbum());
+ result.append(", song name: ");
+ result.append(getSongname());
+ result.append(", year: ");
+ result.append(getYear());
+ result.append(", track: ");
+ result.append(getTrack());
+ }
+
+ return result.toString();
}
@Override
@@ -1249,20 +1249,45 @@ public boolean isLossless(String codecA) {
@Override
public String toString() {
- String s = "container: " + getContainer() + " / bitrate: " + getBitrate() + " / size: " + getSize() + " / codecV: " + getCodecV() + " / duration: " + getDurationString() + " / width: " + getWidth() + " / height: " + getHeight() + " / frameRate: " + getFrameRate() + " / thumb size : " + (getThumb() != null ? getThumb().length : 0) + " / muxingMode: " + getMuxingMode();
+ StringBuilder result = new StringBuilder();
+ result.append("container: ");
+ result.append(getContainer());
+ result.append(", bitrate: ");
+ result.append(getBitrate());
+ result.append(", size: ");
+ result.append(getSize());
+ result.append(", video codec: ");
+ result.append(getCodecV());
+ result.append(", duration: ");
+ result.append(getDurationString());
+ result.append(", width: ");
+ result.append(getWidth());
+ result.append(", height: ");
+ result.append(getHeight());
+ result.append(", frame rate: ");
+ result.append(getFrameRate());
+
+ if (getThumb() != null) {
+ result.append(", thumb size : ");
+ result.append(getThumb().length);
+ }
+
+ result.append(", muxing mode: ");
+ result.append(getMuxingMode());
+ result.append(", mime type: ");
+ result.append(getMimeType());
for (DLNAMediaAudio audio : getAudioTracksList()) {
- s += "\n\taudio: id=" + audio.getId() + " / lang: " + audio.getLang() + " / flavor: " + audio.getFlavor() + " / codec: " + audio.getCodecA() + " / sf:" + audio.getSampleFrequency() + " / na: " + (audio.getAudioProperties() != null ? audio.getAudioProperties().getNumberOfChannels() : "-") + " / bs: " + audio.getBitsperSample();
- if (audio.getArtist() != null) {
- s += " / " + audio.getArtist() + "|" + audio.getAlbum() + "|" + audio.getSongname() + "|" + audio.getYear() + "|" + audio.getTrack();
- }
+ result.append("\n\tAudio track ");
+ result.append(audio.toString());
}
for (DLNAMediaSubtitle sub : getSubtitleTracksList()) {
- s += "\n\tsub: id=" + sub.getId() + " / lang: " + sub.getLang() + " / flavor: " + sub.getFlavor() + " / type: " + (sub.getType() != null ? sub.getType().toString() : "null");
+ result.append("\n\tSubtitle track ");
+ result.append(sub.toString());
}
- return s;
+ return result.toString();
}
public InputStream getThumbnailInputStream() {
Oops, something went wrong.

10 comments on commit 50b75f0

Contributor

ExSport replied Sep 16, 2013

👍 The logic, logging and added comments make code and trace logs much more understandable, thx!

You sir are a beast.

this has broken mp4 video playback on my bravia hx750 which supports mp4, they are sent as mp4 and not transcoded from pms but are detected on my tv as mpeg2 and wont play. it worked fine before this commit.

INFO 2013-09-17 17:51:19.811 [StartPlaying Event] Started sending C:\file.mp4 to Sony Bravia HX on 192.168.1.102
INFO 2013-09-17 17:51:28.624 [StopPlaying Event] Stopped sending C:\file.mp4 to Sony Bravia HX on 192.168.1.102

both versions show the same in debug as above, though with this commit it detects it as mpeg2 instead of mpeg4 and wont play

Contributor

ExSport replied Sep 17, 2013

Do you have TRACE level of Debug.log enabled? Any difference between logs?

this commit does

TRACE 2013-09-18 09:45:52.436 [New I/O server worker #1-1] Asked stream chunk : TimeRange [start=null, end=null] of file.mp4 and player null
TRACE 2013-09-18 09:45:52.436 [New I/O server worker #1-1] Sending 288270774 bytes.
TRACE 2013-09-18 09:45:52.452 [New I/O server worker #1-1] Sent to socket: Accept-Ranges: bytes
TRACE 2013-09-18 09:45:52.452 [New I/O server worker #1-1] Sent to socket: Connection: keep-alive
TRACE 2013-09-18 09:45:52.452 [New I/O server worker #1-1] Sent to socket: Content-Length: 288270774
TRACE 2013-09-18 09:45:52.452 [New I/O server worker #1-1] Sent to socket: Content-Range: bytes 0-288270773/288270774
TRACE 2013-09-18 09:45:52.452 [New I/O server worker #1-1] Sent to socket: Content-Type: video/mpeg

when before it would do

TRACE 2013-09-18 09:37:36.159 [New I/O server worker #1-3] Asked stream chunk : TimeRange [start=null, end=null] of file.mp4 and player null
TRACE 2013-09-18 09:37:36.159 [New I/O server boss #1 ([id: 0x3f98f94c, /192.168.1.100:5001])] Creating new pipeline
TRACE 2013-09-18 09:37:36.159 [New I/O server worker #1-3] Sent to socket: Accept-Ranges: bytes
TRACE 2013-09-18 09:37:36.159 [New I/O server worker #1-3] Sent to socket: Connection: keep-alive
TRACE 2013-09-18 09:37:36.159 [New I/O server worker #1-3] Sent to socket: Content-Length: 288270774
TRACE 2013-09-18 09:37:36.159 [New I/O server worker #1-3] Sent to socket: Content-Type: video/mp4

Contributor

ExSport replied Sep 18, 2013

On line 1530 it seems to be only change which can be relevant.
If it will return null, mpeg mime is used as next IF defines.
But I found also another flaw which can be culprit.
Check your BraviaHX.conf file and comment this line:
MimeTypesChanges=audio/wav=audio/L16|video/mp4=video/mpeg
This line changes mime mp4 to mpeg. Worth to try...

yay that fixed it, wow i never even noticed that line, im using the official conf file that comes with the build, i helped with its development ok we need to change that also then :) and remove or alter it in braviahx.conf

change
MimeTypesChanges=audio/wav=audio/L16|video/mp4=video/mpeg
to

MimeTypesChanges=audio/wav=audio/L16|video/mp4=video/mpeg

or remove the line completely

either way MimeTypesChanges=audio/wav=audio/L16|video/mp4=video/mpeg = bad with this commit and is in the official braviahx.conf.

it seems to be in alot of the confs it wasnt working before but now it is so its making mp4 to mpeg memes which wont play all confs need to be adjusted and video/mp4=video/mpeg removed.

Contributor

ExSport replied Sep 18, 2013

I thought this "MimeTypesChanges" is ignored when MediaInfo=true. Maybe it is due to the update (line 1530) how mime is read from renderer config but can't say from mobile phone without Eclipse :-)

well i have the default config and mediainfo=true and as soon as i removed that line it instantly started working again. i had the previous revision as a .backup file and mp4 worked fine until i add this commit and it stops playing mp4 and detects em as mpeg. so 100% its something in the code is it intended to work the way it is now if so then memetypechanges=mp4=video/mpeg needs to be removed or else mp4 wont work for everyone when the next build comes out.

Member

Raptor399 replied Sep 26, 2013

@ExSport: You're welcome! The logic in DLNAResource.java is way too complex to understand without proper logging.

@DeFlanko: Rrrroooar! ;-)

@mazeyx: I think ExSport is right; MimeTypeChanges weren't applied with "MediaInfo=true" before this commit, but now they are. I have mistaken the variable mimes to mean the mime type replacement done in some supported lines - however that is probably done elsewhere. I'll restore the old situation, thanks for bringing it up!

Please sign in to comment.