Parse Picture Metadata in FLAC
This commit is contained in:
parent
09835c454b
commit
ea64ecf2c4
@ -229,8 +229,8 @@ public final class FlacExtractor implements Extractor {
|
||||
binarySearchSeeker =
|
||||
outputSeekMap(decoderJni, streamMetadata, input.getLength(), extractorOutput);
|
||||
Metadata metadata = id3MetadataDisabled ? null : id3Metadata;
|
||||
if (streamMetadata.vorbisComments != null) {
|
||||
metadata = streamMetadata.vorbisComments.copyWithAppendedEntriesFrom(metadata);
|
||||
if (streamMetadata.flacMetadata != null) {
|
||||
metadata = streamMetadata.flacMetadata.copyWithAppendedEntriesFrom(metadata);
|
||||
}
|
||||
outputFormat(streamMetadata, metadata, trackOutput);
|
||||
outputBuffer.reset(streamMetadata.maxDecodedFrameSize());
|
||||
|
@ -102,10 +102,10 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
|
||||
jmethodID arrayListConstructor =
|
||||
env->GetMethodID(arrayListClass, "<init>", "()V");
|
||||
jobject commentList = env->NewObject(arrayListClass, arrayListConstructor);
|
||||
jmethodID arrayListAddMethod =
|
||||
env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
|
||||
if (context->parser->isVorbisCommentsValid()) {
|
||||
jmethodID arrayListAddMethod =
|
||||
env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
std::vector<std::string> vorbisComments =
|
||||
context->parser->getVorbisComments();
|
||||
for (std::vector<std::string>::const_iterator vorbisComment =
|
||||
@ -117,6 +117,39 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
|
||||
}
|
||||
}
|
||||
|
||||
jobject pictureList = env->NewObject(arrayListClass, arrayListConstructor);
|
||||
bool picValid = context->parser->isPicValid();
|
||||
if (picValid) {
|
||||
std::vector<flacPicture> pictures = context->parser->getPictures();
|
||||
jclass flacPictureFrameClass = env->FindClass(
|
||||
"com/google/android/exoplayer2/metadata/flac/PictureFrame");
|
||||
jmethodID flacPictureFrameConstructor = env->GetMethodID(
|
||||
flacPictureFrameClass, "<init>",
|
||||
"(ILjava/lang/String;Ljava/lang/String;IIII[B)V");
|
||||
for (std::vector<flacPicture>::const_iterator picture = pictures.begin();
|
||||
picture != pictures.end(); ++picture) {
|
||||
jstring mimeType = env->NewStringUTF(picture->mimeType.c_str());
|
||||
jstring description = env->NewStringUTF(picture->description.c_str());
|
||||
jbyteArray picArr = env->NewByteArray(picture->data.size());
|
||||
env->SetByteArrayRegion(picArr, 0, picture->data.size(),
|
||||
(signed char *)&picture->data[0]);
|
||||
jobject flacPictureFrame = env->NewObject(flacPictureFrameClass,
|
||||
flacPictureFrameConstructor,
|
||||
picture->type,
|
||||
mimeType,
|
||||
description,
|
||||
picture->width,
|
||||
picture->height,
|
||||
picture->depth,
|
||||
picture->colors,
|
||||
picArr);
|
||||
env->CallBooleanMethod(pictureList, arrayListAddMethod, flacPictureFrame);
|
||||
env->DeleteLocalRef(mimeType);
|
||||
env->DeleteLocalRef(description);
|
||||
env->DeleteLocalRef(picArr);
|
||||
}
|
||||
}
|
||||
|
||||
const FLAC__StreamMetadata_StreamInfo &streamInfo =
|
||||
context->parser->getStreamInfo();
|
||||
|
||||
@ -124,14 +157,21 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
|
||||
"com/google/android/exoplayer2/util/"
|
||||
"FlacStreamMetadata");
|
||||
jmethodID flacStreamMetadataConstructor = env->GetMethodID(
|
||||
flacStreamMetadataClass, "<init>", "(IIIIIIIJLjava/util/List;)V");
|
||||
flacStreamMetadataClass, "<init>",
|
||||
"(IIIIIIIJLjava/util/List;Ljava/util/List;)V");
|
||||
|
||||
return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor,
|
||||
streamInfo.min_blocksize, streamInfo.max_blocksize,
|
||||
streamInfo.min_framesize, streamInfo.max_framesize,
|
||||
streamInfo.sample_rate, streamInfo.channels,
|
||||
streamInfo.bits_per_sample, streamInfo.total_samples,
|
||||
commentList);
|
||||
jobject streamMetaData = env->NewObject(flacStreamMetadataClass,
|
||||
flacStreamMetadataConstructor,
|
||||
streamInfo.min_blocksize,
|
||||
streamInfo.max_blocksize,
|
||||
streamInfo.min_framesize,
|
||||
streamInfo.max_framesize,
|
||||
streamInfo.sample_rate,
|
||||
streamInfo.channels,
|
||||
streamInfo.bits_per_sample,
|
||||
streamInfo.total_samples,
|
||||
commentList, pictureList);
|
||||
return streamMetaData;
|
||||
}
|
||||
|
||||
DECODER_FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
|
||||
|
@ -191,6 +191,22 @@ void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) {
|
||||
ALOGE("FLACParser::metadataCallback unexpected VORBISCOMMENT");
|
||||
}
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_PICTURE:
|
||||
{
|
||||
const FLAC__StreamMetadata_Picture *pic = &metadata->data.picture;
|
||||
flacPicture picture;
|
||||
picture.mimeType.assign(std::string(pic->mime_type));
|
||||
picture.description.assign(std::string((char *)pic->description));
|
||||
picture.data.assign(pic->data, pic->data + pic->data_length);
|
||||
picture.width = pic->width;
|
||||
picture.height = pic->height;
|
||||
picture.depth = pic->depth;
|
||||
picture.colors = pic->colors;
|
||||
picture.type = pic->type;
|
||||
mPictures.push_back(picture);
|
||||
mPicValid = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ALOGE("FLACParser::metadataCallback unexpected type %u", metadata->type);
|
||||
break;
|
||||
@ -253,6 +269,7 @@ FLACParser::FLACParser(DataSource *source)
|
||||
mEOF(false),
|
||||
mStreamInfoValid(false),
|
||||
mVorbisCommentsValid(false),
|
||||
mPicValid(false),
|
||||
mWriteRequested(false),
|
||||
mWriteCompleted(false),
|
||||
mWriteBuffer(NULL),
|
||||
@ -288,6 +305,8 @@ bool FLACParser::init() {
|
||||
FLAC__METADATA_TYPE_SEEKTABLE);
|
||||
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||
FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||
FLAC__METADATA_TYPE_PICTURE);
|
||||
FLAC__StreamDecoderInitStatus initStatus;
|
||||
initStatus = FLAC__stream_decoder_init_stream(
|
||||
mDecoder, read_callback, seek_callback, tell_callback, length_callback,
|
||||
|
@ -30,6 +30,17 @@
|
||||
|
||||
typedef int status_t;
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
std::string mimeType;
|
||||
std::string description;
|
||||
FLAC__uint32 width;
|
||||
FLAC__uint32 height;
|
||||
FLAC__uint32 depth;
|
||||
FLAC__uint32 colors;
|
||||
std::vector<char> data;
|
||||
} flacPicture;
|
||||
|
||||
class FLACParser {
|
||||
public:
|
||||
FLACParser(DataSource *source);
|
||||
@ -54,6 +65,10 @@ class FLACParser {
|
||||
return mVorbisComments;
|
||||
}
|
||||
|
||||
bool isPicValid() const { return mPicValid; }
|
||||
|
||||
const std::vector<flacPicture>& getPictures() const { return mPictures; }
|
||||
|
||||
int64_t getLastFrameTimestamp() const {
|
||||
return (1000000LL * mWriteHeader.number.sample_number) / getSampleRate();
|
||||
}
|
||||
@ -82,7 +97,9 @@ class FLACParser {
|
||||
if (newPosition == 0) {
|
||||
mStreamInfoValid = false;
|
||||
mVorbisCommentsValid = false;
|
||||
mPicValid = false;
|
||||
mVorbisComments.clear();
|
||||
mPictures.clear();
|
||||
FLAC__stream_decoder_reset(mDecoder);
|
||||
} else {
|
||||
FLAC__stream_decoder_flush(mDecoder);
|
||||
@ -132,6 +149,10 @@ class FLACParser {
|
||||
std::vector<std::string> mVorbisComments;
|
||||
bool mVorbisCommentsValid;
|
||||
|
||||
// cached when the PICTURE metadata is parsed by libFLAC
|
||||
std::vector<flacPicture> mPictures;
|
||||
bool mPicValid;
|
||||
|
||||
// cached when a decoded PCM block is "written" by libFLAC parser
|
||||
bool mWriteRequested;
|
||||
bool mWriteCompleted;
|
||||
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 com.google.android.exoplayer2.metadata.flac;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** A Picture parsed in a FLAC file. */
|
||||
public final class PictureFrame implements Metadata.Entry {
|
||||
|
||||
/** The type of the picture. */
|
||||
public final int pictureType;
|
||||
/** The mime type of the picture. */
|
||||
public final String mimeType;
|
||||
/** A description of the picture. */
|
||||
@Nullable public final String description;
|
||||
/** The pixel width of the picture. */
|
||||
public final int width;
|
||||
/** The pixel height of the picture. */
|
||||
public final int height;
|
||||
/** The color depth of the picture in bits-per-pixel. */
|
||||
public final int depth;
|
||||
/**
|
||||
* For indexed-color pictures (e.g. GIF), the number of colors used, or 0 for non-indexed
|
||||
* pictures.
|
||||
*/
|
||||
public final int colors;
|
||||
/** The encoded picture data. */
|
||||
public final byte[] pictureData;
|
||||
|
||||
public PictureFrame(
|
||||
int pictureType,
|
||||
String mimeType,
|
||||
@Nullable String description,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int colors,
|
||||
byte[] pictureData) {
|
||||
this.pictureType = pictureType;
|
||||
this.mimeType = mimeType;
|
||||
this.description = description;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.colors = colors;
|
||||
this.pictureData = pictureData;
|
||||
}
|
||||
|
||||
/* package */ PictureFrame(Parcel in) {
|
||||
this.pictureType = in.readInt();
|
||||
this.mimeType = castNonNull(in.readString());
|
||||
this.description = castNonNull(in.readString());
|
||||
this.width = in.readInt();
|
||||
this.height = in.readInt();
|
||||
this.depth = in.readInt();
|
||||
this.colors = in.readInt();
|
||||
this.pictureData = castNonNull(in.createByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FLAC Picture"
|
||||
+ "\nType: " + pictureType
|
||||
+ "\nMime Type: " + mimeType
|
||||
+ "\nDescription: " + description
|
||||
+ "\nWidth: " + width
|
||||
+ "\nHeight: " + height
|
||||
+ "\nDepth: " + depth
|
||||
+ "\nColors: " + colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PictureFrame other = (PictureFrame) obj;
|
||||
return (pictureType == other.pictureType)
|
||||
&& mimeType.equals(other.mimeType)
|
||||
&& description.equals(other.description)
|
||||
&& (width == other.width)
|
||||
&& (height == other.height)
|
||||
&& (depth == other.depth)
|
||||
&& (colors == other.colors)
|
||||
&& Arrays.equals(pictureData, other.pictureData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + pictureType;
|
||||
result = 31 * result + (mimeType != null ? mimeType.hashCode() : 0);
|
||||
result = 31 * result + (description != null ? description.hashCode() : 0);
|
||||
result = 31 * result + width;
|
||||
result = 31 * result + height;
|
||||
result = 31 * result + depth;
|
||||
result = 31 * result + colors;
|
||||
result = 31 * result + Arrays.hashCode(pictureData);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(pictureType);
|
||||
dest.writeString(mimeType);
|
||||
dest.writeString(description);
|
||||
dest.writeInt(width);
|
||||
dest.writeInt(height);
|
||||
dest.writeInt(depth);
|
||||
dest.writeInt(colors);
|
||||
dest.writeByteArray(pictureData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PictureFrame> CREATOR =
|
||||
new Parcelable.Creator<PictureFrame>() {
|
||||
|
||||
@Override
|
||||
public PictureFrame createFromParcel(Parcel in) {
|
||||
return new PictureFrame(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PictureFrame[] newArray(int size) {
|
||||
return new PictureFrame[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.metadata.vorbis;
|
||||
package com.google.android.exoplayer2.metadata.flac;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||
|
@ -18,7 +18,8 @@ package com.google.android.exoplayer2.util;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment;
|
||||
import com.google.android.exoplayer2.metadata.flac.PictureFrame;
|
||||
import com.google.android.exoplayer2.metadata.flac.VorbisComment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -35,7 +36,7 @@ public final class FlacStreamMetadata {
|
||||
public final int channels;
|
||||
public final int bitsPerSample;
|
||||
public final long totalSamples;
|
||||
@Nullable public final Metadata vorbisComments;
|
||||
@Nullable public final Metadata flacMetadata;
|
||||
|
||||
private static final String SEPARATOR = "=";
|
||||
|
||||
@ -58,7 +59,7 @@ public final class FlacStreamMetadata {
|
||||
this.channels = scratch.readBits(3) + 1;
|
||||
this.bitsPerSample = scratch.readBits(5) + 1;
|
||||
this.totalSamples = ((scratch.readBits(4) & 0xFL) << 32) | (scratch.readBits(32) & 0xFFFFFFFFL);
|
||||
this.vorbisComments = null;
|
||||
this.flacMetadata = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,10 +72,13 @@ public final class FlacStreamMetadata {
|
||||
* @param bitsPerSample Number of bits per sample of the FLAC stream.
|
||||
* @param totalSamples Total samples of the FLAC stream.
|
||||
* @param vorbisComments Vorbis comments. Each entry must be in key=value form.
|
||||
* @param pictureList A list of pictures in the stream.
|
||||
* @see <a href="https://xiph.org/flac/format.html#metadata_block_streaminfo">FLAC format
|
||||
* METADATA_BLOCK_STREAMINFO</a>
|
||||
* @see <a href="https://xiph.org/flac/format.html#metadata_block_vorbis_comment">FLAC format
|
||||
* METADATA_BLOCK_VORBIS_COMMENT</a>
|
||||
* @see <a href="https://xiph.org/flac/format.html#metadata_block_picture">FLAC format
|
||||
* METADATA_BLOCK_PICTURE</a>
|
||||
*/
|
||||
public FlacStreamMetadata(
|
||||
int minBlockSize,
|
||||
@ -85,7 +89,8 @@ public final class FlacStreamMetadata {
|
||||
int channels,
|
||||
int bitsPerSample,
|
||||
long totalSamples,
|
||||
List<String> vorbisComments) {
|
||||
List<String> vorbisComments,
|
||||
List<PictureFrame> pictureList) {
|
||||
this.minBlockSize = minBlockSize;
|
||||
this.maxBlockSize = maxBlockSize;
|
||||
this.minFrameSize = minFrameSize;
|
||||
@ -94,7 +99,8 @@ public final class FlacStreamMetadata {
|
||||
this.channels = channels;
|
||||
this.bitsPerSample = bitsPerSample;
|
||||
this.totalSamples = totalSamples;
|
||||
this.vorbisComments = parseVorbisComments(vorbisComments);
|
||||
Metadata metadata = new Metadata(pictureList);
|
||||
this.flacMetadata = metadata.copyWithAppendedEntriesFrom(parseVorbisComments(vorbisComments));
|
||||
}
|
||||
|
||||
/** Returns the maximum size for a decoded frame from the FLAC stream. */
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 com.google.android.exoplayer2.metadata.flac;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.Parcel;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Test for {@link PictureFrame}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PictureTest {
|
||||
|
||||
@Test
|
||||
public void testParcelable() {
|
||||
PictureFrame pictureFrameToParcel =
|
||||
new PictureFrame(0, "", "", 0, 0, 0, 0, new byte[0]);
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
pictureFrameToParcel.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
PictureFrame pictureFrameFromParcel = PictureFrame.CREATOR.createFromParcel(parcel);
|
||||
assertThat(pictureFrameFromParcel).isEqualTo(pictureFrameToParcel);
|
||||
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.metadata.vorbis;
|
||||
package com.google.android.exoplayer2.metadata.flac;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
@ -19,7 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment;
|
||||
import com.google.android.exoplayer2.metadata.flac.VorbisComment;
|
||||
import java.util.ArrayList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -34,7 +34,9 @@ public final class FlacStreamMetadataTest {
|
||||
commentsList.add("Title=Song");
|
||||
commentsList.add("Artist=Singer");
|
||||
|
||||
Metadata metadata = new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList).vorbisComments;
|
||||
Metadata metadata =
|
||||
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>())
|
||||
.flacMetadata;
|
||||
|
||||
assertThat(metadata.length()).isEqualTo(2);
|
||||
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
|
||||
@ -49,7 +51,9 @@ public final class FlacStreamMetadataTest {
|
||||
public void parseEmptyVorbisComments() {
|
||||
ArrayList<String> commentsList = new ArrayList<>();
|
||||
|
||||
Metadata metadata = new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList).vorbisComments;
|
||||
Metadata metadata =
|
||||
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>())
|
||||
.flacMetadata;
|
||||
|
||||
assertThat(metadata).isNull();
|
||||
}
|
||||
@ -59,7 +63,9 @@ public final class FlacStreamMetadataTest {
|
||||
ArrayList<String> commentsList = new ArrayList<>();
|
||||
commentsList.add("Title=So=ng");
|
||||
|
||||
Metadata metadata = new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList).vorbisComments;
|
||||
Metadata metadata =
|
||||
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>())
|
||||
.flacMetadata;
|
||||
|
||||
assertThat(metadata.length()).isEqualTo(1);
|
||||
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
|
||||
@ -73,7 +79,9 @@ public final class FlacStreamMetadataTest {
|
||||
commentsList.add("TitleSong");
|
||||
commentsList.add("Artist=Singer");
|
||||
|
||||
Metadata metadata = new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList).vorbisComments;
|
||||
Metadata metadata =
|
||||
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>())
|
||||
.flacMetadata;
|
||||
|
||||
assertThat(metadata.length()).isEqualTo(1);
|
||||
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
|
||||
|
@ -49,6 +49,7 @@ import com.google.android.exoplayer2.PlaybackPreparer;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.flac.PictureFrame;
|
||||
import com.google.android.exoplayer2.metadata.id3.ApicFrame;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
@ -303,6 +304,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
|
||||
private boolean controllerHideOnTouch;
|
||||
private int textureViewRotation;
|
||||
private boolean isTouching;
|
||||
private static final int PICTURE_TYPE_FRONT_COVER = 3;
|
||||
|
||||
public PlayerView(Context context) {
|
||||
this(context, null);
|
||||
@ -1246,15 +1248,29 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
|
||||
}
|
||||
|
||||
private boolean setArtworkFromMetadata(Metadata metadata) {
|
||||
boolean isArtworkSet = false;
|
||||
int currentPicType = -1;
|
||||
for (int i = 0; i < metadata.length(); i++) {
|
||||
Metadata.Entry metadataEntry = metadata.get(i);
|
||||
int picType;
|
||||
byte[] bitmapData;
|
||||
if (metadataEntry instanceof ApicFrame) {
|
||||
byte[] bitmapData = ((ApicFrame) metadataEntry).pictureData;
|
||||
bitmapData = ((ApicFrame) metadataEntry).pictureData;
|
||||
picType = ((ApicFrame) metadataEntry).pictureType;
|
||||
} else if (metadataEntry instanceof PictureFrame) {
|
||||
bitmapData = ((PictureFrame) metadataEntry).pictureData;
|
||||
picType = ((PictureFrame) metadataEntry).pictureType;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
/* Prefers the first front cover picture in the picture list */
|
||||
if (currentPicType != PICTURE_TYPE_FRONT_COVER) {
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length);
|
||||
return setDrawableArtwork(new BitmapDrawable(getResources(), bitmap));
|
||||
isArtworkSet = setDrawableArtwork(new BitmapDrawable(getResources(), bitmap));
|
||||
currentPicType = picType;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return isArtworkSet;
|
||||
}
|
||||
|
||||
private boolean setDrawableArtwork(@Nullable Drawable drawable) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user