|
|
|
@ -68,6 +68,7 @@ enum {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class MPEG4Source : public MediaTrack {
|
|
|
|
|
static const size_t kMaxPcmFrameSize = 8192;
|
|
|
|
|
public:
|
|
|
|
|
// Caller retains ownership of both "dataSource" and "sampleTable".
|
|
|
|
|
MPEG4Source(MetaDataBase &format,
|
|
|
|
@ -127,7 +128,7 @@ private:
|
|
|
|
|
bool mIsAVC;
|
|
|
|
|
bool mIsHEVC;
|
|
|
|
|
bool mIsAC4;
|
|
|
|
|
|
|
|
|
|
bool mIsPcm;
|
|
|
|
|
size_t mNALLengthSize;
|
|
|
|
|
|
|
|
|
|
bool mStarted;
|
|
|
|
@ -332,6 +333,11 @@ static const char *FourCC2MIME(uint32_t fourcc) {
|
|
|
|
|
return MEDIA_MIMETYPE_VIDEO_HEVC;
|
|
|
|
|
case FOURCC('a', 'c', '-', '4'):
|
|
|
|
|
return MEDIA_MIMETYPE_AUDIO_AC4;
|
|
|
|
|
|
|
|
|
|
case FOURCC('t', 'w', 'o', 's'):
|
|
|
|
|
case FOURCC('s', 'o', 'w', 't'):
|
|
|
|
|
return MEDIA_MIMETYPE_AUDIO_RAW;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ALOGW("Unknown fourcc: %c%c%c%c",
|
|
|
|
|
(fourcc >> 24) & 0xff,
|
|
|
|
@ -1488,6 +1494,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
|
|
|
|
|
case FOURCC('e', 'n', 'c', 'a'):
|
|
|
|
|
case FOURCC('s', 'a', 'm', 'r'):
|
|
|
|
|
case FOURCC('s', 'a', 'w', 'b'):
|
|
|
|
|
case FOURCC('t', 'w', 'o', 's'):
|
|
|
|
|
case FOURCC('s', 'o', 'w', 't'):
|
|
|
|
|
{
|
|
|
|
|
if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')
|
|
|
|
|
&& depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) {
|
|
|
|
@ -1557,6 +1565,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
|
|
|
|
|
// if the chunk type is enca, we'll get the type from the frma box later
|
|
|
|
|
mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
|
|
|
|
|
AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
|
|
|
|
|
|
|
|
|
|
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, FourCC2MIME(chunk_type))) {
|
|
|
|
|
mLastTrack->meta.setInt32(kKeyBitsPerSample, sample_size);
|
|
|
|
|
if (chunk_type == FOURCC('t', 'w', 'o', 's')) {
|
|
|
|
|
mLastTrack->meta.setInt32(kKeyPcmBigEndian, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
|
|
|
|
|
chunk, num_channels, sample_size, sample_rate);
|
|
|
|
@ -4106,6 +4121,7 @@ MPEG4Source::MPEG4Source(
|
|
|
|
|
mIsAVC(false),
|
|
|
|
|
mIsHEVC(false),
|
|
|
|
|
mIsAC4(false),
|
|
|
|
|
mIsPcm(false),
|
|
|
|
|
mNALLengthSize(0),
|
|
|
|
|
mStarted(false),
|
|
|
|
|
mGroup(NULL),
|
|
|
|
@ -4168,6 +4184,27 @@ MPEG4Source::MPEG4Source(
|
|
|
|
|
mNALLengthSize = 1 + (ptr[14 + 7] & 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW);
|
|
|
|
|
|
|
|
|
|
if (mIsPcm) {
|
|
|
|
|
int32_t numChannels = 0;
|
|
|
|
|
int32_t bitsPerSample = 0;
|
|
|
|
|
CHECK(mFormat.findInt32(kKeyBitsPerSample, &bitsPerSample));
|
|
|
|
|
CHECK(mFormat.findInt32(kKeyChannelCount, &numChannels));
|
|
|
|
|
|
|
|
|
|
int32_t bytesPerSample = bitsPerSample >> 3;
|
|
|
|
|
int32_t pcmSampleSize = bytesPerSample * numChannels;
|
|
|
|
|
|
|
|
|
|
size_t maxSampleSize;
|
|
|
|
|
status_t err = mSampleTable->getMaxSampleSize(&maxSampleSize);
|
|
|
|
|
if (err != OK || maxSampleSize != static_cast<size_t>(pcmSampleSize) || bitsPerSample != 16) {
|
|
|
|
|
// Not supported
|
|
|
|
|
mIsPcm = false;
|
|
|
|
|
} else {
|
|
|
|
|
mFormat.setInt32(kKeyMaxInputSize, pcmSampleSize * kMaxPcmFrameSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK(format.findInt32(kKeyTrackID, &mTrackId));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
@ -5082,34 +5119,78 @@ status_t MPEG4Source::read(
|
|
|
|
|
|
|
|
|
|
if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) {
|
|
|
|
|
if (newBuffer) {
|
|
|
|
|
ssize_t num_bytes_read =
|
|
|
|
|
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
|
|
|
|
|
if (mIsPcm) {
|
|
|
|
|
// The twos' PCM block reader assumes that all samples has the same size.
|
|
|
|
|
|
|
|
|
|
if (num_bytes_read < (ssize_t)size) {
|
|
|
|
|
mBuffer->release();
|
|
|
|
|
mBuffer = NULL;
|
|
|
|
|
uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk()
|
|
|
|
|
- mCurrentSampleIndex + 1;
|
|
|
|
|
if (samplesToRead > kMaxPcmFrameSize) {
|
|
|
|
|
samplesToRead = kMaxPcmFrameSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ERROR_IO;
|
|
|
|
|
}
|
|
|
|
|
ALOGV("Reading %d PCM frames of size %zu at index %d to stop of chunk at %d",
|
|
|
|
|
samplesToRead, size, mCurrentSampleIndex,
|
|
|
|
|
mSampleTable->getLastSampleIndexInChunk());
|
|
|
|
|
|
|
|
|
|
CHECK(mBuffer != NULL);
|
|
|
|
|
mBuffer->set_range(0, size);
|
|
|
|
|
mBuffer->meta_data().clear();
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
|
|
|
|
|
size_t totalSize = samplesToRead * size;
|
|
|
|
|
uint8_t* buf = (uint8_t *)mBuffer->data();
|
|
|
|
|
ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize);
|
|
|
|
|
if (bytesRead < (ssize_t)totalSize) {
|
|
|
|
|
mBuffer->release();
|
|
|
|
|
mBuffer = NULL;
|
|
|
|
|
|
|
|
|
|
if (targetSampleTimeUs >= 0) {
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyTargetTime, targetSampleTimeUs);
|
|
|
|
|
}
|
|
|
|
|
return ERROR_IO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isSyncSample) {
|
|
|
|
|
mBuffer->meta_data().clear();
|
|
|
|
|
mBuffer->meta_data().setInt64(kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
|
|
|
|
|
mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++mCurrentSampleIndex;
|
|
|
|
|
int32_t byteOrder;
|
|
|
|
|
mFormat.findInt32(kKeyPcmBigEndian, &byteOrder);
|
|
|
|
|
|
|
|
|
|
if (byteOrder == 1) {
|
|
|
|
|
// Big-endian -> little-endian
|
|
|
|
|
uint16_t *dstData = (uint16_t *)buf;
|
|
|
|
|
uint16_t *srcData = (uint16_t *)buf;
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < bytesRead / sizeof(uint16_t); j++) {
|
|
|
|
|
dstData[j] = ntohs(srcData[j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mCurrentSampleIndex += samplesToRead;
|
|
|
|
|
mBuffer->set_range(0, totalSize);
|
|
|
|
|
} else {
|
|
|
|
|
ssize_t num_bytes_read =
|
|
|
|
|
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
|
|
|
|
|
|
|
|
|
|
if (num_bytes_read < (ssize_t)size) {
|
|
|
|
|
mBuffer->release();
|
|
|
|
|
mBuffer = NULL;
|
|
|
|
|
|
|
|
|
|
return ERROR_IO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK(mBuffer != NULL);
|
|
|
|
|
mBuffer->set_range(0, size);
|
|
|
|
|
mBuffer->meta_data().clear();
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
|
|
|
|
|
|
|
|
|
|
if (targetSampleTimeUs >= 0) {
|
|
|
|
|
mBuffer->meta_data().setInt64(
|
|
|
|
|
kKeyTargetTime, targetSampleTimeUs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isSyncSample) {
|
|
|
|
|
mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++mCurrentSampleIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!mIsAVC && !mIsHEVC && !mIsAC4) {
|
|
|
|
|