Skip to content

Commit 66331b9

Browse files
marconeAndroid (Google) Code Review
authored andcommitted
Merge "Support gapless playback for mp3 and m4a"
2 parents af0c843 + da9deca commit 66331b9

File tree

7 files changed

+252
-6
lines changed

7 files changed

+252
-6
lines changed

include/media/stagefright/OMXCodec.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct MediaCodecList;
3030
class MemoryDealer;
3131
struct OMXCodecObserver;
3232
struct CodecProfileLevel;
33+
class SkipCutBuffer;
3334

3435
struct OMXCodec : public MediaSource,
3536
public MediaBufferObserver {
@@ -201,6 +202,7 @@ struct OMXCodec : public MediaSource,
201202
ReadOptions::SeekMode mSeekMode;
202203
int64_t mTargetTimeUs;
203204
bool mOutputPortSettingsChangedPending;
205+
SkipCutBuffer *mSkipCutBuffer;
204206

205207
MediaBuffer *mLeftOverBuffer;
206208

@@ -378,6 +380,7 @@ status_t QueryCodecs(
378380
const char *mimeType, bool queryDecoders,
379381
Vector<CodecCapabilities> *results);
380382

383+
381384
} // namespace android
382385

383386
#endif // OMX_CODEC_H_
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef SKIP_CUT_BUFFER_H_
18+
19+
#define SKIP_CUT_BUFFER_H_
20+
21+
#include <media/stagefright/MediaBuffer.h>
22+
23+
namespace android {
24+
25+
/**
26+
* utility class to cut the start and end off a stream of data in MediaBuffers
27+
*
28+
*/
29+
class SkipCutBuffer {
30+
public:
31+
// 'skip' is the number of bytes to skip from the beginning
32+
// 'cut' is the number of bytes to cut from the end
33+
// 'output_size' is the size in bytes of the MediaBuffers that will be used
34+
SkipCutBuffer(int32_t skip, int32_t cut, int32_t output_size);
35+
virtual ~SkipCutBuffer();
36+
37+
// Submit one MediaBuffer for skipping and cutting. This may consume all or
38+
// some of the data in the buffer, or it may add data to it.
39+
// After this, the caller should continue processing the buffer as usual.
40+
void submit(MediaBuffer *buffer);
41+
void clear();
42+
size_t size(); // how many bytes are currently stored in the buffer
43+
44+
private:
45+
void write(const char *src, size_t num);
46+
size_t read(char *dst, size_t num);
47+
int32_t mFrontPadding;
48+
int32_t mBackPadding;
49+
int32_t mWriteHead;
50+
int32_t mReadHead;
51+
int32_t mCapacity;
52+
char* mCutBuffer;
53+
DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer);
54+
};
55+
56+
} // namespace android
57+
58+
#endif // OMX_CODEC_H_

media/libstagefright/Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \
4242
OggExtractor.cpp \
4343
SampleIterator.cpp \
4444
SampleTable.cpp \
45+
SkipCutBuffer.cpp \
4546
StagefrightMediaScanner.cpp \
4647
StagefrightMetadataRetriever.cpp \
4748
SurfaceMediaSource.cpp \

media/libstagefright/OMXCodec.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <media/stagefright/MetaData.h>
3939
#include <media/stagefright/OMXCodec.h>
4040
#include <media/stagefright/Utils.h>
41+
#include <media/stagefright/SkipCutBuffer.h>
4142
#include <utils/Vector.h>
4243

4344
#include <OMX_Audio.h>
@@ -1303,6 +1304,7 @@ OMXCodec::OMXCodec(
13031304
mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC),
13041305
mTargetTimeUs(-1),
13051306
mOutputPortSettingsChangedPending(false),
1307+
mSkipCutBuffer(NULL),
13061308
mLeftOverBuffer(NULL),
13071309
mPaused(false),
13081310
mNativeWindow(
@@ -1413,6 +1415,9 @@ OMXCodec::~OMXCodec() {
14131415

14141416
free(mMIME);
14151417
mMIME = NULL;
1418+
1419+
delete mSkipCutBuffer;
1420+
mSkipCutBuffer = NULL;
14161421
}
14171422

14181423
status_t OMXCodec::init() {
@@ -1573,6 +1578,34 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
15731578
portIndex == kPortIndexInput ? "input" : "output");
15741579
}
15751580

1581+
if (portIndex == kPortIndexOutput) {
1582+
1583+
sp<MetaData> meta = mSource->getFormat();
1584+
int32_t delay = 0;
1585+
if (!meta->findInt32(kKeyEncoderDelay, &delay)) {
1586+
delay = 0;
1587+
}
1588+
int32_t padding = 0;
1589+
if (!meta->findInt32(kKeyEncoderPadding, &padding)) {
1590+
padding = 0;
1591+
}
1592+
int32_t numchannels = 0;
1593+
if (delay + padding) {
1594+
if (meta->findInt32(kKeyChannelCount, &numchannels)) {
1595+
size_t frameSize = numchannels * sizeof(int16_t);
1596+
if (mSkipCutBuffer) {
1597+
size_t prevbuffersize = mSkipCutBuffer->size();
1598+
if (prevbuffersize != 0) {
1599+
ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbuffersize);
1600+
}
1601+
delete mSkipCutBuffer;
1602+
}
1603+
mSkipCutBuffer = new SkipCutBuffer(delay * frameSize, padding * frameSize,
1604+
def.nBufferSize);
1605+
}
1606+
}
1607+
}
1608+
15761609
// dumpPortStatus(portIndex);
15771610

15781611
if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) {
@@ -2490,6 +2523,10 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
24902523
CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
24912524
mPortBuffers[portIndex].size());
24922525

2526+
if (mSkipCutBuffer && mPortStatus[kPortIndexOutput] == ENABLED) {
2527+
mSkipCutBuffer->clear();
2528+
}
2529+
24932530
if (mState == RECONFIGURING) {
24942531
CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
24952532

@@ -3800,6 +3837,9 @@ status_t OMXCodec::read(
38003837
info->mStatus = OWNED_BY_CLIENT;
38013838

38023839
info->mMediaBuffer->add_ref();
3840+
if (mSkipCutBuffer) {
3841+
mSkipCutBuffer->submit(info->mMediaBuffer);
3842+
}
38033843
*buffer = info->mMediaBuffer;
38043844

38053845
return OK;
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//#define LOG_NDEBUG 0
18+
#define LOG_TAG "SkipCutBuffer"
19+
#include <utils/Log.h>
20+
21+
#include <media/stagefright/foundation/ADebug.h>
22+
#include <media/stagefright/MediaBuffer.h>
23+
#include <media/stagefright/SkipCutBuffer.h>
24+
25+
namespace android {
26+
27+
SkipCutBuffer::SkipCutBuffer(int32_t skip, int32_t cut, int32_t output_size) {
28+
mFrontPadding = skip;
29+
mBackPadding = cut;
30+
mWriteHead = 0;
31+
mReadHead = 0;
32+
mCapacity = cut + output_size;
33+
mCutBuffer = new char[mCapacity];
34+
ALOGV("skipcutbuffer %d %d %d", skip, cut, mCapacity);
35+
}
36+
37+
SkipCutBuffer::~SkipCutBuffer() {
38+
delete[] mCutBuffer;
39+
}
40+
41+
void SkipCutBuffer::submit(MediaBuffer *buffer) {
42+
int32_t offset = buffer->range_offset();
43+
int32_t buflen = buffer->range_length();
44+
45+
// drop the initial data from the buffer if needed
46+
if (mFrontPadding > 0) {
47+
// still data left to drop
48+
int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding;
49+
offset += to_drop;
50+
buflen -= to_drop;
51+
buffer->set_range(offset, buflen);
52+
mFrontPadding -= to_drop;
53+
}
54+
55+
56+
// append data to cutbuffer
57+
char *src = ((char*) buffer->data()) + offset;
58+
write(src, buflen);
59+
60+
61+
// the mediabuffer is now empty. Fill it from cutbuffer, always leaving
62+
// at least mBackPadding bytes in the cutbuffer
63+
char *dst = (char*) buffer->data();
64+
size_t copied = read(dst, buffer->size());
65+
buffer->set_range(0, copied);
66+
}
67+
68+
void SkipCutBuffer::clear() {
69+
mWriteHead = mReadHead = 0;
70+
}
71+
72+
void SkipCutBuffer::write(const char *src, size_t num) {
73+
int32_t sizeused = (mWriteHead - mReadHead);
74+
if (sizeused < 0) sizeused += mCapacity;
75+
76+
// everything must fit
77+
CHECK_GE((mCapacity - size_t(sizeused)), num);
78+
79+
size_t copyfirst = (mCapacity - mWriteHead);
80+
if (copyfirst > num) copyfirst = num;
81+
if (copyfirst) {
82+
memcpy(mCutBuffer + mWriteHead, src, copyfirst);
83+
num -= copyfirst;
84+
src += copyfirst;
85+
mWriteHead += copyfirst;
86+
CHECK_LE(mWriteHead, mCapacity);
87+
if (mWriteHead == mCapacity) mWriteHead = 0;
88+
if (num) {
89+
memcpy(mCutBuffer, src, num);
90+
mWriteHead += num;
91+
}
92+
}
93+
}
94+
95+
size_t SkipCutBuffer::read(char *dst, size_t num) {
96+
int32_t available = (mWriteHead - mReadHead);
97+
if (available < 0) available += mCapacity;
98+
99+
available -= mBackPadding;
100+
if (available <=0) {
101+
return 0;
102+
}
103+
if (available < num) {
104+
num = available;
105+
}
106+
107+
size_t copyfirst = (mCapacity - mReadHead);
108+
if (copyfirst > num) copyfirst = num;
109+
if (copyfirst) {
110+
memcpy(dst, mCutBuffer + mReadHead, copyfirst);
111+
num -= copyfirst;
112+
dst += copyfirst;
113+
mReadHead += copyfirst;
114+
CHECK_LE(mReadHead, mCapacity);
115+
if (mReadHead == mCapacity) mReadHead = 0;
116+
if (num) {
117+
memcpy(dst, mCutBuffer, num);
118+
mReadHead += num;
119+
}
120+
}
121+
return available;
122+
}
123+
124+
size_t SkipCutBuffer::size() {
125+
int32_t available = (mWriteHead - mReadHead);
126+
if (available < 0) available += mCapacity;
127+
return available;
128+
}
129+
130+
} // namespace android

media/libstagefright/codecs/mp3dec/SoftMP3.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ void SoftMP3::initDecoder() {
115115
mDecoderBuf = malloc(memRequirements);
116116

117117
pvmp3_InitDecoder(mConfig, mDecoderBuf);
118+
mIsFirst = true;
118119
}
119120

120121
OMX_ERRORTYPE SoftMP3::internalGetParameter(
@@ -190,7 +191,10 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
190191
inInfo->mOwnedByUs = false;
191192
notifyEmptyBufferDone(inHeader);
192193

193-
outHeader->nFilledLen = 0;
194+
// pad the end of the stream with 529 samples, since that many samples
195+
// were trimmed off the beginning when decoding started
196+
outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
197+
memset(outHeader->pBuffer, 0, outHeader->nFilledLen);
194198
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
195199

196200
outQueue.erase(outQueue.begin());
@@ -251,8 +255,17 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
251255
return;
252256
}
253257

254-
outHeader->nOffset = 0;
255-
outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);
258+
if (mIsFirst) {
259+
mIsFirst = false;
260+
// The decoder delay is 529 samples, so trim that many samples off
261+
// the start of the first output buffer. This essentially makes this
262+
// decoder have zero delay, which the rest of the pipeline assumes.
263+
outHeader->nOffset = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
264+
outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset;
265+
} else {
266+
outHeader->nOffset = 0;
267+
outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);
268+
}
256269

257270
outHeader->nTimeStamp =
258271
mAnchorTimeUs
@@ -288,6 +301,7 @@ void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) {
288301
// Make sure that the next buffer output does not still
289302
// depend on fragments from the last one decoded.
290303
pvmp3_InitDecoder(mConfig, mDecoderBuf);
304+
mIsFirst = true;
291305
}
292306
}
293307

media/libstagefright/codecs/mp3dec/SoftMP3.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ struct SoftMP3 : public SimpleSoftOMXComponent {
4646
private:
4747
enum {
4848
kNumBuffers = 4,
49-
kOutputBufferSize = 4608 * 2
49+
kOutputBufferSize = 4608 * 2,
50+
kPVMP3DecoderDelay = 529 // frames
5051
};
5152

5253
tPVMP3DecoderExternal *mConfig;
@@ -57,8 +58,7 @@ struct SoftMP3 : public SimpleSoftOMXComponent {
5758
int32_t mNumChannels;
5859
int32_t mSamplingRate;
5960

60-
bool mConfigured;
61-
61+
bool mIsFirst;
6262
bool mSignalledError;
6363

6464
enum {

0 commit comments

Comments
 (0)