FlacExtractorTest

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=124357946
This commit is contained in:
eguven 2016-06-08 09:07:05 -07:00 committed by Oliver Woodman
parent 544d88ca56
commit 4b3c72c057
9 changed files with 314 additions and 14 deletions

View File

@ -41,5 +41,6 @@ android {
dependencies {
compile project(':library')
androidTestCompile project(':testutils')
}

Binary file not shown.

View 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

View File

@ -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());
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;