Search in sources :

Example 1 with DLNAMediaAudio

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;
}
Also used : DLNAMediaAudio(net.pms.dlna.DLNAMediaAudio)

Example 2 with DLNAMediaAudio

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;
}
Also used : DLNAMediaAudio(net.pms.dlna.DLNAMediaAudio) FileChannel(java.nio.channels.FileChannel) IOException(java.io.IOException) ID3v1Tag(org.jaudiotagger.tag.id3.ID3v1Tag) ByteBuffer(java.nio.ByteBuffer)

Example 3 with DLNAMediaAudio

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));
}
Also used : DLNAMediaAudio(net.pms.dlna.DLNAMediaAudio) Format(net.pms.formats.Format) MPG(net.pms.formats.MPG) DLNAMediaInfo(net.pms.dlna.DLNAMediaInfo) RendererConfiguration(net.pms.configuration.RendererConfiguration) ArrayList(java.util.ArrayList) Test(org.junit.Test)

Example 4 with DLNAMediaAudio

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));
}
Also used : DLNAMediaAudio(net.pms.dlna.DLNAMediaAudio) Format(net.pms.formats.Format) MPG(net.pms.formats.MPG) DLNAMediaInfo(net.pms.dlna.DLNAMediaInfo) RendererConfiguration(net.pms.configuration.RendererConfiguration) ArrayList(java.util.ArrayList) Test(org.junit.Test)

Example 5 with DLNAMediaAudio

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);
        }
    }
}
Also used : DLNAMediaAudio(net.pms.dlna.DLNAMediaAudio) StringTokenizer(java.util.StringTokenizer) PmsConfiguration(net.pms.configuration.PmsConfiguration)

Aggregations

DLNAMediaAudio (net.pms.dlna.DLNAMediaAudio)7 ArrayList (java.util.ArrayList)4 DLNAMediaInfo (net.pms.dlna.DLNAMediaInfo)4 Format (net.pms.formats.Format)4 Test (org.junit.Test)4 RendererConfiguration (net.pms.configuration.RendererConfiguration)3 MPG (net.pms.formats.MPG)3 PmsConfiguration (net.pms.configuration.PmsConfiguration)2 IOException (java.io.IOException)1 ByteBuffer (java.nio.ByteBuffer)1 FileChannel (java.nio.channels.FileChannel)1 StringTokenizer (java.util.StringTokenizer)1 MP3 (net.pms.formats.audio.MP3)1 ConfigurationException (org.apache.commons.configuration.ConfigurationException)1 ID3v1Tag (org.jaudiotagger.tag.id3.ID3v1Tag)1