mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add vorbis comments support to flac extractor
Decode and add vorbis comments from the flac file to metadata. #5527
This commit is contained in:
parent
65d9c11027
commit
77e1e4cc1e
9 changed files with 394 additions and 1 deletions
|
|
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.FlacStreamInfo;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JNI wrapper for the libflac Flac decoder.
|
* JNI wrapper for the libflac Flac decoder.
|
||||||
|
|
@ -151,6 +152,12 @@ import java.nio.ByteBuffer;
|
||||||
return streamInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decodes and consumes the Vorbis Comment section from the FLAC stream. */
|
||||||
|
@Nullable
|
||||||
|
public ArrayList<String> decodeVorbisComment() throws IOException, InterruptedException {
|
||||||
|
return flacDecodeVorbisComment(nativeDecoderContext);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes and consumes the next frame from the FLAC stream into the given byte buffer. If any IO
|
* Decodes and consumes the next frame from the FLAC stream into the given byte buffer. If any IO
|
||||||
* error occurs, resets the stream and input to the given {@code retryPosition}.
|
* error occurs, resets the stream and input to the given {@code retryPosition}.
|
||||||
|
|
@ -269,6 +276,9 @@ import java.nio.ByteBuffer;
|
||||||
private native FlacStreamInfo flacDecodeMetadata(long context)
|
private native FlacStreamInfo flacDecodeMetadata(long context)
|
||||||
throws IOException, InterruptedException;
|
throws IOException, InterruptedException;
|
||||||
|
|
||||||
|
private native ArrayList<String> flacDecodeVorbisComment(long context)
|
||||||
|
throws IOException, InterruptedException;
|
||||||
|
|
||||||
private native int flacDecodeToBuffer(long context, ByteBuffer outputBuffer)
|
private native int flacDecodeToBuffer(long context, ByteBuffer outputBuffer)
|
||||||
throws IOException, InterruptedException;
|
throws IOException, InterruptedException;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import com.google.android.exoplayer2.extractor.SeekPoint;
|
||||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||||
|
import com.google.android.exoplayer2.metadata.vorbis.VorbisCommentDecoder;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.FlacStreamInfo;
|
import com.google.android.exoplayer2.util.FlacStreamInfo;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
@ -42,6 +43,7 @@ import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
@ -91,6 +93,7 @@ public final class FlacExtractor implements Extractor {
|
||||||
private @MonotonicNonNull OutputFrameHolder outputFrameHolder;
|
private @MonotonicNonNull OutputFrameHolder outputFrameHolder;
|
||||||
|
|
||||||
@Nullable private Metadata id3Metadata;
|
@Nullable private Metadata id3Metadata;
|
||||||
|
@Nullable private Metadata vorbisMetadata;
|
||||||
@Nullable private FlacBinarySearchSeeker binarySearchSeeker;
|
@Nullable private FlacBinarySearchSeeker binarySearchSeeker;
|
||||||
|
|
||||||
/** Constructs an instance with flags = 0. */
|
/** Constructs an instance with flags = 0. */
|
||||||
|
|
@ -224,11 +227,16 @@ public final class FlacExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
streamInfoDecoded = true;
|
streamInfoDecoded = true;
|
||||||
|
vorbisMetadata = decodeVorbisComment(input);
|
||||||
if (this.streamInfo == null) {
|
if (this.streamInfo == null) {
|
||||||
this.streamInfo = streamInfo;
|
this.streamInfo = streamInfo;
|
||||||
binarySearchSeeker =
|
binarySearchSeeker =
|
||||||
outputSeekMap(decoderJni, streamInfo, input.getLength(), extractorOutput);
|
outputSeekMap(decoderJni, streamInfo, input.getLength(), extractorOutput);
|
||||||
outputFormat(streamInfo, id3MetadataDisabled ? null : id3Metadata, trackOutput);
|
Metadata metadata = id3MetadataDisabled ? null : id3Metadata;
|
||||||
|
if (vorbisMetadata != null) {
|
||||||
|
metadata = vorbisMetadata.copyWithAppendedEntriesFrom(metadata);
|
||||||
|
}
|
||||||
|
outputFormat(streamInfo, metadata, trackOutput);
|
||||||
outputBuffer.reset(streamInfo.maxDecodedFrameSize());
|
outputBuffer.reset(streamInfo.maxDecodedFrameSize());
|
||||||
outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data));
|
outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data));
|
||||||
}
|
}
|
||||||
|
|
@ -262,6 +270,19 @@ public final class FlacExtractor implements Extractor {
|
||||||
return Arrays.equals(header, FLAC_SIGNATURE);
|
return Arrays.equals(header, FLAC_SIGNATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Metadata decodeVorbisComment(ExtractorInput input)
|
||||||
|
throws InterruptedException, IOException {
|
||||||
|
try {
|
||||||
|
ArrayList<String> vorbisCommentList = decoderJni.decodeVorbisComment();
|
||||||
|
return new VorbisCommentDecoder().decodeVorbisComments(vorbisCommentList);
|
||||||
|
} catch (IOException e) {
|
||||||
|
decoderJni.reset(0);
|
||||||
|
input.setRetryPosition(0, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs a {@link SeekMap} and returns a {@link FlacBinarySearchSeeker} if one is required to
|
* Outputs a {@link SeekMap} and returns a {@link FlacBinarySearchSeeker} if one is required to
|
||||||
* handle seeks.
|
* handle seeks.
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,32 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
|
||||||
streamInfo.total_samples);
|
streamInfo.total_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECODER_FUNC(jobject, flacDecodeVorbisComment, jlong jContext) {
|
||||||
|
Context *context = reinterpret_cast<Context *>(jContext);
|
||||||
|
context->source->setFlacDecoderJni(env, thiz);
|
||||||
|
|
||||||
|
VorbisComment vorbisComment = context->parser->getVorbisComment();
|
||||||
|
|
||||||
|
if (vorbisComment.numComments == 0) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
jclass java_util_ArrayList = env->FindClass("java/util/ArrayList");
|
||||||
|
|
||||||
|
jmethodID java_util_ArrayList_ = env->GetMethodID(java_util_ArrayList, "<init>", "(I)V");
|
||||||
|
jmethodID java_util_ArrayList_add = env->GetMethodID(java_util_ArrayList, "add",
|
||||||
|
"(Ljava/lang/Object;)Z");
|
||||||
|
|
||||||
|
jobject result = env->NewObject(java_util_ArrayList, java_util_ArrayList_,
|
||||||
|
vorbisComment.numComments);
|
||||||
|
for (FLAC__uint32 i = 0; i < vorbisComment.numComments; ++i) {
|
||||||
|
jstring element = env->NewStringUTF(vorbisComment.metadataArray[i]);
|
||||||
|
env->CallBooleanMethod(result, java_util_ArrayList_add, element);
|
||||||
|
env->DeleteLocalRef(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DECODER_FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
|
DECODER_FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
|
||||||
Context *context = reinterpret_cast<Context *>(jContext);
|
Context *context = reinterpret_cast<Context *>(jContext);
|
||||||
context->source->setFlacDecoderJni(env, thiz);
|
context->source->setFlacDecoderJni(env, thiz);
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,30 @@ void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) {
|
||||||
case FLAC__METADATA_TYPE_SEEKTABLE:
|
case FLAC__METADATA_TYPE_SEEKTABLE:
|
||||||
mSeekTable = &metadata->data.seek_table;
|
mSeekTable = &metadata->data.seek_table;
|
||||||
break;
|
break;
|
||||||
|
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||||
|
if (!mVorbisCommentValid) {
|
||||||
|
FLAC__uint32 count = 0;
|
||||||
|
const FLAC__StreamMetadata_VorbisComment *vc =
|
||||||
|
&metadata->data.vorbis_comment;
|
||||||
|
mVorbisCommentValid = true;
|
||||||
|
mVorbisComment.metadataArray =
|
||||||
|
(char **) malloc(vc->num_comments * sizeof(char *));
|
||||||
|
for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) {
|
||||||
|
FLAC__StreamMetadata_VorbisComment_Entry *vce = &vc->comments[i];
|
||||||
|
if (vce->entry != NULL) {
|
||||||
|
mVorbisComment.metadataArray[count] =
|
||||||
|
(char *) malloc((vce->length + 1) * sizeof(char));
|
||||||
|
memcpy(mVorbisComment.metadataArray[count], vce->entry,
|
||||||
|
vce->length);
|
||||||
|
mVorbisComment.metadataArray[count][vce->length] = '\0';
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mVorbisComment.numComments = count;
|
||||||
|
} else {
|
||||||
|
ALOGE("FLACParser::metadataCallback unexpected VORBISCOMMENT");
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ALOGE("FLACParser::metadataCallback unexpected type %u", metadata->type);
|
ALOGE("FLACParser::metadataCallback unexpected type %u", metadata->type);
|
||||||
break;
|
break;
|
||||||
|
|
@ -233,6 +257,7 @@ FLACParser::FLACParser(DataSource *source)
|
||||||
mCurrentPos(0LL),
|
mCurrentPos(0LL),
|
||||||
mEOF(false),
|
mEOF(false),
|
||||||
mStreamInfoValid(false),
|
mStreamInfoValid(false),
|
||||||
|
mVorbisCommentValid(false),
|
||||||
mWriteRequested(false),
|
mWriteRequested(false),
|
||||||
mWriteCompleted(false),
|
mWriteCompleted(false),
|
||||||
mWriteBuffer(NULL),
|
mWriteBuffer(NULL),
|
||||||
|
|
@ -240,6 +265,7 @@ FLACParser::FLACParser(DataSource *source)
|
||||||
ALOGV("FLACParser::FLACParser");
|
ALOGV("FLACParser::FLACParser");
|
||||||
memset(&mStreamInfo, 0, sizeof(mStreamInfo));
|
memset(&mStreamInfo, 0, sizeof(mStreamInfo));
|
||||||
memset(&mWriteHeader, 0, sizeof(mWriteHeader));
|
memset(&mWriteHeader, 0, sizeof(mWriteHeader));
|
||||||
|
memset(&mVorbisComment, 0, sizeof(mVorbisComment));
|
||||||
}
|
}
|
||||||
|
|
||||||
FLACParser::~FLACParser() {
|
FLACParser::~FLACParser() {
|
||||||
|
|
@ -266,6 +292,8 @@ bool FLACParser::init() {
|
||||||
FLAC__METADATA_TYPE_STREAMINFO);
|
FLAC__METADATA_TYPE_STREAMINFO);
|
||||||
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||||
FLAC__METADATA_TYPE_SEEKTABLE);
|
FLAC__METADATA_TYPE_SEEKTABLE);
|
||||||
|
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||||
|
FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||||
FLAC__StreamDecoderInitStatus initStatus;
|
FLAC__StreamDecoderInitStatus initStatus;
|
||||||
initStatus = FLAC__stream_decoder_init_stream(
|
initStatus = FLAC__stream_decoder_init_stream(
|
||||||
mDecoder, read_callback, seek_callback, tell_callback, length_callback,
|
mDecoder, read_callback, seek_callback, tell_callback, length_callback,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@
|
||||||
|
|
||||||
typedef int status_t;
|
typedef int status_t;
|
||||||
|
|
||||||
|
typedef struct VorbisComment_ {
|
||||||
|
int numComments;
|
||||||
|
char **metadataArray;
|
||||||
|
} VorbisComment;
|
||||||
|
|
||||||
class FLACParser {
|
class FLACParser {
|
||||||
public:
|
public:
|
||||||
FLACParser(DataSource *source);
|
FLACParser(DataSource *source);
|
||||||
|
|
@ -71,6 +76,7 @@ class FLACParser {
|
||||||
mEOF = false;
|
mEOF = false;
|
||||||
if (newPosition == 0) {
|
if (newPosition == 0) {
|
||||||
mStreamInfoValid = false;
|
mStreamInfoValid = false;
|
||||||
|
mVorbisCommentValid = false;
|
||||||
FLAC__stream_decoder_reset(mDecoder);
|
FLAC__stream_decoder_reset(mDecoder);
|
||||||
} else {
|
} else {
|
||||||
FLAC__stream_decoder_flush(mDecoder);
|
FLAC__stream_decoder_flush(mDecoder);
|
||||||
|
|
@ -96,6 +102,10 @@ class FLACParser {
|
||||||
FLAC__STREAM_DECODER_END_OF_STREAM;
|
FLAC__STREAM_DECODER_END_OF_STREAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VorbisComment getVorbisComment() {
|
||||||
|
return mVorbisComment;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DataSource *mDataSource;
|
DataSource *mDataSource;
|
||||||
|
|
||||||
|
|
@ -116,6 +126,8 @@ class FLACParser {
|
||||||
const FLAC__StreamMetadata_SeekTable *mSeekTable;
|
const FLAC__StreamMetadata_SeekTable *mSeekTable;
|
||||||
uint64_t firstFrameOffset;
|
uint64_t firstFrameOffset;
|
||||||
|
|
||||||
|
bool mVorbisCommentValid;
|
||||||
|
|
||||||
// cached when a decoded PCM block is "written" by libFLAC parser
|
// cached when a decoded PCM block is "written" by libFLAC parser
|
||||||
bool mWriteRequested;
|
bool mWriteRequested;
|
||||||
bool mWriteCompleted;
|
bool mWriteCompleted;
|
||||||
|
|
@ -129,6 +141,8 @@ class FLACParser {
|
||||||
FLACParser(const FLACParser &);
|
FLACParser(const FLACParser &);
|
||||||
FLACParser &operator=(const FLACParser &);
|
FLACParser &operator=(const FLACParser &);
|
||||||
|
|
||||||
|
VorbisComment mVorbisComment;
|
||||||
|
|
||||||
// FLAC parser callbacks as C++ instance methods
|
// FLAC parser callbacks as C++ instance methods
|
||||||
FLAC__StreamDecoderReadStatus readCallback(FLAC__byte buffer[],
|
FLAC__StreamDecoderReadStatus readCallback(FLAC__byte buffer[],
|
||||||
size_t *bytes);
|
size_t *bytes);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.vorbis;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/** Decodes vorbis comments */
|
||||||
|
public class VorbisCommentDecoder {
|
||||||
|
|
||||||
|
private static final String SEPARATOR = "=";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes an {@link ArrayList} of vorbis comments.
|
||||||
|
*
|
||||||
|
* @param metadataStringList An {@link ArrayList} containing vorbis comments as {@link String}
|
||||||
|
* @return A {@link Metadata} structure with the vorbis comments as its entries.
|
||||||
|
*/
|
||||||
|
public Metadata decodeVorbisComments(@Nullable ArrayList<String> metadataStringList) {
|
||||||
|
if (metadataStringList == null || metadataStringList.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<VorbisCommentFrame> vorbisCommentFrames = new ArrayList<>();
|
||||||
|
VorbisCommentFrame vorbisCommentFrame;
|
||||||
|
|
||||||
|
for (String commentEntry : metadataStringList) {
|
||||||
|
String[] keyValue;
|
||||||
|
|
||||||
|
keyValue = commentEntry.split(SEPARATOR);
|
||||||
|
if (keyValue.length != 2) {
|
||||||
|
/* Could not parse this comment, no key value pair found */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vorbisCommentFrame = new VorbisCommentFrame(keyValue[0], keyValue[1]);
|
||||||
|
vorbisCommentFrames.add(vorbisCommentFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vorbisCommentFrames.size() > 0) {
|
||||||
|
return new Metadata(vorbisCommentFrames);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* 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.vorbis;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** Base class for Vorbis Comment Frames. */
|
||||||
|
public class VorbisCommentFrame implements Metadata.Entry {
|
||||||
|
|
||||||
|
/** The frame key and value */
|
||||||
|
public final String key;
|
||||||
|
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key The key
|
||||||
|
* @param value Value corresponding to the key
|
||||||
|
*/
|
||||||
|
public VorbisCommentFrame(String key, String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ VorbisCommentFrame(Parcel in) {
|
||||||
|
this.key = castNonNull(in.readString());
|
||||||
|
this.value = castNonNull(in.readString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parcelable implementation.
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(key);
|
||||||
|
dest.writeString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
if ((obj != null) && (obj.getClass() == this.getClass())) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
VorbisCommentFrame compareFrame = (VorbisCommentFrame) obj;
|
||||||
|
if (this.key.equals(compareFrame.key) && this.value.equals(compareFrame.value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 17;
|
||||||
|
|
||||||
|
result = 31 * result + key.hashCode();
|
||||||
|
result = 31 * result + value.hashCode();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<VorbisCommentFrame> CREATOR =
|
||||||
|
new Parcelable.Creator<VorbisCommentFrame>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VorbisCommentFrame createFromParcel(Parcel in) {
|
||||||
|
return new VorbisCommentFrame(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VorbisCommentFrame[] newArray(int size) {
|
||||||
|
return new VorbisCommentFrame[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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.vorbis;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Test for {@link VorbisCommentDecoder}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class VorbisCommentDecoderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decode() {
|
||||||
|
VorbisCommentDecoder decoder = new VorbisCommentDecoder();
|
||||||
|
ArrayList<String> commentsList = new ArrayList<>();
|
||||||
|
|
||||||
|
commentsList.add("Title=Test");
|
||||||
|
commentsList.add("Artist=Test2");
|
||||||
|
|
||||||
|
Metadata metadata = decoder.decodeVorbisComments(commentsList);
|
||||||
|
|
||||||
|
assertThat(metadata.length()).isEqualTo(2);
|
||||||
|
VorbisCommentFrame commentFrame = (VorbisCommentFrame) metadata.get(0);
|
||||||
|
assertThat(commentFrame.key).isEqualTo("Title");
|
||||||
|
assertThat(commentFrame.value).isEqualTo("Test");
|
||||||
|
commentFrame = (VorbisCommentFrame) metadata.get(1);
|
||||||
|
assertThat(commentFrame.key).isEqualTo("Artist");
|
||||||
|
assertThat(commentFrame.value).isEqualTo("Test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeEmptyList() {
|
||||||
|
VorbisCommentDecoder decoder = new VorbisCommentDecoder();
|
||||||
|
ArrayList<String> commentsList = new ArrayList<>();
|
||||||
|
|
||||||
|
Metadata metadata = decoder.decodeVorbisComments(commentsList);
|
||||||
|
|
||||||
|
assertThat(metadata).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeTwoSeparators() {
|
||||||
|
VorbisCommentDecoder decoder = new VorbisCommentDecoder();
|
||||||
|
ArrayList<String> commentsList = new ArrayList<>();
|
||||||
|
|
||||||
|
commentsList.add("Title=Test");
|
||||||
|
commentsList.add("Artist=Test=2");
|
||||||
|
|
||||||
|
Metadata metadata = decoder.decodeVorbisComments(commentsList);
|
||||||
|
|
||||||
|
assertThat(metadata.length()).isEqualTo(1);
|
||||||
|
VorbisCommentFrame commentFrame = (VorbisCommentFrame) metadata.get(0);
|
||||||
|
assertThat(commentFrame.key).isEqualTo("Title");
|
||||||
|
assertThat(commentFrame.value).isEqualTo("Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeNoSeparators() {
|
||||||
|
VorbisCommentDecoder decoder = new VorbisCommentDecoder();
|
||||||
|
ArrayList<String> commentsList = new ArrayList<>();
|
||||||
|
|
||||||
|
commentsList.add("TitleTest");
|
||||||
|
commentsList.add("Artist=Test2");
|
||||||
|
|
||||||
|
Metadata metadata = decoder.decodeVorbisComments(commentsList);
|
||||||
|
|
||||||
|
assertThat(metadata.length()).isEqualTo(1);
|
||||||
|
VorbisCommentFrame commentFrame = (VorbisCommentFrame) metadata.get(0);
|
||||||
|
assertThat(commentFrame.key).isEqualTo("Artist");
|
||||||
|
assertThat(commentFrame.value).isEqualTo("Test2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.vorbis;
|
||||||
|
|
||||||
|
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 VorbisCommentFrame}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class VorbisCommentFrameTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParcelable() {
|
||||||
|
VorbisCommentFrame vorbisCommentFrameToParcel = new VorbisCommentFrame("key", "value");
|
||||||
|
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
vorbisCommentFrameToParcel.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
|
||||||
|
VorbisCommentFrame vorbisCommentFrameFromParcel =
|
||||||
|
VorbisCommentFrame.CREATOR.createFromParcel(parcel);
|
||||||
|
assertThat(vorbisCommentFrameFromParcel).isEqualTo(vorbisCommentFrameToParcel);
|
||||||
|
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue