use of net.pms.dlna.DLNAMediaAudio in project UniversalMediaServer by UniversalMediaServer.
the class FormatConfiguration method match.
/**
* Match media information to audio codecs supported by the renderer and
* return its MIME-type if the match is successful. Returns null if the
* media is not natively supported by the renderer, which means it has
* to be transcoded.
*
* @param media The MediaInfo metadata
* @return The MIME type or null if no match was found.
*/
public String match(DLNAMediaInfo media) {
if (media.getFirstAudioTrack() == null) {
// no sound
return match(media.getContainer(), media.getCodecV(), null, 0, 0, media.getBitrate(), media.getWidth(), media.getHeight(), media.getExtras());
}
if (media.isSLS()) {
/*
* MPEG-4 SLS is a special case and must be treated differently. It
* consists of a MPEG-4 ISO container with two audio tracks, the
* first is the lossy "core" stream and the second is the SLS
* correction stream. When the SLS stream is applied to the core
* stream the result is lossless. It is arranged this way so that
* players that can't play SLS can still play the (lossy) core
* stream. Because of this, only compatibility for the first audio
* track needs to be checked.
*/
DLNAMediaAudio audio = media.getFirstAudioTrack();
return match(media.getContainer(), media.getCodecV(), audio.getCodecA(), audio.getAudioProperties().getNumberOfChannels(), audio.getSampleRate(), audio.getBitRate(), media.getWidth(), media.getHeight(), media.getExtras());
}
String finalMimeType = null;
for (DLNAMediaAudio audio : media.getAudioTracksList()) {
String mimeType = match(media.getContainer(), media.getCodecV(), audio.getCodecA(), audio.getAudioProperties().getNumberOfChannels(), audio.getSampleRate(), media.getBitrate(), media.getWidth(), media.getHeight(), media.getExtras());
finalMimeType = mimeType;
if (mimeType == null) {
// if at least one audio track is not compatible, the file must be transcoded.
return null;
}
}
return finalMimeType;
}
use of net.pms.dlna.DLNAMediaAudio in project UniversalMediaServer by UniversalMediaServer.
the class AudioUtils method parseRealAudio.
/**
* Parses the old RealAudio 1.0 and 2.0 formats that's not supported by
* neither {@link org.jaudiotagger} nor MediaInfo. Returns {@code false} if
* {@code channel} isn't one of these formats or the parsing fails.
* <p>
* Primary references:
* <ul>
* <li><a href="https://wiki.multimedia.cx/index.php/RealMedia">RealAudio on
* MultimediaWiki</a></li>
* <li><a
* href="https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/rmdec.c"
* >FFmpeg rmdec.c</a></li>
* </ul>
*
* @param channel the {@link Channel} containing the input. Size will only
* be parsed if {@code channel} is a {@link FileChannel}
* instance.
* @param media the {@link DLNAMediaInfo} instance to write the parsing
* results to.
* @return {@code true} if the {@code channel} input is in RealAudio 1.0 or
* 2.0 format and the parsing succeeds; false otherwise
*/
public static boolean parseRealAudio(ReadableByteChannel channel, DLNAMediaInfo media) {
final byte[] magicBytes = { 0x2E, 0x72, 0x61, (byte) 0xFD };
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(ByteOrder.BIG_ENDIAN);
DLNAMediaAudio audio = new DLNAMediaAudio();
try {
int count = channel.read(buffer);
if (count < 4) {
LOGGER.trace("Input is too short to be RealAudio");
return false;
}
buffer.flip();
byte[] signature = new byte[4];
buffer.get(signature);
if (!Arrays.equals(magicBytes, signature)) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Input signature ({}) mismatches RealAudio version 1.0 or 2.0", new String(signature, StandardCharsets.US_ASCII));
}
return false;
}
media.setContainer(FormatConfiguration.RA);
short version = buffer.getShort();
int reportedHeaderSize = 0;
int reportedDataSize = 0;
if (version == 3) {
audio.setCodecA(FormatConfiguration.REALAUDIO_14_4);
audio.getAudioProperties().setNumberOfChannels(1);
audio.getAudioProperties().setSampleFrequency(8000);
short headerSize = buffer.getShort();
buffer = ByteBuffer.allocate(headerSize);
channel.read(buffer);
buffer.flip();
buffer.position(8);
int bytesPerMinute = buffer.getShort() & 0xFFFF;
reportedDataSize = buffer.getInt();
byte b = buffer.get();
if (b != 0) {
byte[] title = new byte[b & 0xFF];
buffer.get(title);
String titleString = new String(title, StandardCharsets.US_ASCII);
audio.setSongname(titleString);
audio.setAudioTrackTitleFromMetadata(titleString);
}
b = buffer.get();
if (b != 0) {
byte[] artist = new byte[b & 0xFF];
buffer.get(artist);
audio.setArtist(new String(artist, StandardCharsets.US_ASCII));
}
audio.setBitRate(bytesPerMinute * 8 / 60);
media.setBitrate(bytesPerMinute * 8 / 60);
} else if (version == 4 || version == 5) {
buffer = ByteBuffer.allocate(14);
channel.read(buffer);
buffer.flip();
buffer.get(signature);
if (!".ra4".equals(new String(signature, StandardCharsets.US_ASCII))) {
LOGGER.debug("Invalid RealAudio 2.0 signature \"{}\"", new String(signature, StandardCharsets.US_ASCII));
return false;
}
reportedDataSize = buffer.getInt();
// skip version repeated
buffer.getShort();
reportedHeaderSize = buffer.getInt();
buffer = ByteBuffer.allocate(reportedHeaderSize);
channel.read(buffer);
buffer.flip();
// skip codec flavor
buffer.getShort();
// skip coded frame size
buffer.getInt();
// skip unknown
buffer.getInt();
long bytesPerMinute = buffer.getInt() & 0xFFFFFFFFL;
// skip unknown
buffer.getInt();
// skip sub packet
buffer.getShort();
// skip frame size
buffer.getShort();
// skip sub packet size
buffer.getShort();
// skip unknown
buffer.getShort();
if (version == 5) {
// skip unknown
buffer.position(buffer.position() + 6);
}
short sampleRate = buffer.getShort();
// skip unknown
buffer.getShort();
short sampleSize = buffer.getShort();
short nrChannels = buffer.getShort();
byte[] fourCC;
if (version == 4) {
// skip interleaver id
buffer.position(buffer.get() + buffer.position());
fourCC = new byte[buffer.get()];
buffer.get(fourCC);
} else {
// skip deinterlace id
buffer.getFloat();
fourCC = new byte[4];
buffer.get(fourCC);
}
String fourCCString = new String(fourCC, StandardCharsets.US_ASCII).toLowerCase(Locale.ROOT);
switch(fourCCString) {
case "lpcJ":
audio.setCodecA(FormatConfiguration.REALAUDIO_14_4);
break;
case "28_8":
audio.setCodecA(FormatConfiguration.REALAUDIO_28_8);
break;
case "dnet":
audio.setCodecA(FormatConfiguration.AC3);
break;
case "sipr":
audio.setCodecA(FormatConfiguration.SIPRO);
break;
case "cook":
audio.setCodecA(FormatConfiguration.COOK);
case "atrc":
audio.setCodecA(FormatConfiguration.ATRAC);
case "ralf":
audio.setCodecA(FormatConfiguration.RALF);
case "raac":
audio.setCodecA(FormatConfiguration.AAC_LC);
case "racp":
audio.setCodecA(FormatConfiguration.HE_AAC);
default:
LOGGER.debug("Unknown RealMedia codec FourCC \"{}\" - parsing failed", fourCCString);
return false;
}
if (buffer.hasRemaining()) {
parseRealAudioMetaData(buffer, audio, version);
}
audio.setBitRate((int) (bytesPerMinute * 8 / 60));
media.setBitrate((int) (bytesPerMinute * 8 / 60));
audio.setBitsperSample(sampleSize);
audio.getAudioProperties().setNumberOfChannels(nrChannels);
audio.getAudioProperties().setSampleFrequency(sampleRate);
} else {
LOGGER.error("Could not parse RealAudio format - unknown format version {}", version);
return false;
}
media.getAudioTracksList().add(audio);
long fileSize = 0;
if (channel instanceof FileChannel) {
fileSize = ((FileChannel) channel).size();
media.setSize(fileSize);
}
// Duration is estimated based on bitrate and might not be accurate
if (audio.getBitRate() > 0) {
int dataSize;
if (fileSize > 0 && reportedHeaderSize > 0) {
int fullHeaderSize = reportedHeaderSize + (version == 3 ? 8 : 16);
if (reportedDataSize > 0) {
dataSize = (int) Math.min(reportedDataSize, fileSize - fullHeaderSize);
} else {
dataSize = (int) (fileSize - fullHeaderSize);
}
} else {
dataSize = reportedDataSize;
}
media.setDuration((double) dataSize / audio.getBitRate() * 8);
}
} catch (IOException e) {
LOGGER.debug("Error while trying to parse RealAudio version 1 or 2: {}", e.getMessage());
LOGGER.trace("", e);
return false;
}
if (PMS.getConfiguration() != null && !PMS.getConfiguration().getAudioThumbnailMethod().equals(CoverSupplier.NONE) && (StringUtils.isNotBlank(media.getFirstAudioTrack().getSongname()) || StringUtils.isNotBlank(media.getFirstAudioTrack().getArtist()))) {
ID3v1Tag tag = new ID3v1Tag();
if (StringUtils.isNotBlank(media.getFirstAudioTrack().getSongname())) {
tag.setTitle(media.getFirstAudioTrack().getSongname());
}
if (StringUtils.isNotBlank(media.getFirstAudioTrack().getArtist())) {
tag.setArtist(media.getFirstAudioTrack().getArtist());
}
try {
media.setThumb(DLNAThumbnail.toThumbnail(CoverUtil.get().getThumbnail(tag), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false));
} catch (IOException e) {
LOGGER.error("An error occurred while generating thumbnail for RealAudio source: [\"{}\", \"{}\"]", tag.getFirstTitle(), tag.getFirstArtist());
}
}
media.setThumbready(true);
media.setMediaparsed(true);
return true;
}
use of net.pms.dlna.DLNAMediaAudio in project UniversalMediaServer by UniversalMediaServer.
the class FormatRecognitionTest method testPlaystationVideoMkvCompatibility.
/**
* Test the compatibility of the Playstation 3 with the MPG format.
*/
@Test
public void testPlaystationVideoMkvCompatibility() {
// This test is only useful if the MediaInfo library is available
assumeTrue(mediaInfoParserIsValid);
RendererConfiguration conf = RendererConfiguration.getRendererConfigurationByName("Playstation 3");
assertNotNull("Renderer named \"Playstation 3\" found.", conf);
// Construct MKV information
DLNAMediaInfo info = new DLNAMediaInfo();
info.setContainer("mkv");
DLNAMediaAudio audio = new DLNAMediaAudio();
audio.setCodecA("ac3");
audio.getAudioProperties().setNumberOfChannels(5);
List<DLNAMediaAudio> audioCodes = new ArrayList<>();
audioCodes.add(audio);
info.setAudioTracksList(audioCodes);
info.setCodecV("mp4");
Format format = new MPG();
format.match("test.mkv");
assertEquals("PS3 is incompatible with MKV", false, conf.isCompatible(info, format, configuration));
}
use of net.pms.dlna.DLNAMediaAudio in project UniversalMediaServer by UniversalMediaServer.
the class FormatRecognitionTest method testPlaystationVideoMpgCompatibility.
/**
* Test the compatibility of the Playstation 3 with the MPG format.
*/
@Test
public void testPlaystationVideoMpgCompatibility() {
// This test is only useful if the MediaInfo library is available
assumeTrue(mediaInfoParserIsValid);
RendererConfiguration conf = RendererConfiguration.getRendererConfigurationByName("Playstation 3");
assertNotNull("Renderer named \"Playstation 3\" found.", conf);
// Construct regular two channel MPG information
DLNAMediaInfo info = new DLNAMediaInfo();
info.setContainer("avi");
DLNAMediaAudio audio = new DLNAMediaAudio();
audio.setCodecA("ac3");
audio.getAudioProperties().setNumberOfChannels(5);
List<DLNAMediaAudio> audioCodes = new ArrayList<>();
audioCodes.add(audio);
info.setAudioTracksList(audioCodes);
info.setCodecV("mp4");
Format format = new MPG();
format.match("test.avi");
assertEquals("PS3 is compatible with MPG", true, conf.isCompatible(info, format, configuration));
// Construct MPG with wmv codec that the PS3 does not support natively
info.setCodecV("wmv");
assertEquals("PS3 is incompatible with MPG with wmv codec", false, conf.isCompatible(info, format, configuration));
}
use of net.pms.dlna.DLNAMediaAudio in project UniversalMediaServer by UniversalMediaServer.
the class Player method setAudioOutputParameters.
/**
* This method populates the supplied {@link OutputParams} object with the correct audio track (aid)
* based on the MediaInfo metadata and PMS configuration settings.
*
* @param media
* The MediaInfo metadata for the file.
* @param params
* The parameters to populate.
*/
public static void setAudioOutputParameters(DLNAMediaInfo media, OutputParams params) {
// Use device-specific pms conf
PmsConfiguration configuration = PMS.getConfiguration(params);
if (params.aid == null && media != null && media.getFirstAudioTrack() != null) {
// check for preferred audio
DLNAMediaAudio dtsTrack = null;
StringTokenizer st = new StringTokenizer(configuration.getAudioLanguages(), ",");
while (st.hasMoreTokens()) {
String lang = st.nextToken().trim();
LOGGER.trace("Looking for an audio track with lang: " + lang);
for (DLNAMediaAudio audio : media.getAudioTracksList()) {
if (audio.matchCode(lang)) {
params.aid = audio;
LOGGER.trace("Matched audio track: " + audio);
return;
}
if (dtsTrack == null && audio.isDTS()) {
dtsTrack = audio;
}
}
}
// preferred audio not found, take a default audio track, dts first if available
if (dtsTrack != null) {
params.aid = dtsTrack;
LOGGER.trace("Found priority audio track with DTS: " + dtsTrack);
} else {
params.aid = media.getAudioTracksList().get(0);
LOGGER.trace("Chose a default audio track: " + params.aid);
}
}
}
Aggregations