diff options
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java')
-rw-r--r-- | mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java new file mode 100644 index 0000000000..1d44219eda --- /dev/null +++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.icy; + +import androidx.annotation.Nullable; +import org.mozilla.thirdparty.com.google.android.exoplayer2.C; +import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.Metadata; +import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.MetadataDecoder; +import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.MetadataInputBuffer; +import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions; +import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util; +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Decodes ICY stream information. */ +public final class IcyDecoder implements MetadataDecoder { + + private static final Pattern METADATA_ELEMENT = Pattern.compile("(.+?)='(.*?)';", Pattern.DOTALL); + private static final String STREAM_KEY_NAME = "streamtitle"; + private static final String STREAM_KEY_URL = "streamurl"; + + private final CharsetDecoder utf8Decoder; + private final CharsetDecoder iso88591Decoder; + + public IcyDecoder() { + utf8Decoder = Charset.forName(C.UTF8_NAME).newDecoder(); + iso88591Decoder = Charset.forName(C.ISO88591_NAME).newDecoder(); + } + + @Override + @SuppressWarnings("ByteBufferBackingArray") + public Metadata decode(MetadataInputBuffer inputBuffer) { + ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); + @Nullable String icyString = decodeToString(buffer); + byte[] icyBytes = new byte[buffer.limit()]; + buffer.get(icyBytes); + + if (icyString == null) { + return new Metadata(new IcyInfo(icyBytes, /* title= */ null, /* url= */ null)); + } + + @Nullable String name = null; + @Nullable String url = null; + int index = 0; + Matcher matcher = METADATA_ELEMENT.matcher(icyString); + while (matcher.find(index)) { + @Nullable String key = Util.toLowerInvariant(matcher.group(1)); + @Nullable String value = matcher.group(2); + switch (key) { + case STREAM_KEY_NAME: + name = value; + break; + case STREAM_KEY_URL: + url = value; + break; + } + index = matcher.end(); + } + return new Metadata(new IcyInfo(icyBytes, name, url)); + } + + // The ICY spec doesn't specify a character encoding, and there's no way to communicate one + // either. So try decoding UTF-8 first, then fall back to ISO-8859-1. + // https://github.com/google/ExoPlayer/issues/6753 + @Nullable + private String decodeToString(ByteBuffer data) { + try { + return utf8Decoder.decode(data).toString(); + } catch (CharacterCodingException e) { + // Fall through to try ISO-8859-1 decoding. + } finally { + utf8Decoder.reset(); + data.rewind(); + } + try { + return iso88591Decoder.decode(data).toString(); + } catch (CharacterCodingException e) { + return null; + } finally { + iso88591Decoder.reset(); + data.rewind(); + } + } +} |