Browse files

Renderers:

	- Panasonic TVs: Add "exclusive" option to SeekByTime to work around a transcoding issue (thanks, ExSport)
	- PS3: Restore seek-by-time support for streaming
  • Loading branch information...
1 parent cc73a88 commit f5f7747a13031292915941103f65c1560374390b @chocolateboy chocolateboy committed Nov 25, 2012
View
2 CHANGELOG.txt
@@ -7,6 +7,8 @@ Changelog:
- ffmpeg has been renamed ffmpeg_video_custom_options and now defaults to null
Renderers:
- Document TranscodeVideo and TranscodeAudio profiles
+ - Panasonic TVs: Add "exclusive" option to SeekByTime to work around a transcoding issue
+ - PS3: Restore seek-by-time support for streaming
Engines:
FFmpeg Video and FFmpeg Web Video:
- support all three TranscodeVideo profiles: WMV, MPEGTSAC3 and MPEGPSAC3
View
34 src/main/external-resources/renderers/PS3.conf
@@ -127,9 +127,33 @@ Image = true
# DLNA SETTINGS
#
-# SeekByTime: Set to true to use the DLNA feature seek by time instead of by
-# range.
-SeekByTime = true
+# SeekByTime: Set to true to use the DLNA feature seek-by-time instead of
+# the default seek-by-byte method.
+#
+# Valid values:
+#
+# true:
+#
+# The renderer supports seek-by-time.
+#
+# false:
+#
+# The renderer doesn't support seek-by-time, so it should
+# only use the default seek-by-byte method.
+#
+# exclusive:
+#
+# While most renderers that support seek-by-time also support
+# seek-by-byte, some (e.g. the PS3 [1] and Panasonic TVs [2])
+# behave unreliably if PMS permits them to use seek-by-byte
+# when seek-by-time is permitted. This option works around this
+# by disabling seek-by-byte when permitting seek-by-time.
+#
+# The default is false.
+#
+# [1] Seeking via "Go To" with the PS3 (firmware 4.31) and MEncoder works with "exclusive", but hangs or crashes with "true".
+# [2] http://www.ps3mediaserver.org/forum/viewtopic.php?f=6&t=15841
+SeekByTime = exclusive
# DLNALocalizationRequired: Serve different flavors of localization in the
# DLNA parameters (PAL/NTSC, NA/EU/JP) to allow every world wide renderer to
@@ -138,13 +162,13 @@ DLNALocalizationRequired = false
# CBRVideoBitrate is useful for renderers without SeekByTime support. It does time2byte conversion to support FF/RW.
# Only possibility how to predict where we are when seeking is using CBR bitrate instead of VBR used by default
-# Making CBR stream by MEncoder is 3 times slower than using VBR so count with it if you have a slow computer!
+# Creating a CBR stream with MEncoder is 3 times slower than using VBR so bear this in mind if you have a slow computer!
# Speed can hopefully be optimized a little bit in the future:
# http://www.ps3mediaserver.org/forum/viewtopic.php?f=14&t=8883&p=53706&hilit=ditlew#p53700
# http://www.ps3mediaserver.org/forum/viewtopic.php?f=11&t=11284&p=62765&hilit=1835#p62765
# CBRVideoBitrate=15000
-# ByteToTimeseekRewindSeconds is used for finetuning so default is 0
+# ByteToTimeseekRewindSeconds is used for fine tuning, so the default is 0
# ByteToTimeseekRewindSeconds=0
#-----------------------------------------------------------------------------
View
2 src/main/external-resources/renderers/SMP-N100.conf
@@ -21,7 +21,7 @@ UserAgentAdditionalHeaderSearch=(cn="Sony Corporation"; mn="Media Player")
Video=true
Audio=true
Image=true
-SeekByTime=false
+SeekByTime=exclusive
TranscodeVideo=MPEGTSAC3
TranscodeAudio=LPCM
DefaultVBVBufSize=true
View
124 src/main/java/net/pms/configuration/RendererConfiguration.java
@@ -34,74 +34,72 @@
private static PmsConfiguration pmsConfiguration;
private static RendererConfiguration defaultConf;
private static Map<InetAddress, RendererConfiguration> addressAssociation = new HashMap<InetAddress, RendererConfiguration>();
+
private RootFolder rootFolder;
+ private final PropertiesConfiguration configuration;
+ private FormatConfiguration formatConfiguration;
+ private int rank;
+ private final Map<String, String> mimes;
+ private final Map<String, String> DLNAPN;
- private static final String RENDERER_NAME = "RendererName";
- private static final String RENDERER_ICON = "RendererIcon";
- private static final String USER_AGENT = "UserAgentSearch";
- private static final String USER_AGENT_ADDITIONAL_HEADER = "UserAgentAdditionalHeader";
- private static final String USER_AGENT_ADDITIONAL_SEARCH = "UserAgentAdditionalHeaderSearch";
- private static final String VIDEO = "Video";
- private static final String AUDIO = "Audio";
- private static final String IMAGE = "Image";
- private static final String SEEK_BY_TIME = "SeekByTime";
-
+ // property values
private static final String DEPRECATED_MPEGPSAC3 = "MPEGAC3"; // XXX deprecated: old name with missing container
+ private static final String EXCLUSIVE = "exclusive";
+ private static final String LPCM = "LPCM";
+ private static final String MP3 = "MP3";
private static final String MPEGPSAC3 = "MPEGPSAC3";
private static final String MPEGTSAC3 = "MPEGTSAC3";
- private static final String WMV = "WMV";
- private static final String LPCM = "LPCM";
private static final String WAV = "WAV";
- private static final String MP3 = "MP3";
+ private static final String WMV = "WMV";
- private static final String TRANSCODE_AUDIO = "TranscodeAudio";
- private static final String TRANSCODE_VIDEO = "TranscodeVideo";
+ // property names
+ private static final String AUDIO = "Audio";
+ private static final String AUTO_EXIF_ROTATE = "AutoExifRotate";
+ private static final String BYTE_TO_TIMESEEK_REWIND_SECONDS = "ByteToTimeseekRewindSeconds"; // Ditlew
+ private static final String CBR_VIDEO_BITRATE = "CBRVideoBitrate"; // Ditlew
+ private static final String CHUNKED_TRANSFER = "ChunkedTransfer";
+ private static final String CUSTOM_MENCODER_OPTIONS = "CustomMencoderOptions";
+ private static final String CUSTOM_MENCODER_QUALITY_SETTINGS = "CustomMencoderQualitySettings";
private static final String DEFAULT_VBV_BUFSIZE = "DefaultVBVBufSize";
- private static final String MUX_H264_WITH_MPEGTS = "MuxH264ToMpegTS";
- private static final String MUX_DTS_TO_MPEG = "MuxDTSToMpeg";
- private static final String WRAP_DTS_INTO_PCM = "WrapDTSIntoPCM";
- private static final String MUX_LPCM_TO_MPEG = "MuxLPCMToMpeg";
+ private static final String DLNA_LOCALIZATION_REQUIRED = "DLNALocalizationRequired";
+ private static final String DLNA_ORGPN_USE = "DLNAOrgPN";
+ private static final String DLNA_PN_CHANGES = "DLNAProfileChanges";
+ private static final String DLNA_TREE_HACK = "CreateDLNATreeFaster";
+ private static final String FORCE_JPG_THUMBNAILS = "ForceJPGThumbnails"; // Sony devices require JPG thumbnails
+ private static final String H264_L41_LIMITED = "H264Level41Limited";
+ private static final String IMAGE = "Image";
+ private static final String LONG_FILE_NAME_FORMAT = "LongFileNameFormat";
private static final String MAX_VIDEO_BITRATE = "MaxVideoBitrateMbps";
- private static final String MAX_VIDEO_WIDTH = "MaxVideoWidth";
private static final String MAX_VIDEO_HEIGHT = "MaxVideoHeight";
- private static final String USE_SAME_EXTENSION = "UseSameExtension";
- private static final String MIME_TYPES_CHANGES = "MimeTypesChanges";
- private static final String TRANSCODE_EXT = "TranscodeExtensions";
- private static final String STREAM_EXT = "StreamExtensions";
- private static final String H264_L41_LIMITED = "H264Level41Limited";
- private static final String TRANSCODE_AUDIO_441KHZ = "TranscodeAudioTo441kHz";
- private static final String TRANSCODED_SIZE = "TranscodedVideoFileSize";
- private static final String DLNA_PN_CHANGES = "DLNAProfileChanges";
- private static final String TRANSCODE_FAST_START = "TranscodeFastStart";
- private static final String AUTO_EXIF_ROTATE = "AutoExifRotate";
- private static final String DLNA_ORGPN_USE = "DLNAOrgPN";
- private static final String DLNA_LOCALIZATION_REQUIRED = "DLNALocalizationRequired";
+ private static final String MAX_VIDEO_WIDTH = "MaxVideoWidth";
private static final String MEDIAPARSERV2 = "MediaInfo";
private static final String MEDIAPARSERV2_THUMB = "MediaParserV2_ThumbnailGeneration";
- private static final String SUPPORTED = "Supported";
- private static final String CUSTOM_MENCODER_QUALITY_SETTINGS = "CustomMencoderQualitySettings";
- private static final String CUSTOM_MENCODER_OPTIONS = "CustomMencoderOptions";
+ private static final String MIME_TYPES_CHANGES = "MimeTypesChanges";
+ private static final String MUX_DTS_TO_MPEG = "MuxDTSToMpeg";
+ private static final String MUX_H264_WITH_MPEGTS = "MuxH264ToMpegTS";
+ private static final String MUX_LPCM_TO_MPEG = "MuxLPCMToMpeg";
+ private static final String RENDERER_ICON = "RendererIcon";
+ private static final String RENDERER_NAME = "RendererName";
+ private static final String SEEK_BY_TIME = "SeekByTime";
+ private static final String SHORT_FILE_NAME_FORMAT = "ShortFileNameFormat";
private static final String SHOW_AUDIO_METADATA = "ShowAudioMetadata";
+ private static final String SHOW_DVD_TITLE_DURATION = "ShowDVDTitleDuration"; // Ditlew
private static final String SHOW_SUB_METADATA = "ShowSubMetadata";
- private static final String DLNA_TREE_HACK = "CreateDLNATreeFaster";
- private static final String CHUNKED_TRANSFER = "ChunkedTransfer";
+ private static final String STREAM_EXT = "StreamExtensions";
private static final String SUBTITLE_HTTP_HEADER = "SubtitleHttpHeader";
- private static final String LONG_FILE_NAME_FORMAT = "LongFileNameFormat";
- private static final String SHORT_FILE_NAME_FORMAT = "ShortFileNameFormat";
-
- // Sony devices require JPG thumbnails
- private static final String FORCE_JPG_THUMBNAILS = "ForceJPGThumbnails";
-
- // Ditlew
- private static final String SHOW_DVD_TITLE_DURATION = "ShowDVDTitleDuration";
- private static final String CBR_VIDEO_BITRATE = "CBRVideoBitrate";
- private static final String BYTE_TO_TIMESEEK_REWIND_SECONDS = "ByteToTimeseekRewindSeconds";
-
- private final PropertiesConfiguration configuration;
- private FormatConfiguration formatConfiguration;
- private int rank;
- private final Map<String, String> mimes;
- private final Map<String, String> DLNAPN;
+ private static final String SUPPORTED = "Supported";
+ private static final String TRANSCODE_AUDIO_441KHZ = "TranscodeAudioTo441kHz";
+ private static final String TRANSCODE_AUDIO = "TranscodeAudio";
+ private static final String TRANSCODED_SIZE = "TranscodedVideoFileSize";
+ private static final String TRANSCODE_EXT = "TranscodeExtensions";
+ private static final String TRANSCODE_FAST_START = "TranscodeFastStart";
+ private static final String TRANSCODE_VIDEO = "TranscodeVideo";
+ private static final String USER_AGENT_ADDITIONAL_HEADER = "UserAgentAdditionalHeader";
+ private static final String USER_AGENT_ADDITIONAL_SEARCH = "UserAgentAdditionalHeaderSearch";
+ private static final String USER_AGENT = "UserAgentSearch";
+ private static final String USE_SAME_EXTENSION = "UseSameExtension";
+ private static final String VIDEO = "Video";
+ private static final String WRAP_DTS_INTO_PCM = "WrapDTSIntoPCM";
public static RendererConfiguration getDefaultConf() {
return defaultConf;
@@ -717,8 +715,25 @@ public String getUseSameExtension(String file) {
return s;
}
+ /**
+ * Returns true if SeekByTime is set to "true" or "exclusive", false otherwise.
+ * Default value is false.
+ *
+ * @return true if the renderer supports seek-by-time, false otherwise.
+ */
public boolean isSeekByTime() {
- return getBoolean(SEEK_BY_TIME, true);
+ return isSeekByTimeExclusive() || getBoolean(SEEK_BY_TIME, false);
+ }
+
+ /**
+ * Returns true if SeekByTime is set to "exclusive", false otherwise.
+ * Default value is false.
+ *
+ * @return true if the renderer supports seek-by-time exclusively
+ * (i.e. not in conjunction with seek-by-byte), false otherwise.
+ */
+ public boolean isSeekByTimeExclusive() {
+ return getString(SEEK_BY_TIME, "").equalsIgnoreCase("exclusive");
}
public boolean isMuxH264MpegTS() {
@@ -742,6 +757,7 @@ public boolean isMuxDTSToMpeg() {
if (isMediaParserV2()) {
return getFormatConfiguration().isDTSSupported();
}
+
return getBoolean(MUX_DTS_TO_MPEG, false);
}
View
71 src/main/java/net/pms/dlna/DLNAResource.java
@@ -218,7 +218,7 @@
protected boolean skipTranscode = false;
private boolean allChildrenAreFolders = true;
- private String flags;
+ private String dlnaOrgOpFlags;
/**
* @deprecated Use standard getter and setter to access this field.
@@ -359,7 +359,7 @@ public long length(RendererConfiguration mediaRenderer) {
public abstract boolean isFolder();
public String getDlnaContentFeatures() {
- return (dlnaspec != null ? (dlnaspec + ";") : "") + getFlags() + ";DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000";
+ return (dlnaspec != null ? (dlnaspec + ";") : "") + getDlnaOrgOpFlags() + ";DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000";
}
public DLNAResource getPrimaryResource() {
@@ -1234,7 +1234,12 @@ protected DLNAResource clone() {
// this shouldn't be public
@Deprecated
public String getFlags() {
- return flags;
+ return getDlnaOrgOpFlags();
+ }
+
+ // permit the renderer to seek by time, bytes or both
+ private String getDlnaOrgOpFlags() {
+ return "DLNA.ORG_OP=" + dlnaOrgOpFlags;
}
/**Returns an XML (DIDL) representation of the DLNA node. It gives a complete representation of the item, with as many tags as available.
@@ -1318,37 +1323,52 @@ public final String toString(RendererConfiguration mediaRenderer) {
// DLNA.ORG_OP flags
//
// Two booleans (binary digits) which determine what transport operations the renderer is allowed to
- // request (in the form of HTTP headers): the first digit allows the renderer to send
+ // perform (in the form of HTTP headers): the first digit allows the renderer to send
// TimeSeekRange.DLNA.ORG (seek-by-time) headers; the second allows it to send RANGE (seek-by-byte)
// headers.
//
- // See here for an example of how these options can be mapped to keys on the renderer's controller:
- // http://www.ps3mediaserver.org/forum/viewtopic.php?f=2&t=2908&p=12550#p12550
- //
// 00 - no seeking (or even pausing) allowed
// 01 - seek by byte
// 10 - seek by time
// 11 - seek by both
+ //
+ // See here for an example of how these options can be mapped to keys on the renderer's controller:
+ // http://www.ps3mediaserver.org/forum/viewtopic.php?f=2&t=2908&p=12550#p12550
+ //
+ // Note that seek-by-time is the preferred option (seek-by-byte is a fallback) but it requires a) support
+ // by the renderer (via the SeekByTime renderer conf option) and either a) a file that's not being transcoded
+ // or if it is, b) support by its transcode engine for seek-by-time.
- flags = "DLNA.ORG_OP=01";
+ dlnaOrgOpFlags = "01";
if (mediaRenderer.isSeekByTime()) {
if (getPlayer() != null) { // transcoded
if (getPlayer().isTimeSeekable()) {
- // PS3 doesn't like OP=11
- // FIXME in the unlikely event that this still applies, it should be a dedicated option
- // (feature detection rather than renderer detection) e.g.
- // DLNATranscodeMapSeekByBothToSeekByTime
- if (mediaRenderer.isPS3()) {
- flags = "DLNA.ORG_OP=10";
+ // Some renderers - e.g. the PS3 and Panasonic TVs - behave erratically when
+ // transcoding if we keep the default seek-by-byte permission on when permitting
+ // seek-by-time: http://www.ps3mediaserver.org/forum/viewtopic.php?f=6&t=15841
+ //
+ // It's not clear if this is a bug in the DLNA libraries of these renderers or a bug
+ // in PMS, but setting an option in the renderer conf that disables seek-by-byte when
+ // we permit seek-by-time - e.g.:
+ //
+ // SeekByTime = exclusive
+ //
+ // - works around it.
+ if (mediaRenderer.isSeekByTimeExclusive()) {
+ dlnaOrgOpFlags = "10";
} else {
- flags = "DLNA.ORG_OP=11";
+ dlnaOrgOpFlags = "11";
}
}
} else { // streamed
- if (!mediaRenderer.isPS3()) {
- flags = "DLNA.ORG_OP=11";
- }
+ // chocolateboy 2012-11-25: seek-by-time used to be disabled here for the PS3
+ // (the flag was left at the default seek-by-byte value) and only set to
+ // seek-by-both for non-PS3 renderers. I can't reproduce with PS3 firmware 4.31
+ // whatever (undocumented) issue led to the creation of this exception, so
+ // it has been removed unless/until someone can reproduce it (e.g. with old
+ // firmware)
+ dlnaOrgOpFlags = "11";
}
}
@@ -1373,8 +1393,11 @@ public final String toString(RendererConfiguration mediaRenderer) {
if (!mpegTsMux) {
mpegTsMux = MEncoderVideo.ID.equals(getPlayer().id()) && mediaRenderer.isTranscodeToMPEGTSAC3();
}
+
if (mpegTsMux) {
- dlnaspec = getMedia().isH264() && !VideoLanVideoStreaming.ID.equals(getPlayer().id()) && getMedia().isMuxable(mediaRenderer) ? "DLNA.ORG_PN=AVC_TS_HD_24_AC3_ISO" : "DLNA.ORG_PN=" + getMPEG_TS_SD_EU_ISOLocalizedValue(c);
+ dlnaspec = getMedia().isH264() && !VideoLanVideoStreaming.ID.equals(getPlayer().id()) && getMedia().isMuxable(mediaRenderer) ?
+ "DLNA.ORG_PN=AVC_TS_HD_24_AC3_ISO" :
+ "DLNA.ORG_PN=" + getMPEG_TS_SD_EU_ISOLocalizedValue(c);
} else {
dlnaspec = "DLNA.ORG_PN=" + getMPEG_PS_PALLocalizedValue(c);
}
@@ -1407,8 +1430,7 @@ public final String toString(RendererConfiguration mediaRenderer) {
dlnaspec = null;
}
- addAttribute(sb, "protocolInfo", "http-get:*:" + mime + ":" + (dlnaspec != null ? (dlnaspec + ";") : "") + flags);
-
+ addAttribute(sb, "protocolInfo", "http-get:*:" + mime + ":" + (dlnaspec != null ? (dlnaspec + ";") : "") + getDlnaOrgOpFlags());
if (getFormat() != null && getFormat().isVideo() && getMedia() != null && getMedia().isMediaparsed()) {
if (getPlayer() == null && getMedia() != null) {
@@ -1426,14 +1448,18 @@ public final String toString(RendererConfiguration mediaRenderer) {
addAttribute(sb, "duration", getMedia().getDurationString());
}
}
+
if (getMedia().getResolution() != null) {
addAttribute(sb, "resolution", getMedia().getResolution());
}
+
addAttribute(sb, "bitrate", getMedia().getRealVideoBitrate());
+
if (firstAudioTrack != null) {
if (firstAudioTrack.getAudioProperties().getNumberOfChannels() > 0) {
addAttribute(sb, "nrAudioChannels", firstAudioTrack.getAudioProperties().getNumberOfChannels());
}
+
if (firstAudioTrack.getSampleFrequency() != null) {
addAttribute(sb, "sampleFrequency", firstAudioTrack.getSampleFrequency());
}
@@ -1453,9 +1479,11 @@ public final String toString(RendererConfiguration mediaRenderer) {
if (getMedia().getDuration() != null) {
addAttribute(sb, "duration", DLNAMediaInfo.getDurationString(getMedia().getDuration()));
}
+
if (firstAudioTrack != null && firstAudioTrack.getSampleFrequency() != null) {
addAttribute(sb, "sampleFrequency", firstAudioTrack.getSampleFrequency());
}
+
if (firstAudioTrack != null) {
addAttribute(sb, "nrAudioChannels", firstAudioTrack.getAudioProperties().getNumberOfChannels());
}
@@ -1493,6 +1521,7 @@ public final String toString(RendererConfiguration mediaRenderer) {
addAttribute(sb, "duration", "09:59:59");
addAttribute(sb, "bitrate", "1000000");
}
+
endTag(sb);
sb.append(getFileURL());
closeTag(sb, "res");

0 comments on commit f5f7747

Please sign in to comment.