[audio][eac3] Support EAC3 with small frames
EAC3 can have 1,2,3 or 6 audio blocks per sync frame.
The old code only supported 6 audio blocks per sync frame.
So streams with fewer blocks were not aligned correctly
in the SPDIF data burst. See IEC61937-3 spec P5.3.3.
The code now tracks how many blocks have been accumulated
for each possible substream.
Bug: 18315783
Change-Id: I75dbedeb69a597a877b2b68c6be23c831ae119b6
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/audio_utils/include/audio_utils/spdif/FrameScanner.h b/audio_utils/include/audio_utils/spdif/FrameScanner.h
index f1f2cd1..ff245e4 100644
--- a/audio_utils/include/audio_utils/spdif/FrameScanner.h
+++ b/audio_utils/include/audio_utils/spdif/FrameScanner.h
@@ -74,13 +74,10 @@
virtual int getDataType() const { return mDataType; }
virtual int getDataTypeInfo() const { return mDataTypeInfo; }
- /**
- * lengthCode is defined by the SPDIF standard
- * @return length of the frame in bits or bytes, depending on the format.
- */
- virtual int getLengthCode() const = 0;
virtual int getMaxChannels() const = 0;
+ virtual void resetBurst() = 0;
+
/**
* @return the number of pcm frames that correspond to one encoded frame
*/
@@ -113,6 +110,7 @@
#define EAC3_RATE_MULTIPLIER 4
#define EAC3_NUM_SAMPLE_RATE_TABLE_ENTRIES 3
#define EAC3_NUM_BLOCKS_PER_FRAME_TABLE_ENTRIES 38
+#define EAC3_MAX_SUBSTREAMS 8
class AC3FrameScanner : public FrameScanner
{
@@ -127,7 +125,6 @@
virtual int getDataType() const { return mDataType; }
virtual int getDataTypeInfo() const { return 0; }
- virtual int getLengthCode() const { return mLengthCode; }
virtual int getMaxChannels() const { return 5 + 1; }
virtual int getMaxSampleFramesPerSyncFrame() const { return EAC3_RATE_MULTIPLIER
@@ -136,6 +133,7 @@
virtual bool isFirstInBurst();
virtual bool isLastInBurst();
+ virtual void resetBurst();
protected:
@@ -152,7 +150,7 @@
State mState;
uint32_t mBytesSkipped;
uint8_t mHeaderBuffer[6];
- int mLengthCode;
+ uint8_t mSubstreamBlockCounts[EAC3_MAX_SUBSTREAMS];
int mAudioBlocksPerSyncFrame;
uint mCursor;
uint mStreamType;
diff --git a/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h b/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
index 24b6074..b880a93 100644
--- a/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
+++ b/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
@@ -37,14 +37,22 @@
SPDIFEncoder();
virtual ~SPDIFEncoder();
+ // Write encoded data to be wrapped for SPDIF.
+ // The compressed frames do not have to be aligned.
ssize_t write( const void* buffer, size_t numBytes );
// Called by SPDIFEncoder when it is ready to output a data burst.
// Must be implemented by caller.
virtual ssize_t writeOutput( const void* buffer, size_t numBytes ) = 0;
+ // Get ration of the encoded data burst sample rate to the encoded rate.
+ // For example, EAC3 data bursts are 4X the encoded rate.
uint32_t getRateMultiplier() const { return mRateMultiplier; }
+
+ // Return the number of PCM frames in a data burst.
uint32_t getBurstFrames() const { return mBurstFrames; }
+
+ // Return number of bytes per PCM frame for the data burst.
int getBytesPerOutputFrame();
protected:
@@ -54,7 +62,8 @@
void writeBurstBufferBytes(const uint8_t* buffer, size_t numBytes);
void sendZeroPad();
void flushBurstBuffer();
- size_t startDataBurst();
+ void startDataBurst();
+ size_t startSyncFrame();
// State machine states.
enum State {
diff --git a/audio_utils/spdif/FrameScanner.cpp b/audio_utils/spdif/FrameScanner.cpp
index f12e538..5a6e21f 100644
--- a/audio_utils/spdif/FrameScanner.cpp
+++ b/audio_utils/spdif/FrameScanner.cpp
@@ -15,7 +15,7 @@
** limitations under the License.
*/
-#define LOG_TAG "AudioHardwareTungsten"
+#define LOG_TAG "AudioSPDIF"
#include <utils/Log.h>
#include <audio_utils/spdif/FrameScanner.h>
@@ -104,13 +104,13 @@
: FrameScanner(SPDIF_DATA_TYPE_AC3)
, mState(STATE_EXPECTING_SYNC_1)
, mBytesSkipped(0)
- , mLengthCode(0)
, mAudioBlocksPerSyncFrame(6)
, mCursor(AC3_SYNCWORD_SIZE) // past sync word
, mStreamType(0)
, mSubstreamID(0)
, mFormatDumpCount(0)
{
+ memset(mSubstreamBlockCounts, 0, sizeof(mSubstreamBlockCounts));
// Define beginning of syncinfo for getSyncAddress()
mHeaderBuffer[0] = kAC3SyncByte1;
mHeaderBuffer[1] = kAC3SyncByte2;
@@ -125,14 +125,36 @@
return mRateMultiplier * AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK * AC3_PCM_FRAMES_PER_BLOCK;
}
+void AC3FrameScanner::resetBurst()
+{
+ for (int i = 0; i < EAC3_MAX_SUBSTREAMS; i++) {
+ if (mSubstreamBlockCounts[i] >= AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK) {
+ mSubstreamBlockCounts[i] -= AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK;
+ } else if (mSubstreamBlockCounts[i] > 0) {
+ ALOGW("EAC3 substream[%d] has only %d audio blocks!",
+ i, mSubstreamBlockCounts[i]);
+ mSubstreamBlockCounts[i] = 0;
+ }
+ }
+}
+
// per IEC 61973-3 Paragraph 5.3.3
+// We have to send 6 audio blocks on all active substreams.
+// Substream zero must be the first.
+// We don't know if we have all the blocks we need until we see
+// the 7th block of substream#0.
bool AC3FrameScanner::isFirstInBurst()
{
if (mDataType == SPDIF_DATA_TYPE_E_AC3) {
- return (((mStreamType == 0) || (mStreamType == 2)) && (mSubstreamID == 0));
- } else {
- return false; // For AC3 just flush at end.
+ if (((mStreamType == 0) || (mStreamType == 2))
+ && (mSubstreamID == 0)
+ // The ">" is intentional. We have to see the beginning of the block
+ // in the next burst before we can send the current burst.
+ && (mSubstreamBlockCounts[0] > AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK)) {
+ return true;
+ }
}
+ return false;
}
bool AC3FrameScanner::isLastInBurst()
@@ -165,14 +187,9 @@
// The names fscod, frmsiz are from the AC3 spec.
int fscod = mHeaderBuffer[4] >> 6;
if (mDataType == SPDIF_DATA_TYPE_E_AC3) {
- mStreamType = mHeaderBuffer[2] >> 6;
+ mStreamType = mHeaderBuffer[2] >> 6; // strmtyp in spec
mSubstreamID = (mHeaderBuffer[2] >> 3) & 0x07;
- // Print enough so we can see all the substreams.
- ALOGD_IF((mFormatDumpCount < 3*8 ),
- "EAC3 strmtyp = %d, substreamid = %d",
- mStreamType, mSubstreamID);
-
// Frame size is explicit in EAC3. Paragraph E2.3.1.3
int frmsiz = ((mHeaderBuffer[2] & 0x07) << 8) + mHeaderBuffer[3];
mFrameSizeBytes = (frmsiz + 1) * sizeof(int16_t);
@@ -191,8 +208,17 @@
numblkscod = (mHeaderBuffer[4] >> 4) & 0x03;
}
mRateMultiplier = EAC3_RATE_MULTIPLIER; // per IEC 61973-3 Paragraph 5.3.3
- // TODO Don't send data burst until we have 6 blocks per substream.
+ // Don't send data burst until we have 6 blocks per substream.
mAudioBlocksPerSyncFrame = kEAC3BlocksPerFrameTable[numblkscod];
+ // Keep track of how many audio blocks we have for each substream.
+ // This should be safe because mSubstreamID is ANDed with 0x07 above.
+ // And the array is allocated as [8].
+ mSubstreamBlockCounts[mSubstreamID] += mAudioBlocksPerSyncFrame;
+
+ // Print enough so we can see all the substreams.
+ ALOGD_IF((mFormatDumpCount < 3*8 ),
+ "EAC3 mStreamType = %d, mSubstreamID = %d",
+ mStreamType, mSubstreamID);
} else { // regular AC3
// Extract sample rate and frame size from codes.
unsigned int frmsizcod = mHeaderBuffer[4] & 0x3F; // frame size code
@@ -211,7 +237,6 @@
}
mAudioBlocksPerSyncFrame = 6;
}
- mLengthCode = 8 * mFrameSizeBytes; // size in bits
ALOGI_IF((mFormatDumpCount == 0),
"AC3 frame rate = %d * %d, size = %d, audioBlocksPerSyncFrame = %d\n",
mSampleRate, mRateMultiplier, mFrameSizeBytes, mAudioBlocksPerSyncFrame);
diff --git a/audio_utils/spdif/SPDIFEncoder.cpp b/audio_utils/spdif/SPDIFEncoder.cpp
index 07a1800..df39f14 100644
--- a/audio_utils/spdif/SPDIFEncoder.cpp
+++ b/audio_utils/spdif/SPDIFEncoder.cpp
@@ -18,7 +18,7 @@
#include <stdint.h>
-#define LOG_TAG "AudioHardwareTungsten"
+#define LOG_TAG "AudioSPDIF"
#include <utils/Log.h>
#include <audio_utils/spdif/SPDIFEncoder.h>
@@ -48,8 +48,9 @@
mBurstBufferSizeBytes = sizeof(uint16_t)
* SPDIF_ENCODED_CHANNEL_COUNT
* mFramer->getMaxSampleFramesPerSyncFrame();
+
ALOGI("SPDIFEncoder: mBurstBufferSizeBytes = %d, littleEndian = %d",
- mBurstBufferSizeBytes, isLittleEndian());
+ mBurstBufferSizeBytes, isLittleEndian());
mBurstBuffer = new uint16_t[mBurstBufferSizeBytes >> 1];
clearBurstBuffer();
}
@@ -116,22 +117,29 @@
size_t burstSize = mFramer->getSampleFramesPerSyncFrame() * sizeof(uint16_t)
* SPDIF_ENCODED_CHANNEL_COUNT;
if (mByteCursor > burstSize) {
- ALOGE("SPDIFEncoder: Burst buffer, contents too large!\n");
+ ALOGE("SPDIFEncoder: Burst buffer, contents too large!");
clearBurstBuffer();
} else {
// We don't have to write zeros because buffer already set to zero
- // by clearBurstBuffer().
+ // by clearBurstBuffer(). Just pretend we wrote zeros by
+ // incrementing cursor.
mByteCursor = burstSize;
}
}
void SPDIFEncoder::flushBurstBuffer()
{
- if (mByteCursor > 0) {
+ const int preambleSize = 4 * sizeof(uint16_t);
+ if (mByteCursor > preambleSize) {
+ // Set number of bits for valid payload before zeroPad.
+ uint16_t lengthCode = (mByteCursor - preambleSize) * 8;
+ mBurstBuffer[3] = lengthCode;
+
sendZeroPad();
writeOutput(mBurstBuffer, mByteCursor);
- clearBurstBuffer();
}
+ clearBurstBuffer();
+ mFramer->resetBurst();
}
void SPDIFEncoder::clearBurstBuffer()
@@ -142,7 +150,7 @@
mByteCursor = 0;
}
-size_t SPDIFEncoder::startDataBurst()
+void SPDIFEncoder::startDataBurst()
{
// Encode IEC61937-1 Burst Preamble
uint16_t preamble[4];
@@ -156,14 +164,15 @@
preamble[0] = kSPDIFSync1;
preamble[1] = kSPDIFSync2;
preamble[2] = burstInfo;
- preamble[3] = mFramer->getLengthCode();
+ preamble[3] = 0; // lengthCode - This will get set after the buffer is full.
writeBurstBufferShorts(preamble, 4);
+}
+size_t SPDIFEncoder::startSyncFrame()
+{
// Write start of encoded frame that was buffered in frame detector.
size_t syncSize = mFramer->getHeaderSizeBytes();
writeBurstBufferBytes(mFramer->getHeaderAddress(), syncSize);
- ALOGV("SPDIFEncoder: startDataBurst, syncSize = %u, lengthCode = %d",
- syncSize, mFramer->getLengthCode());
return mFramer->getFrameSizeBytes() - syncSize;
}
@@ -177,14 +186,17 @@
while (bytesLeft > 0) {
State nextState = mState;
switch (mState) {
- // Look for beginning of encoded frame.
+ // Look for beginning of next encoded frame.
case STATE_IDLE:
if (mFramer->scan(*data)) {
- if (mFramer->isFirstInBurst()) {
+ if (mByteCursor == 0) {
+ startDataBurst();
+ } else if (mFramer->isFirstInBurst()) {
// Make sure that this frame is at the beginning of the data burst.
flushBurstBuffer();
+ startDataBurst();
}
- mPayloadBytesPending = startDataBurst();
+ mPayloadBytesPending = startSyncFrame();
nextState = STATE_BURST;
}
data++;
@@ -208,7 +220,6 @@
// If we have all the payload then send a data burst.
if (mPayloadBytesPending == 0) {
if (mFramer->isLastInBurst()) {
- // Flush now if we know the burst is ready.
flushBurstBuffer();
}
nextState = STATE_IDLE;