MKV handles MP3 audio whose frame is not complete

MP3 frames of some special mkv files are not complete, so the
frames read by MatroskaSource can't be played normally.
MP3 frameSize for these special files is bigger than the buffer
length of the frame read by readBlock. Read function can't get
a complete MP3 frame which will be sent to MP3 decoder.

Bug: 123590715
Test: Test MKV file whose MP3 audio frameSize in MP3 header is
not consistent with buffer length and check if MP3 audio can
be played normally.

Change-Id: If1deca29fffc50012edb3af63d96ec9e99aa3fba
gugelfrei
Iris Chang 6 years ago committed by Robert Shih
parent e9955729fa
commit d9b70d5309

@ -32,6 +32,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaDataUtils.h>
#include <media/stagefright/foundation/avc_utils.h>
#include <utils/String8.h>
#include <arpa/inet.h>
@ -147,6 +148,7 @@ private:
AVC,
AAC,
HEVC,
MP3,
OTHER
};
@ -159,6 +161,15 @@ private:
List<MediaBufferHelper *> mPendingFrames;
int64_t mCurrentTS; // add for mp3
uint32_t mMP3Header;
media_status_t findMP3Header(uint32_t * header,
const uint8_t *dataSource, int length, int *outStartPos);
media_status_t mp3FrameRead(
MediaBufferHelper **out, const ReadOptions *options,
int64_t targetSampleTimeUs);
status_t advance();
status_t setWebmBlockCryptoInfo(MediaBufferHelper *mbuf);
@ -225,7 +236,9 @@ MatroskaSource::MatroskaSource(
mBlockIter(mExtractor,
mExtractor->mTracks.itemAt(index).mTrackNum,
index),
mNALSizeLen(-1) {
mNALSizeLen(-1),
mCurrentTS(0),
mMP3Header(0) {
MatroskaExtractor::TrackInfo &trackInfo = mExtractor->mTracks.editItemAt(index);
AMediaFormat *meta = trackInfo.mMeta;
@ -254,6 +267,8 @@ MatroskaSource::MatroskaSource(
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
mType = AAC;
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
mType = MP3;
}
}
@ -270,6 +285,16 @@ media_status_t MatroskaSource::start() {
mBufferGroup->init(1 /* number of buffers */, 1024 /* buffer size */, 64 /* growth limit */);
mBlockIter.reset();
if (mType == MP3 && mMP3Header == 0) {
int start = -1;
media_status_t err = findMP3Header(&mMP3Header, NULL, 0, &start);
if (err != OK) {
ALOGE("No mp3 header found");
clearPendingFrames();
return err;
}
}
return AMEDIA_OK;
}
@ -796,6 +821,188 @@ media_status_t MatroskaSource::readBlock() {
return AMEDIA_OK;
}
//the value of kMP3HeaderMask is from MP3Extractor
static const uint32_t kMP3HeaderMask = 0xfffe0c00;
media_status_t MatroskaSource::findMP3Header(uint32_t * header,
const uint8_t *dataSource, int length, int *outStartPos) {
if (NULL == header) {
ALOGE("header is null!");
return AMEDIA_ERROR_END_OF_STREAM;
}
//to find header start position
if (0 != *header) {
if (NULL == dataSource) {
*outStartPos = -1;
return AMEDIA_OK;
}
uint32_t tmpCode = 0;
for (int i = 0; i < length; i++) {
tmpCode = (tmpCode << 8) + dataSource[i];
if ((tmpCode & kMP3HeaderMask) == (*header & kMP3HeaderMask)) {
*outStartPos = i - 3;
return AMEDIA_OK;
}
}
*outStartPos = -1;
return AMEDIA_OK;
}
//to find mp3 header
uint32_t code = 0;
while (0 == *header) {
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
clearPendingFrames();
return err;
}
}
MediaBufferHelper *frame = *mPendingFrames.begin();
size_t size = frame->range_length();
size_t offset = frame->range_offset();
size_t i;
size_t frame_size;
for (i = 0; i < size; i++) {
ALOGV("data[%zu]=%x", i, *((uint8_t*)frame->data() + offset + i));
code = (code << 8) + *((uint8_t*)frame->data() + offset + i);
if (GetMPEGAudioFrameSize(code, &frame_size, NULL, NULL, NULL)) {
*header = code;
mBlockIter.reset();
clearPendingFrames();
return AMEDIA_OK;
}
}
}
return AMEDIA_ERROR_END_OF_STREAM;
}
media_status_t MatroskaSource::mp3FrameRead(
MediaBufferHelper **out, const ReadOptions *options,
int64_t targetSampleTimeUs) {
MediaBufferHelper *frame = *mPendingFrames.begin();
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
CHECK(AMediaFormat_getInt64(frame->meta_data(),
AMEDIAFORMAT_KEY_TIME_US, &mCurrentTS));
if (mCurrentTS < 0) {
mCurrentTS = 0;
AMediaFormat_setInt64(frame->meta_data(),
AMEDIAFORMAT_KEY_TIME_US, mCurrentTS);
}
}
int32_t start = -1;
while (start < 0) {
//find header start position
findMP3Header(&mMP3Header,
(const uint8_t*)frame->data() + frame->range_offset(),
frame->range_length(), &start);
ALOGV("start=%d, frame->range_length() = %zu, frame->range_offset() =%zu",
start, frame->range_length(), frame->range_offset());
if (start >= 0)
break;
frame->release();
mPendingFrames.erase(mPendingFrames.begin());
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
clearPendingFrames();
return err;
}
}
frame = *mPendingFrames.begin();
}
frame->set_range(frame->range_offset() + start, frame->range_length() - start);
uint32_t header = *(uint32_t*)((uint8_t*)frame->data() + frame->range_offset());
header = ((header >> 24) & 0xff) | ((header >> 8) & 0xff00) |
((header << 8) & 0xff0000) | ((header << 24) & 0xff000000);
size_t frame_size;
int out_sampling_rate;
int out_channels;
int out_bitrate;
if (!GetMPEGAudioFrameSize(header, &frame_size,
&out_sampling_rate, &out_channels, &out_bitrate)) {
ALOGE("MP3 Header read fail!!");
return AMEDIA_ERROR_UNSUPPORTED;
}
MediaBufferHelper *buffer;
mBufferGroup->acquire_buffer(&buffer, false /* nonblocking */, frame_size /* requested size */);
buffer->set_range(0, frame_size);
uint8_t *data = static_cast<uint8_t *>(buffer->data());
ALOGV("MP3 frame %zu frame->range_length() %zu", frame_size, frame->range_length());
if (frame_size > frame->range_length()) {
memcpy(data, (uint8_t*)(frame->data()) + frame->range_offset(), frame->range_length());
size_t sumSize = 0;
sumSize += frame->range_length();
size_t needSize = frame_size - frame->range_length();
frame->release();
mPendingFrames.erase(mPendingFrames.begin());
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
clearPendingFrames();
return err;
}
}
frame = *mPendingFrames.begin();
size_t offset = frame->range_offset();
size_t size = frame->range_length();
// the next buffer frame is not enough to fullfill mp3 frame,
// we have to read until mp3 frame is completed.
while (size < needSize) {
memcpy(data + sumSize, (uint8_t*)(frame->data()) + offset, size);
needSize -= size;
sumSize += size;
frame->release();
mPendingFrames.erase(mPendingFrames.begin());
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
clearPendingFrames();
return err;
}
}
frame = *mPendingFrames.begin();
offset = frame->range_offset();
size = frame->range_length();
}
memcpy(data + sumSize, (uint8_t*)(frame->data()) + offset, needSize);
frame->set_range(offset + needSize, size - needSize);
} else {
size_t offset = frame->range_offset();
size_t size = frame->range_length();
memcpy(data, (uint8_t*)(frame->data()) + offset, frame_size);
frame->set_range(offset + frame_size, size - frame_size);
}
if (frame->range_length() < 4) {
frame->release();
frame = NULL;
mPendingFrames.erase(mPendingFrames.begin());
}
ALOGV("MatroskaSource::read MP3 frame kKeyTime=%lld,kKeyTargetTime=%lld",
(long long)mCurrentTS, (long long)targetSampleTimeUs);
AMediaFormat_setInt64(buffer->meta_data(),
AMEDIAFORMAT_KEY_TIME_US, mCurrentTS);
mCurrentTS += (int64_t)frame_size * 8000ll / out_bitrate;
if (targetSampleTimeUs >= 0ll)
AMediaFormat_setInt64(buffer->meta_data(),
AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs);
*out = buffer;
ALOGV("MatroskaSource::read MP3, keyTime=%lld for next frame", (long long)mCurrentTS);
return AMEDIA_OK;
}
media_status_t MatroskaSource::read(
MediaBufferHelper **out, const ReadOptions *options) {
*out = NULL;
@ -833,6 +1040,10 @@ media_status_t MatroskaSource::read(
}
}
if (mType == MP3) {
return mp3FrameRead(out, options, targetSampleTimeUs);
}
MediaBufferHelper *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());

Loading…
Cancel
Save