mirror of
https://github.com/androidx/media.git
synced 2025-05-12 10:09:55 +08:00
FlacExtractorTest
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=124357946
This commit is contained in:
parent
544d88ca56
commit
4b3c72c057
@ -41,5 +41,6 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
androidTestCompile project(':testutils')
|
||||
}
|
||||
|
||||
|
BIN
extensions/flac/src/androidTest/assets/bear.flac
Normal file
BIN
extensions/flac/src/androidTest/assets/bear.flac
Normal file
Binary file not shown.
162
extensions/flac/src/androidTest/assets/bear.flac.dump
Normal file
162
extensions/flac/src/androidTest/assets/bear.flac.dump
Normal file
@ -0,0 +1,162 @@
|
||||
seekMap:
|
||||
isSeekable = true
|
||||
duration = 2741000
|
||||
getPosition(0) = 8304
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/raw
|
||||
maxInputSize = -1
|
||||
requiresSecureDecryption = false
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
rotationDegrees = -1
|
||||
pixelWidthHeightRatio = -1.0
|
||||
channelCount = 2
|
||||
sampleRate = 48000
|
||||
pcmEncoding = 2
|
||||
encoderDelay = -1
|
||||
encoderPadding = -1
|
||||
subsampleOffsetUs = 9223372036854775807
|
||||
selectionFlags = 0
|
||||
language = null
|
||||
drmInitData = -
|
||||
initializationData:
|
||||
sample count = 33
|
||||
sample 0:
|
||||
time = 0
|
||||
flags = 1
|
||||
data = length 16384, hash 61D2C5C2
|
||||
sample 1:
|
||||
time = 85333
|
||||
flags = 1
|
||||
data = length 16384, hash E6D7F214
|
||||
sample 2:
|
||||
time = 170666
|
||||
flags = 1
|
||||
data = length 16384, hash 59BF0D5D
|
||||
sample 3:
|
||||
time = 256000
|
||||
flags = 1
|
||||
data = length 16384, hash 3625F468
|
||||
sample 4:
|
||||
time = 341333
|
||||
flags = 1
|
||||
data = length 16384, hash F66A323
|
||||
sample 5:
|
||||
time = 426666
|
||||
flags = 1
|
||||
data = length 16384, hash CDBAE629
|
||||
sample 6:
|
||||
time = 512000
|
||||
flags = 1
|
||||
data = length 16384, hash 536F3A91
|
||||
sample 7:
|
||||
time = 597333
|
||||
flags = 1
|
||||
data = length 16384, hash D4F35C9C
|
||||
sample 8:
|
||||
time = 682666
|
||||
flags = 1
|
||||
data = length 16384, hash EE04CEBF
|
||||
sample 9:
|
||||
time = 768000
|
||||
flags = 1
|
||||
data = length 16384, hash 647E2A67
|
||||
sample 10:
|
||||
time = 853333
|
||||
flags = 1
|
||||
data = length 16384, hash 31583F2C
|
||||
sample 11:
|
||||
time = 938666
|
||||
flags = 1
|
||||
data = length 16384, hash E433A93D
|
||||
sample 12:
|
||||
time = 1024000
|
||||
flags = 1
|
||||
data = length 16384, hash 5E1C7051
|
||||
sample 13:
|
||||
time = 1109333
|
||||
flags = 1
|
||||
data = length 16384, hash 43E6E358
|
||||
sample 14:
|
||||
time = 1194666
|
||||
flags = 1
|
||||
data = length 16384, hash 5DC1B256
|
||||
sample 15:
|
||||
time = 1280000
|
||||
flags = 1
|
||||
data = length 16384, hash 3D9D95CF
|
||||
sample 16:
|
||||
time = 1365333
|
||||
flags = 1
|
||||
data = length 16384, hash 2A5BD2C0
|
||||
sample 17:
|
||||
time = 1450666
|
||||
flags = 1
|
||||
data = length 16384, hash 93E25061
|
||||
sample 18:
|
||||
time = 1536000
|
||||
flags = 1
|
||||
data = length 16384, hash B81793D8
|
||||
sample 19:
|
||||
time = 1621333
|
||||
flags = 1
|
||||
data = length 16384, hash 1A3BD49F
|
||||
sample 20:
|
||||
time = 1706666
|
||||
flags = 1
|
||||
data = length 16384, hash FB672FF1
|
||||
sample 21:
|
||||
time = 1792000
|
||||
flags = 1
|
||||
data = length 16384, hash 48AB8B45
|
||||
sample 22:
|
||||
time = 1877333
|
||||
flags = 1
|
||||
data = length 16384, hash 13C9640A
|
||||
sample 23:
|
||||
time = 1962666
|
||||
flags = 1
|
||||
data = length 16384, hash 499E4A0B
|
||||
sample 24:
|
||||
time = 2048000
|
||||
flags = 1
|
||||
data = length 16384, hash F9A783E6
|
||||
sample 25:
|
||||
time = 2133333
|
||||
flags = 1
|
||||
data = length 16384, hash D2B77598
|
||||
sample 26:
|
||||
time = 2218666
|
||||
flags = 1
|
||||
data = length 16384, hash CE5B826C
|
||||
sample 27:
|
||||
time = 2304000
|
||||
flags = 1
|
||||
data = length 16384, hash E99EE956
|
||||
sample 28:
|
||||
time = 2389333
|
||||
flags = 1
|
||||
data = length 16384, hash F2DB1486
|
||||
sample 29:
|
||||
time = 2474666
|
||||
flags = 1
|
||||
data = length 16384, hash 1636EAB
|
||||
sample 30:
|
||||
time = 2560000
|
||||
flags = 1
|
||||
data = length 16384, hash 23457C08
|
||||
sample 31:
|
||||
time = 2645333
|
||||
flags = 1
|
||||
data = length 16384, hash 30EB8381
|
||||
sample 32:
|
||||
time = 2730666
|
||||
flags = 1
|
||||
data = length 1984, hash 59CFDE1B
|
||||
tracksEnded = true
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exoplayer.ext.flac;
|
||||
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.testutil.TestUtil;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link FlacExtractor}.
|
||||
*/
|
||||
public class FlacExtractorTest extends InstrumentationTestCase {
|
||||
|
||||
public void testSample() throws Exception {
|
||||
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
|
||||
@Override
|
||||
public Extractor create() {
|
||||
return new FlacExtractor();
|
||||
}
|
||||
}, "bear.flac", getInstrumentation());
|
||||
}
|
||||
}
|
@ -77,9 +77,16 @@ public final class FlacExtractor implements Extractor {
|
||||
decoder.setData(input);
|
||||
|
||||
if (!metadataParsed) {
|
||||
final FlacStreamInfo streamInfo = decoder.decodeMetadata();
|
||||
if (streamInfo == null) {
|
||||
throw new IOException("Metadata decoding failed");
|
||||
final FlacStreamInfo streamInfo;
|
||||
try {
|
||||
streamInfo = decoder.decodeMetadata();
|
||||
if (streamInfo == null) {
|
||||
throw new IOException("Metadata decoding failed");
|
||||
}
|
||||
} catch (IOException e){
|
||||
decoder.reset(0);
|
||||
input.setRetryPosition(0, e);
|
||||
throw e; // never executes
|
||||
}
|
||||
metadataParsed = true;
|
||||
|
||||
@ -114,7 +121,17 @@ public final class FlacExtractor implements Extractor {
|
||||
}
|
||||
|
||||
outputBuffer.reset();
|
||||
int size = decoder.decodeSample(outputByteBuffer);
|
||||
long lastDecodePosition = decoder.getDecodePosition();
|
||||
int size;
|
||||
try {
|
||||
size = decoder.decodeSample(outputByteBuffer);
|
||||
} catch (IOException e){
|
||||
if (lastDecodePosition >= 0) {
|
||||
decoder.reset(lastDecodePosition);
|
||||
input.setRetryPosition(lastDecodePosition, e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
if (size <= 0) {
|
||||
return RESULT_END_OF_INPUT;
|
||||
}
|
||||
@ -128,7 +145,10 @@ public final class FlacExtractor implements Extractor {
|
||||
|
||||
@Override
|
||||
public void seek(long position) {
|
||||
decoder.flush();
|
||||
if (position == 0) {
|
||||
metadataParsed = false;
|
||||
}
|
||||
decoder.reset(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -140,6 +140,13 @@ import java.nio.ByteBuffer;
|
||||
: flacDecodeToArray(nativeDecoderContext, output.array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The position of the next data to be decoded or -1 in case of error.
|
||||
*/
|
||||
public long getDecodePosition() {
|
||||
return flacGetDecodePosition(nativeDecoderContext);
|
||||
}
|
||||
|
||||
public long getLastSampleTimestamp() {
|
||||
return flacGetLastTimestamp(nativeDecoderContext);
|
||||
}
|
||||
@ -156,10 +163,23 @@ import java.nio.ByteBuffer;
|
||||
return flacGetSeekPosition(nativeDecoderContext, timeUs);
|
||||
}
|
||||
|
||||
public String getStateString() {
|
||||
return flacGetStateString(nativeDecoderContext);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
flacFlush(nativeDecoderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets internal state of the decoder and sets the stream position.
|
||||
*
|
||||
* @param newPosition Stream's new position.
|
||||
*/
|
||||
public void reset(long newPosition) {
|
||||
flacReset(nativeDecoderContext, newPosition);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
flacRelease(nativeDecoderContext);
|
||||
}
|
||||
@ -185,12 +205,18 @@ import java.nio.ByteBuffer;
|
||||
private native int flacDecodeToArray(long context, byte[] outputArray)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
private native long flacGetDecodePosition(long context);
|
||||
|
||||
private native long flacGetLastTimestamp(long context);
|
||||
|
||||
private native long flacGetSeekPosition(long context, long timeUs);
|
||||
|
||||
private native String flacGetStateString(long context);
|
||||
|
||||
private native void flacFlush(long context);
|
||||
|
||||
private native void flacReset(long context, long newPosition);
|
||||
|
||||
private native void flacRelease(long context);
|
||||
|
||||
}
|
||||
|
@ -69,19 +69,31 @@ class JavaDataSource : public DataSource {
|
||||
struct Context {
|
||||
JavaDataSource *source;
|
||||
FLACParser *parser;
|
||||
|
||||
Context() {
|
||||
source = new JavaDataSource();
|
||||
parser = new FLACParser(source);
|
||||
}
|
||||
|
||||
~Context() {
|
||||
delete parser;
|
||||
delete source;
|
||||
}
|
||||
};
|
||||
|
||||
FUNC(jlong, flacInit) {
|
||||
Context *context = new Context;
|
||||
context->source = new JavaDataSource();
|
||||
context->parser = new FLACParser(context->source);
|
||||
if (!context->parser->init()) {
|
||||
delete context;
|
||||
return 0;
|
||||
}
|
||||
return reinterpret_cast<intptr_t>(context);
|
||||
}
|
||||
|
||||
FUNC(jobject, flacDecodeMetadata, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
context->source->setFlacJni(env, thiz);
|
||||
if (!context->parser->init()) {
|
||||
if (!context->parser->decodeMetadata()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -118,6 +130,11 @@ FUNC(jint, flacDecodeToArray, jlong jContext, jbyteArray jOutputArray) {
|
||||
return count;
|
||||
}
|
||||
|
||||
FUNC(jlong, flacGetDecodePosition, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
return context->parser->getDecodePosition();
|
||||
}
|
||||
|
||||
FUNC(jlong, flacGetLastTimestamp, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
return context->parser->getLastTimestamp();
|
||||
@ -128,14 +145,23 @@ FUNC(jlong, flacGetSeekPosition, jlong jContext, jlong timeUs) {
|
||||
return context->parser->getSeekPosition(timeUs);
|
||||
}
|
||||
|
||||
FUNC(jstring, flacGetStateString, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
const char *str = context->parser->getDecoderStateString();
|
||||
return env->NewStringUTF(str);
|
||||
}
|
||||
|
||||
FUNC(void, flacFlush, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
context->parser->flush();
|
||||
}
|
||||
|
||||
FUNC(void, flacReset, jlong jContext, jlong newPosition) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
context->parser->reset(newPosition);
|
||||
}
|
||||
|
||||
FUNC(void, flacRelease, jlong jContext) {
|
||||
Context *context = reinterpret_cast<Context *>(jContext);
|
||||
delete context->parser;
|
||||
delete context->source;
|
||||
delete context;
|
||||
}
|
||||
|
@ -316,9 +316,13 @@ bool FLACParser::init() {
|
||||
ALOGE("init_stream failed %d", initStatus);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FLACParser::decodeMetadata() {
|
||||
// parse all metadata
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) {
|
||||
ALOGE("end_of_metadata failed");
|
||||
ALOGE("metadata decoding failed");
|
||||
return false;
|
||||
}
|
||||
// store first frame offset
|
||||
@ -389,14 +393,14 @@ size_t FLACParser::readBuffer(void *output, size_t output_size) {
|
||||
|
||||
if (!FLAC__stream_decoder_process_single(mDecoder)) {
|
||||
ALOGE("FLACParser::readBuffer process_single failed. Status: %s",
|
||||
FLAC__stream_decoder_get_resolved_state_string(mDecoder));
|
||||
getDecoderStateString());
|
||||
return -1;
|
||||
}
|
||||
if (!mWriteCompleted) {
|
||||
if (FLAC__stream_decoder_get_state(mDecoder) !=
|
||||
FLAC__STREAM_DECODER_END_OF_STREAM) {
|
||||
ALOGE("FLACParser::readBuffer write did not complete. Status: %s",
|
||||
FLAC__stream_decoder_get_resolved_state_string(mDecoder));
|
||||
getDecoderStateString());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -48,16 +48,41 @@ class FLACParser {
|
||||
return (1000000LL * mWriteHeader.number.sample_number) / getSampleRate();
|
||||
}
|
||||
|
||||
bool decodeMetadata();
|
||||
size_t readBuffer(void *output, size_t output_size);
|
||||
|
||||
int64_t getSeekPosition(int64_t timeUs);
|
||||
|
||||
void flush() {
|
||||
reset(mCurrentPos);
|
||||
}
|
||||
|
||||
void reset(int64_t newPosition) {
|
||||
if (mDecoder != NULL) {
|
||||
FLAC__stream_decoder_flush(mDecoder);
|
||||
mCurrentPos = newPosition;
|
||||
mEOF = false;
|
||||
if (newPosition == 0) {
|
||||
mStreamInfoValid = false;
|
||||
FLAC__stream_decoder_reset(mDecoder);
|
||||
} else {
|
||||
FLAC__stream_decoder_flush(mDecoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t getDecodePosition() {
|
||||
uint64_t position;
|
||||
if (mDecoder != NULL
|
||||
&& FLAC__stream_decoder_get_decode_position(mDecoder, &position)) {
|
||||
return position;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *getDecoderStateString() {
|
||||
return FLAC__stream_decoder_get_resolved_state_string(mDecoder);
|
||||
}
|
||||
|
||||
private:
|
||||
DataSource *mDataSource;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user