Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
461
TMessagesProj/jni/exoplayer/flac_parser.cc
Normal file
461
TMessagesProj/jni/exoplayer/flac_parser.cc
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "include/flac_parser.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#define LOG_TAG "FLACParser"
|
||||
#define ALOGE(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#define ALOGV(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
(__android_log_assert(NULL, LOG_TAG, ##__VA_ARGS__))
|
||||
|
||||
#define LITERAL_TO_STRING_INTERNAL(x) #x
|
||||
#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
|
||||
|
||||
#define TRESPASS() \
|
||||
LOG_ALWAYS_FATAL(__FILE__ \
|
||||
":" LITERAL_TO_STRING(__LINE__) " Should not be here.");
|
||||
#define CHECK(x) \
|
||||
if (!(x)) ALOGE("Check failed: %s ", #x)
|
||||
|
||||
const int endian = 1;
|
||||
#define isBigEndian() (*(reinterpret_cast<const char *>(&endian)) == 0)
|
||||
|
||||
// The FLAC parser calls our C++ static callbacks using C calling conventions,
|
||||
// inside FLAC__stream_decoder_process_until_end_of_metadata
|
||||
// and FLAC__stream_decoder_process_single.
|
||||
// We immediately then call our corresponding C++ instance methods
|
||||
// with the same parameter list, but discard redundant information.
|
||||
|
||||
FLAC__StreamDecoderReadStatus FLACParser::read_callback(
|
||||
const FLAC__StreamDecoder * /* decoder */, FLAC__byte buffer[],
|
||||
size_t *bytes, void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)
|
||||
->readCallback(buffer, bytes);
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus FLACParser::seek_callback(
|
||||
const FLAC__StreamDecoder * /* decoder */,
|
||||
FLAC__uint64 absolute_byte_offset, void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)
|
||||
->seekCallback(absolute_byte_offset);
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus FLACParser::tell_callback(
|
||||
const FLAC__StreamDecoder * /* decoder */,
|
||||
FLAC__uint64 *absolute_byte_offset, void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)
|
||||
->tellCallback(absolute_byte_offset);
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus FLACParser::length_callback(
|
||||
const FLAC__StreamDecoder * /* decoder */, FLAC__uint64 *stream_length,
|
||||
void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)
|
||||
->lengthCallback(stream_length);
|
||||
}
|
||||
|
||||
FLAC__bool FLACParser::eof_callback(const FLAC__StreamDecoder * /* decoder */,
|
||||
void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)->eofCallback();
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderWriteStatus FLACParser::write_callback(
|
||||
const FLAC__StreamDecoder * /* decoder */, const FLAC__Frame *frame,
|
||||
const FLAC__int32 *const buffer[], void *client_data) {
|
||||
return reinterpret_cast<FLACParser *>(client_data)
|
||||
->writeCallback(frame, buffer);
|
||||
}
|
||||
|
||||
void FLACParser::metadata_callback(const FLAC__StreamDecoder * /* decoder */,
|
||||
const FLAC__StreamMetadata *metadata,
|
||||
void *client_data) {
|
||||
reinterpret_cast<FLACParser *>(client_data)->metadataCallback(metadata);
|
||||
}
|
||||
|
||||
void FLACParser::error_callback(const FLAC__StreamDecoder * /* decoder */,
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
void *client_data) {
|
||||
reinterpret_cast<FLACParser *>(client_data)->errorCallback(status);
|
||||
}
|
||||
|
||||
// These are the corresponding callbacks with C++ calling conventions
|
||||
|
||||
FLAC__StreamDecoderReadStatus FLACParser::readCallback(FLAC__byte buffer[],
|
||||
size_t *bytes) {
|
||||
size_t requested = *bytes;
|
||||
ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested);
|
||||
if (0 > actual) {
|
||||
*bytes = 0;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
} else if (0 == actual) {
|
||||
*bytes = 0;
|
||||
mEOF = true;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||||
} else {
|
||||
assert(actual <= requested);
|
||||
*bytes = actual;
|
||||
mCurrentPos += actual;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus FLACParser::seekCallback(
|
||||
FLAC__uint64 absolute_byte_offset) {
|
||||
mCurrentPos = absolute_byte_offset;
|
||||
mEOF = false;
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus FLACParser::tellCallback(
|
||||
FLAC__uint64 *absolute_byte_offset) {
|
||||
*absolute_byte_offset = mCurrentPos;
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback(
|
||||
FLAC__uint64 *stream_length) {
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
FLAC__bool FLACParser::eofCallback() { return mEOF; }
|
||||
|
||||
FLAC__StreamDecoderWriteStatus FLACParser::writeCallback(
|
||||
const FLAC__Frame *frame, const FLAC__int32 *const buffer[]) {
|
||||
if (mWriteRequested) {
|
||||
mWriteRequested = false;
|
||||
// FLAC parser doesn't free or realloc buffer until next frame or finish
|
||||
mWriteHeader = frame->header;
|
||||
mWriteBuffer = buffer;
|
||||
mWriteCompleted = true;
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
} else {
|
||||
ALOGE("FLACParser::writeCallback unexpected");
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) {
|
||||
switch (metadata->type) {
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
if (!mStreamInfoValid) {
|
||||
mStreamInfo = metadata->data.stream_info;
|
||||
mStreamInfoValid = true;
|
||||
} else {
|
||||
ALOGE("FLACParser::metadataCallback unexpected STREAMINFO");
|
||||
}
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_SEEKTABLE:
|
||||
mSeekTable = &metadata->data.seek_table;
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
if (!mVorbisCommentsValid) {
|
||||
FLAC__StreamMetadata_VorbisComment vorbisComment =
|
||||
metadata->data.vorbis_comment;
|
||||
for (FLAC__uint32 i = 0; i < vorbisComment.num_comments; ++i) {
|
||||
FLAC__StreamMetadata_VorbisComment_Entry vorbisCommentEntry =
|
||||
vorbisComment.comments[i];
|
||||
if (vorbisCommentEntry.entry != NULL) {
|
||||
std::string comment(
|
||||
reinterpret_cast<char *>(vorbisCommentEntry.entry),
|
||||
vorbisCommentEntry.length);
|
||||
mVorbisComments.push_back(comment);
|
||||
}
|
||||
}
|
||||
mVorbisCommentsValid = true;
|
||||
} else {
|
||||
ALOGE("FLACParser::metadataCallback unexpected VORBISCOMMENT");
|
||||
}
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_PICTURE: {
|
||||
const FLAC__StreamMetadata_Picture *parsedPicture =
|
||||
&metadata->data.picture;
|
||||
FlacPicture picture;
|
||||
picture.mimeType.assign(std::string(parsedPicture->mime_type));
|
||||
picture.description.assign(
|
||||
std::string((char *)parsedPicture->description));
|
||||
picture.data.assign(parsedPicture->data,
|
||||
parsedPicture->data + parsedPicture->data_length);
|
||||
picture.width = parsedPicture->width;
|
||||
picture.height = parsedPicture->height;
|
||||
picture.depth = parsedPicture->depth;
|
||||
picture.colors = parsedPicture->colors;
|
||||
picture.type = parsedPicture->type;
|
||||
mPictures.push_back(picture);
|
||||
mPicturesValid = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ALOGE("FLACParser::metadataCallback unexpected type %u", metadata->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) {
|
||||
ALOGE("FLACParser::errorCallback status=%d", status);
|
||||
mErrorStatus = status;
|
||||
}
|
||||
|
||||
// Copy samples from FLAC native 32-bit non-interleaved to
|
||||
// correct bit-depth (non-zero padded), interleaved.
|
||||
// These are candidates for optimization if needed.
|
||||
static void copyToByteArrayBigEndian(int8_t *dst, const int *const *src,
|
||||
unsigned bytesPerSample, unsigned nSamples,
|
||||
unsigned nChannels) {
|
||||
for (unsigned i = 0; i < nSamples; ++i) {
|
||||
for (unsigned c = 0; c < nChannels; ++c) {
|
||||
// point to the first byte of the source address
|
||||
// and then skip the first few bytes (most significant bytes)
|
||||
// depending on the bit depth
|
||||
const int8_t *byteSrc =
|
||||
reinterpret_cast<const int8_t *>(&src[c][i]) + 4 - bytesPerSample;
|
||||
memcpy(dst, byteSrc, bytesPerSample);
|
||||
dst = dst + bytesPerSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copyToByteArrayLittleEndian(int8_t *dst, const int *const *src,
|
||||
unsigned bytesPerSample,
|
||||
unsigned nSamples, unsigned nChannels) {
|
||||
for (unsigned i = 0; i < nSamples; ++i) {
|
||||
for (unsigned c = 0; c < nChannels; ++c) {
|
||||
// with little endian, the most significant bytes will be at the end
|
||||
// copy the bytes in little endian will remove the most significant byte
|
||||
// so we are good here.
|
||||
memcpy(dst, &(src[c][i]), bytesPerSample);
|
||||
dst = dst + bytesPerSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copyTrespass(int8_t * /* dst */, const int *const * /* src */,
|
||||
unsigned /* bytesPerSample */, unsigned /* nSamples */,
|
||||
unsigned /* nChannels */) {
|
||||
TRESPASS();
|
||||
}
|
||||
|
||||
// FLACParser
|
||||
|
||||
FLACParser::FLACParser(DataSource *source)
|
||||
: mDataSource(source),
|
||||
mCopy(copyTrespass),
|
||||
mDecoder(NULL),
|
||||
mCurrentPos(0LL),
|
||||
mEOF(false),
|
||||
mStreamInfoValid(false),
|
||||
mSeekTable(NULL),
|
||||
firstFrameOffset(0LL),
|
||||
mVorbisCommentsValid(false),
|
||||
mPicturesValid(false),
|
||||
mWriteRequested(false),
|
||||
mWriteCompleted(false),
|
||||
mWriteBuffer(NULL),
|
||||
mErrorStatus((FLAC__StreamDecoderErrorStatus)-1) {
|
||||
ALOGV("FLACParser::FLACParser");
|
||||
memset(&mStreamInfo, 0, sizeof(mStreamInfo));
|
||||
memset(&mWriteHeader, 0, sizeof(mWriteHeader));
|
||||
}
|
||||
|
||||
FLACParser::~FLACParser() {
|
||||
ALOGV("FLACParser::~FLACParser");
|
||||
if (mDecoder != NULL) {
|
||||
FLAC__stream_decoder_delete(mDecoder);
|
||||
mDecoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool FLACParser::init() {
|
||||
// setup libFLAC parser
|
||||
mDecoder = FLAC__stream_decoder_new();
|
||||
if (mDecoder == NULL) {
|
||||
// The new should succeed, since probably all it does is a malloc
|
||||
// that always succeeds in Android. But to avoid dependence on the
|
||||
// libFLAC internals, we check and log here.
|
||||
ALOGE("new failed");
|
||||
return false;
|
||||
}
|
||||
FLAC__stream_decoder_set_md5_checking(mDecoder, false);
|
||||
FLAC__stream_decoder_set_metadata_ignore_all(mDecoder);
|
||||
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||
FLAC__METADATA_TYPE_STREAMINFO);
|
||||
FLAC__stream_decoder_set_metadata_respond(mDecoder,
|
||||
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,
|
||||
eof_callback, write_callback, metadata_callback, error_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
|
||||
// A failure here probably indicates a programming error and so is
|
||||
// unlikely to happen. But we check and log here similarly to above.
|
||||
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("metadata decoding failed");
|
||||
return false;
|
||||
}
|
||||
// store first frame offset
|
||||
FLAC__stream_decoder_get_decode_position(mDecoder, &firstFrameOffset);
|
||||
|
||||
if (mStreamInfoValid) {
|
||||
// check channel count
|
||||
if (getChannels() == 0 || getChannels() > 8) {
|
||||
ALOGE("unsupported channel count %u", getChannels());
|
||||
return false;
|
||||
}
|
||||
// check bit depth
|
||||
switch (getBitsPerSample()) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
ALOGE("unsupported bits per sample %u", getBitsPerSample());
|
||||
return false;
|
||||
}
|
||||
// configure the appropriate copy function based on device endianness.
|
||||
if (isBigEndian()) {
|
||||
mCopy = copyToByteArrayBigEndian;
|
||||
} else {
|
||||
mCopy = copyToByteArrayLittleEndian;
|
||||
}
|
||||
} else {
|
||||
ALOGE("missing STREAMINFO");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t FLACParser::readBuffer(void *output, size_t output_size) {
|
||||
mWriteRequested = true;
|
||||
mWriteCompleted = false;
|
||||
|
||||
if (!FLAC__stream_decoder_process_single(mDecoder)) {
|
||||
ALOGE("FLACParser::readBuffer process_single failed. Status: %s",
|
||||
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",
|
||||
getDecoderStateString());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// verify that block header keeps the promises made by STREAMINFO
|
||||
unsigned blocksize = mWriteHeader.blocksize;
|
||||
if (blocksize == 0 || blocksize > getMaxBlockSize()) {
|
||||
ALOGE("FLACParser::readBuffer write invalid blocksize %u", blocksize);
|
||||
return -1;
|
||||
}
|
||||
if (mWriteHeader.sample_rate != getSampleRate() ||
|
||||
mWriteHeader.channels != getChannels() ||
|
||||
mWriteHeader.bits_per_sample != getBitsPerSample()) {
|
||||
ALOGE(
|
||||
"FLACParser::readBuffer write changed parameters mid-stream: %d/%d/%d "
|
||||
"-> %d/%d/%d",
|
||||
getSampleRate(), getChannels(), getBitsPerSample(),
|
||||
mWriteHeader.sample_rate, mWriteHeader.channels,
|
||||
mWriteHeader.bits_per_sample);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned bytesPerSample = getBitsPerSample() >> 3;
|
||||
size_t bufferSize = blocksize * getChannels() * bytesPerSample;
|
||||
if (bufferSize > output_size) {
|
||||
ALOGE(
|
||||
"FLACParser::readBuffer not enough space in output buffer "
|
||||
"%zu < %zu",
|
||||
output_size, bufferSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// copy PCM from FLAC write buffer to our media buffer, with interleaving.
|
||||
(*mCopy)(reinterpret_cast<int8_t *>(output), mWriteBuffer, bytesPerSample,
|
||||
blocksize, getChannels());
|
||||
|
||||
// fill in buffer metadata
|
||||
CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
|
||||
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
bool FLACParser::getSeekPositions(int64_t timeUs,
|
||||
std::array<int64_t, 4> &result) {
|
||||
if (!mSeekTable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned sampleRate = getSampleRate();
|
||||
int64_t totalSamples = getTotalSamples();
|
||||
int64_t targetSampleNumber = (timeUs * sampleRate) / 1000000LL;
|
||||
if (targetSampleNumber >= totalSamples) {
|
||||
targetSampleNumber = totalSamples - 1;
|
||||
}
|
||||
|
||||
FLAC__StreamMetadata_SeekPoint *points = mSeekTable->points;
|
||||
unsigned length = mSeekTable->num_points;
|
||||
|
||||
for (unsigned i = length; i != 0; i--) {
|
||||
int64_t sampleNumber = points[i - 1].sample_number;
|
||||
if (sampleNumber == -1) { // placeholder
|
||||
continue;
|
||||
}
|
||||
if (sampleNumber <= targetSampleNumber) {
|
||||
result[0] = (sampleNumber * 1000000LL) / sampleRate;
|
||||
result[1] = firstFrameOffset + points[i - 1].stream_offset;
|
||||
if (sampleNumber == targetSampleNumber || i >= length ||
|
||||
points[i].sample_number == -1) { // placeholder
|
||||
// exact seek, or no following non-placeholder seek point
|
||||
result[2] = result[0];
|
||||
result[3] = result[1];
|
||||
} else {
|
||||
result[2] = (points[i].sample_number * 1000000LL) / sampleRate;
|
||||
result[3] = firstFrameOffset + points[i].stream_offset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
result[0] = 0;
|
||||
result[1] = firstFrameOffset;
|
||||
result[2] = 0;
|
||||
result[3] = firstFrameOffset;
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue