SPDIF: add DTS support to SPDIF wrapper

Refactor AC3 out of FrameScanner.cpp.
Simplify scanning state machine and move up into FrameScanner.cpp.

Bug: 18292317
Change-Id: Iaefdce9c960abbea5dcdc2390a5da00181da0f8c
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 ff245e4..7b88ae2 100644
--- a/audio_utils/include/audio_utils/spdif/FrameScanner.h
+++ b/audio_utils/include/audio_utils/spdif/FrameScanner.h
@@ -1,19 +1,18 @@
 /*
-**
-** Copyright 2014, 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.
-*/
+ * Copyright 2014, 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.
+ */
 
 #ifndef ANDROID_AUDIO_FRAME_SCANNER_H
 #define ANDROID_AUDIO_FRAME_SCANNER_H
@@ -33,25 +32,28 @@
  */
 class FrameScanner {
 public:
-
-    FrameScanner(int dataType);
+    FrameScanner(int dataType,
+            const uint8_t *syncBytes,
+            uint32_t syncLength,
+            uint32_t headerLength
+            );
     virtual ~FrameScanner();
 
     /**
      * Pass each byte of the encoded stream to this scanner.
      * @return true if a complete and valid header was detected
      */
-    virtual bool scan(uint8_t) = 0;
+    virtual bool scan(uint8_t byte);
 
     /**
      * @return address of where the sync header was stored by scan()
      */
-    virtual const uint8_t *getHeaderAddress() const = 0;
+    const uint8_t *getHeaderAddress() const { return mHeaderBuffer; }
 
     /**
      * @return number of bytes in sync header stored by scan()
      */
-    virtual size_t getHeaderSizeBytes() const = 0;
+    size_t getHeaderSizeBytes() const { return mHeaderLength; }
 
     /**
      * @return sample rate of the encoded audio
@@ -71,8 +73,8 @@
     /**
      * dataType is defined by the SPDIF standard for each format
      */
-    virtual int getDataType()      const { return mDataType; }
-    virtual int getDataTypeInfo()  const { return mDataTypeInfo; }
+    int getDataType()      const { return mDataType; }
+    int getDataTypeInfo()  const { return mDataTypeInfo; }
 
     virtual int getMaxChannels() const = 0;
 
@@ -96,77 +98,28 @@
     virtual bool isLastInBurst()  = 0;
 
 protected:
-    uint32_t mSampleRate;
-    uint32_t mRateMultiplier;
-    size_t   mFrameSizeBytes;
-    int      mDataType;
-    int      mDataTypeInfo;
-};
+    uint32_t  mBytesSkipped;     // how many bytes were skipped looking for the start of a frame
+    const uint8_t *mSyncBytes;   // pointer to the sync word specific to a format
+    uint32_t  mSyncLength;       // number of bytes in sync word
+    uint8_t   mHeaderBuffer[32]; // a place to gather the relevant header bytes for parsing
+    uint32_t  mHeaderLength;     // the number of bytes we need to parse
+    uint32_t  mCursor;           // position in the mHeaderBuffer
+    uint32_t  mFormatDumpCount;  // used to thin out the debug dumps
+    uint32_t  mSampleRate;       // encoded sample rate
+    uint32_t  mRateMultiplier;   // SPDIF output data burst rate = msampleRate * mRateMultiplier
+    size_t    mFrameSizeBytes;   // encoded frame size
+    int       mDataType;         // as defined in IEC61937-2 paragraph 4.2
+    int       mDataTypeInfo;     // as defined in IEC61937-2 paragraph 4.1
 
-#define AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES          3
-#define AC3_NUM_FRAME_SIZE_TABLE_ENTRIES          38
-#define AC3_PCM_FRAMES_PER_BLOCK                 256
-#define AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK        6
-#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
-{
-public:
-    AC3FrameScanner();
-    virtual ~AC3FrameScanner();
-
-    virtual bool scan(uint8_t);
-
-    virtual const uint8_t *getHeaderAddress() const { return mHeaderBuffer; }
-    virtual size_t getHeaderSizeBytes() const { return sizeof(mHeaderBuffer); }
-
-    virtual int getDataType()      const { return mDataType; }
-    virtual int getDataTypeInfo()  const { return 0; }
-    virtual int getMaxChannels()   const { return 5 + 1; }
-
-    virtual int getMaxSampleFramesPerSyncFrame() const { return EAC3_RATE_MULTIPLIER
-            * AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK * AC3_PCM_FRAMES_PER_BLOCK; }
-    virtual int getSampleFramesPerSyncFrame() const;
-
-    virtual bool isFirstInBurst();
-    virtual bool isLastInBurst();
-    virtual void resetBurst();
-
-protected:
-
-    // Preamble state machine states.
-    enum State {
-         STATE_EXPECTING_SYNC_1,
-         STATE_EXPECTING_SYNC_2,
-         STATE_GATHERING,
-         STATE_GOT_HEADER,
-    };
-
-    State parseHeader(void);
-
-    State    mState;
-    uint32_t mBytesSkipped;
-    uint8_t  mHeaderBuffer[6];
-    uint8_t  mSubstreamBlockCounts[EAC3_MAX_SUBSTREAMS];
-    int      mAudioBlocksPerSyncFrame;
-    uint     mCursor;
-    uint     mStreamType;
-    uint     mSubstreamID;
-    uint     mFormatDumpCount;
-
-    static const uint8_t kAC3SyncByte1;
-    static const uint8_t kAC3SyncByte2;
-    static const uint16_t   kAC3SampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
-    static const uint16_t kAC3FrameSizeTable[AC3_NUM_FRAME_SIZE_TABLE_ENTRIES]
-            [AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
-
-    static const uint16_t   kEAC3ReducedSampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
-    static const uint16_t kEAC3BlocksPerFrameTable[EAC3_NUM_BLOCKS_PER_FRAME_TABLE_ENTRIES];
+    /**
+     * Parse data in mHeaderBuffer.
+     * Sets mDataType, mFrameSizeBytes, mSampleRate, mRateMultiplier.
+     * @return true if the header is valid.
+     */
+    virtual bool parseHeader() = 0;
 
 };
 
+
 }  // namespace android
 #endif  // ANDROID_AUDIO_FRAME_SCANNER_H
diff --git a/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h b/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
index b880a93..171fec1 100644
--- a/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
+++ b/audio_utils/include/audio_utils/spdif/SPDIFEncoder.h
@@ -1,24 +1,24 @@
 /*
-**
-** Copyright 2014, 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.
-*/
+ * Copyright 2014, 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.
+ */
 
 #ifndef ANDROID_AUDIO_SPDIF_ENCODER_H
 #define ANDROID_AUDIO_SPDIF_ENCODER_H
 
 #include <stdint.h>
+#include <hardware/audio.h>
 #include <audio_utils/spdif/FrameScanner.h>
 
 namespace android {
@@ -34,30 +34,49 @@
 class SPDIFEncoder {
 public:
 
+    SPDIFEncoder(audio_format_t format);
+    // Defaults to AC3 format. Was in original API.
     SPDIFEncoder();
+
     virtual ~SPDIFEncoder();
 
-    // Write encoded data to be wrapped for SPDIF.
-    // The compressed frames do not have to be aligned.
+    /**
+     * Write encoded data to be wrapped for SPDIF.
+     * The compressed frames do not have to be aligned.
+     * @return number of bytes written or negative error
+     */
     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.
+    /**
+     * Called by SPDIFEncoder when it is ready to output a data burst.
+     * Must be implemented in the subclass.
+     * @return number of bytes written or negative error
+     */
     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.
+    /**
+     * Get ratio 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.
+    /**
+     * @return 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.
+    /**
+     * @return number of bytes per PCM frame for the data burst
+     */
     int      getBytesPerOutputFrame();
 
+    /**
+     * @return  true if we can wrap this format in an SPDIF stream
+     */
+    static bool isFormatSupported(audio_format_t format);
+
 protected:
     void   clearBurstBuffer();
-
     void   writeBurstBufferShorts(const uint16_t* buffer, size_t numBytes);
     void   writeBurstBufferBytes(const uint8_t* buffer, size_t numBytes);
     void   sendZeroPad();
@@ -65,25 +84,20 @@
     void   startDataBurst();
     size_t startSyncFrame();
 
-    // State machine states.
-    enum State {
-        STATE_IDLE,   // Waiting to sync with encoded data.
-        STATE_BURST,  // In the middle of a data burst.
-    };
-
     // Works with various formats including AC3.
     FrameScanner *mFramer;
 
-    State     mState;
     uint32_t  mSampleRate;
-    size_t    mFrameSize;
+    size_t    mFrameSize;   // size of sync frame in bytes
     uint16_t *mBurstBuffer; // ALSA wants to get SPDIF data as shorts.
     size_t    mBurstBufferSizeBytes;
     uint32_t  mRateMultiplier;
     uint32_t  mBurstFrames;
-    size_t    mByteCursor;
+    size_t    mByteCursor;  // cursor into data burst
     int       mBitstreamNumber;
-    size_t    mPayloadBytesPending;
+    size_t    mPayloadBytesPending; // number of bytes needed to finish burst
+    // state variable, true if scanning for start of frame
+    bool      mScanning;
 
     static const unsigned short kSPDIFSync1; // Pa
     static const unsigned short kSPDIFSync2; // Pb
diff --git a/audio_utils/spdif/AC3FrameScanner.cpp b/audio_utils/spdif/AC3FrameScanner.cpp
new file mode 100644
index 0000000..7f8068d
--- /dev/null
+++ b/audio_utils/spdif/AC3FrameScanner.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014, 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.
+ */
+
+#define LOG_TAG "AudioSPDIF"
+
+#include <string.h>
+
+#include <utils/Log.h>
+#include <audio_utils/spdif/FrameScanner.h>
+
+#include "AC3FrameScanner.h"
+
+namespace android {
+
+// These values are from the AC3 spec. Do not change them.
+
+const uint8_t AC3FrameScanner::kSyncBytes[] = { 0x0B, 0x77 };
+
+const uint16_t AC3FrameScanner::kAC3SampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES]
+    = { 48000, 44100, 32000 };
+
+// Table contains number of 16-bit words in an AC3 frame.
+// From AC3 spec table 5.13
+const uint16_t AC3FrameScanner::kAC3FrameSizeTable[AC3_NUM_FRAME_SIZE_TABLE_ENTRIES]
+        [AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES] = {
+    { 64, 69, 96 },
+    { 64, 70, 96 },
+    { 80, 87, 120 },
+    { 80, 88, 120 },
+    { 96, 104, 144 },
+    { 96, 105, 144 },
+    { 112, 121, 168 },
+    { 112, 122, 168 },
+    { 128, 139, 192 },
+    { 128, 140, 192 },
+    { 160, 174, 240 },
+    { 160, 175, 240 },
+    { 192, 208, 288 },
+    { 192, 209, 288 },
+    { 224, 243, 336 },
+    { 224, 244, 336 },
+    { 256, 278, 384 },
+    { 256, 279, 384 },
+    { 320, 348, 480 },
+    { 320, 349, 480 },
+    { 384, 417, 576 },
+    { 384, 418, 576 },
+    { 448, 487, 672 },
+    { 448, 488, 672 },
+    { 512, 557, 768 },
+    { 512, 558, 768 },
+    { 640, 696, 960 },
+    { 640, 697, 960 },
+    { 768, 835, 1152 },
+    { 768, 836, 1152 },
+    { 896, 975, 1344 },
+    { 896, 976, 1344 },
+    { 1024, 1114, 1536 },
+    { 1024, 1115, 1536 },
+    { 1152, 1253, 1728 },
+    { 1152, 1254, 1728 },
+    { 1280, 1393, 1920 },
+    { 1280, 1394, 1920 }
+};
+
+const uint16_t AC3FrameScanner::kEAC3ReducedSampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES]
+        = { 24000, 22050, 16000 };
+
+const uint16_t
+        AC3FrameScanner::kEAC3BlocksPerFrameTable[EAC3_NUM_BLOCKS_PER_FRAME_TABLE_ENTRIES]
+        = { 1, 2, 3, 6 };
+
+// Defined in IEC61937-2
+#define SPDIF_DATA_TYPE_AC3     1
+#define SPDIF_DATA_TYPE_E_AC3  21
+#define AC3_STREAM_TYPE_0       0
+#define AC3_STREAM_TYPE_1       1
+#define AC3_STREAM_TYPE_2       2
+// -----------------------------------------------------------------------------
+
+// Scanner for AC3 byte streams.
+AC3FrameScanner::AC3FrameScanner()
+ : FrameScanner(SPDIF_DATA_TYPE_AC3,
+        AC3FrameScanner::kSyncBytes,
+        sizeof(AC3FrameScanner::kSyncBytes), 6)
+ , mStreamType(0)
+ , mSubstreamID(0)
+{
+    mAudioBlocksPerSyncFrame = 6;
+    memset(mSubstreamBlockCounts, 0, sizeof(mSubstreamBlockCounts));
+}
+
+AC3FrameScanner::~AC3FrameScanner()
+{
+}
+
+int AC3FrameScanner::getSampleFramesPerSyncFrame() const
+{
+    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) {
+        if (((mStreamType == AC3_STREAM_TYPE_0)
+                || (mStreamType == AC3_STREAM_TYPE_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()
+{
+    // For EAC3 we don't know if we are the end until we see a
+    // frame that must be at the beginning. See isFirstInBurst().
+    return (mDataType != SPDIF_DATA_TYPE_E_AC3); // Just one AC3 frame per burst.
+}
+
+// TODO Use BitFieldParser
+
+// Parse AC3 header.
+// Detect whether the stream is AC3 or EAC3. Extract data depending on type.
+//
+// @return true if valid
+bool AC3FrameScanner::parseHeader()
+{
+    // Interpret bsid based on paragraph E2.3.1.6 of EAC3 spec.
+    uint32_t bsid = mHeaderBuffer[5] >> 3; // bitstream ID
+    // Check BSID to see if this is EAC3 or regular AC3.
+    // These arbitrary BSID numbers do not have any names in the spec.
+    if ((bsid > 10) && (bsid <= 16)) {
+        mDataType = SPDIF_DATA_TYPE_E_AC3;
+    } else if (bsid <= 8) {
+        mDataType = SPDIF_DATA_TYPE_AC3;
+    } else {
+        ALOGW("AC3 bsid = %d not supported", bsid);
+        return false;
+    }
+
+    // The names fscod, frmsiz are from the AC3 spec.
+    uint32_t fscod = mHeaderBuffer[4] >> 6;
+    if (mDataType == SPDIF_DATA_TYPE_E_AC3) {
+        mStreamType = mHeaderBuffer[2] >> 6; // strmtyp in spec
+        mSubstreamID = (mHeaderBuffer[2] >> 3) & 0x07;
+
+        // Frame size is explicit in EAC3. Paragraph E2.3.1.3
+        uint32_t frmsiz = ((mHeaderBuffer[2] & 0x07) << 8) + mHeaderBuffer[3];
+        mFrameSizeBytes = (frmsiz + 1) * sizeof(int16_t);
+
+        uint32_t numblkscod = 3; // 6 blocks default
+        if (fscod == 3) {
+            uint32_t fscod2 = (mHeaderBuffer[4] >> 4) & 0x03;
+            if (fscod2 >= AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES) {
+                ALOGW("Invalid EAC3 fscod2 = %d\n", fscod2);
+                return false;
+            } else {
+                mSampleRate = kEAC3ReducedSampleRateTable[fscod2];
+            }
+        } else {
+            mSampleRate = kAC3SampleRateTable[fscod];
+            numblkscod = (mHeaderBuffer[4] >> 4) & 0x03;
+        }
+        mRateMultiplier = EAC3_RATE_MULTIPLIER; // per IEC 61973-3 Paragraph 5.3.3
+        // 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.
+        uint32_t frmsizcod = mHeaderBuffer[4] & 0x3F; // frame size code
+
+        if (fscod >= AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES) {
+            ALOGW("Invalid AC3 sampleRateCode = %d\n", fscod);
+            return false;
+        } else if (frmsizcod >= AC3_NUM_FRAME_SIZE_TABLE_ENTRIES) {
+            ALOGW("Invalid AC3 frameSizeCode = %d\n", frmsizcod);
+            return false;
+        } else {
+            mSampleRate = kAC3SampleRateTable[fscod];
+            mRateMultiplier = 1;
+            mFrameSizeBytes = sizeof(uint16_t)
+                    * kAC3FrameSizeTable[frmsizcod][fscod];
+        }
+        mAudioBlocksPerSyncFrame = 6;
+    }
+    ALOGI_IF((mFormatDumpCount == 0),
+            "AC3 frame rate = %d * %d, size = %d, audioBlocksPerSyncFrame = %d\n",
+            mSampleRate, mRateMultiplier, mFrameSizeBytes, mAudioBlocksPerSyncFrame);
+    mFormatDumpCount++;
+    return true;
+}
+
+}  // namespace android
diff --git a/audio_utils/spdif/AC3FrameScanner.h b/audio_utils/spdif/AC3FrameScanner.h
new file mode 100644
index 0000000..f944dcc
--- /dev/null
+++ b/audio_utils/spdif/AC3FrameScanner.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014, 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.
+ */
+
+#ifndef ANDROID_AUDIO_AC3_FRAME_SCANNER_H
+#define ANDROID_AUDIO_AC3_FRAME_SCANNER_H
+
+#include <stdint.h>
+#include <audio_utils/spdif/FrameScanner.h>
+
+namespace android {
+
+#define AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES          3
+#define AC3_NUM_FRAME_SIZE_TABLE_ENTRIES          38
+#define AC3_PCM_FRAMES_PER_BLOCK                 256
+#define AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK        6
+#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
+{
+public:
+    AC3FrameScanner();
+    virtual ~AC3FrameScanner();
+
+    virtual int getMaxChannels()   const { return 5 + 1; } // 5.1 surround
+
+    virtual int getMaxSampleFramesPerSyncFrame() const { return EAC3_RATE_MULTIPLIER
+            * AC3_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK * AC3_PCM_FRAMES_PER_BLOCK; }
+    virtual int getSampleFramesPerSyncFrame() const;
+
+    virtual bool isFirstInBurst();
+    virtual bool isLastInBurst();
+    virtual void resetBurst();
+
+protected:
+    // Keep track of how many of each substream blocks have been accumulated.
+    // We need all of each substream before sending block data burst.
+    uint8_t   mSubstreamBlockCounts[EAC3_MAX_SUBSTREAMS];
+    int       mAudioBlocksPerSyncFrame;
+    // The type of EAC3 stream as per EAC3 spec paragraph 2.3.1.1
+    uint32_t  mStreamType;
+    // substream index
+    uint32_t  mSubstreamID;
+
+    // used to recognize the start of an AC3 sync frame
+    static const uint8_t  kSyncBytes[];
+    // sample rates from AC3 spec table 5.1
+    static const uint16_t kAC3SampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
+    // frame sizes from AC3 spec table 5.13
+    static const uint16_t kAC3FrameSizeTable[AC3_NUM_FRAME_SIZE_TABLE_ENTRIES]
+            [AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
+    // sample rates from EAC3 spec table E2.3
+    static const uint16_t kEAC3ReducedSampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES];
+    // audio blocks per frame from EAC3 spec table E2.4
+    static const uint16_t kEAC3BlocksPerFrameTable[EAC3_NUM_BLOCKS_PER_FRAME_TABLE_ENTRIES];
+
+    virtual bool parseHeader();
+};
+
+}  // namespace android
+
+#endif  // ANDROID_AUDIO_AC3_FRAME_SCANNER_H
diff --git a/audio_utils/spdif/Android.mk b/audio_utils/spdif/Android.mk
index 5d1ac49..39c2fa2 100644
--- a/audio_utils/spdif/Android.mk
+++ b/audio_utils/spdif/Android.mk
@@ -6,7 +6,10 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES:= \
+	BitFieldParser.cpp \
 	FrameScanner.cpp \
+	AC3FrameScanner.cpp \
+	DTSFrameScanner.cpp \
 	SPDIFEncoder.cpp
 
 LOCAL_C_INCLUDES += $(call include-path-for, audio-utils)
diff --git a/audio_utils/spdif/BitFieldParser.cpp b/audio_utils/spdif/BitFieldParser.cpp
new file mode 100644
index 0000000..8f1c11e
--- /dev/null
+++ b/audio_utils/spdif/BitFieldParser.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015, 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.
+ */
+
+#define LOG_TAG "AudioSPDIF"
+//#define LOG_NDEBUG 0
+
+#include <string.h>
+#include <assert.h>
+
+#include <utils/Log.h>
+#include "BitFieldParser.h"
+
+namespace android {
+
+BitFieldParser::BitFieldParser(uint8_t *data)
+ : mData(data)
+ , mBitCursor(0)
+{
+}
+
+BitFieldParser::~BitFieldParser()
+{
+}
+
+uint32_t BitFieldParser::readBits(uint32_t numBits)
+{
+    ALOG_ASSERT(numBits <= 32);
+
+    // Extract some bits from the current byte.
+    uint32_t byteCursor = mBitCursor >> 3; // 8 bits per byte
+    uint8_t byte = mData[byteCursor];
+
+    uint32_t bitsLeftInByte = 8 - (mBitCursor & 7);
+    uint32_t bitsFromByte = (bitsLeftInByte < numBits) ? bitsLeftInByte : numBits;
+    uint32_t result = byte >> (bitsLeftInByte - bitsFromByte);
+    result &= (1 << bitsFromByte) - 1; // mask
+    mBitCursor += bitsFromByte;
+
+    uint32_t bitsRemaining = numBits - bitsFromByte;
+    if (bitsRemaining == 0) {
+        return result;
+    } else {
+        // Use recursion to get remaining bits.
+        return (result << bitsRemaining) | readBits(bitsRemaining);
+    }
+}
+
+}  // namespace android
diff --git a/audio_utils/spdif/BitFieldParser.h b/audio_utils/spdif/BitFieldParser.h
new file mode 100644
index 0000000..1cf45d0
--- /dev/null
+++ b/audio_utils/spdif/BitFieldParser.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015, 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.
+ */
+
+#ifndef ANDROID_AUDIO_BIT_FIELD_PARSER_H
+#define ANDROID_AUDIO_BIT_FIELD_PARSER_H
+
+#include <stdint.h>
+
+namespace android {
+
+/**
+ * Extract bit fields from a byte array.
+ */
+class BitFieldParser {
+public:
+
+    BitFieldParser(uint8_t *data);
+    virtual ~BitFieldParser();
+
+    /**
+     * Read numBits bits from the data array.
+     * Fields may span byte boundaries but may not exceed 32-bits.
+     * Note that the caller must ensure that there is suffcient data.
+     * Assume data is organized as BigEndian format.
+     */
+    uint32_t readBits(uint32_t numBits);
+
+    /*
+     * When the cursor is zero it points to a position right before
+     * the most significant bit.
+     * When the cursor is seven it points to a position right before
+     * the least significant bit.
+     */
+    uint32_t getBitCursor() const { return mBitCursor; }
+
+private:
+    uint8_t *mData;
+    uint32_t mBitCursor;
+};
+
+
+}  // namespace android
+
+#endif  // ANDROID_AUDIO_BIT_FIELD_PARSER_H
diff --git a/audio_utils/spdif/DTSFrameScanner.cpp b/audio_utils/spdif/DTSFrameScanner.cpp
new file mode 100644
index 0000000..7ee4fd4
--- /dev/null
+++ b/audio_utils/spdif/DTSFrameScanner.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2015, 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.
+ */
+
+#define LOG_TAG "AudioSPDIF"
+//#define LOG_NDEBUG 0
+
+#include <assert.h>
+#include <string.h>
+
+#include <utils/Log.h>
+#include <audio_utils/spdif/FrameScanner.h>
+
+#include "BitFieldParser.h"
+#include "DTSFrameScanner.h"
+
+namespace android {
+
+// TODO Handle termination frames.
+// TODO assert if parse past end of header buffer
+// TODO Handle DTS_HD
+
+const uint8_t DTSFrameScanner::kSyncBytes[] =
+        { 0x7F, 0xFE, 0x80, 0x01 };
+
+const int32_t DTSFrameScanner::kDTSSampleRateTable[DTS_NUM_SAMPLE_RATE_TABLE_ENTRIES]
+        = { -1, 8000, 16000, 32000, -1, -1,
+        11025, 22050, 44100, -1, -1, 12000, 24000, 48000, -1, -1 };
+
+// Defined in IEC61937-2
+#define IEC61937_DATA_TYPE_DTS_I        11
+#define IEC61937_DATA_TYPE_DTS_II       12
+#define IEC61937_DATA_TYPE_DTS_III      13
+#define IEC61937_DATA_TYPE_DTS_IV       17
+
+#define IEC61937_MAX_SAMPLES_TYPE_I    512
+#define IEC61937_MAX_SAMPLES_TYPE_II  1024
+#define IEC61937_MAX_SAMPLES_TYPE_III 2048
+
+// Limits defined in DTS spec paragraph 5.3.1
+#define DTS_MINIMUM_NBLKS                5
+#define DTS_MINIMUM_FSIZE               95
+
+#define DTS_HEADER_BYTES_NEEDED         12
+
+// Scanner for DTS byte streams.
+DTSFrameScanner::DTSFrameScanner()
+ : FrameScanner(IEC61937_DATA_TYPE_DTS_I,
+    DTSFrameScanner::kSyncBytes,
+    sizeof(DTSFrameScanner::kSyncBytes),
+    DTS_HEADER_BYTES_NEEDED)
+ , mSampleFramesPerSyncFrame(0)
+{
+}
+
+DTSFrameScanner::~DTSFrameScanner()
+{
+}
+
+// Parse DTS header.
+// Detect whether the stream is DTS or DTS_HD. Extract data depending on type.
+// Sets mDataType, mFrameSizeBytes,
+//      mSampleRate, mRateMultiplier, mLengthCode.
+//
+// @return true if valid
+bool DTSFrameScanner::parseHeader()
+{
+    BitFieldParser parser(&mHeaderBuffer[mSyncLength]);
+
+    // These variables are named after the fields in the DTS spec 5.3.1
+    // Extract field in order.
+    uint32_t ftype = parser.readBits(1);
+    uint32_t deficit = parser.readBits(5); // "short"
+    uint32_t cpf = parser.readBits(1);
+    uint32_t nblks = parser.readBits(7);
+    uint32_t fsize = parser.readBits(14);
+    uint32_t amode = parser.readBits(6);
+    uint32_t sfreq = parser.readBits(4);
+    uint32_t rate = parser.readBits(5);
+    // make sure we did not read past collected data
+    ALOG_ASSERT((mSyncLength + ((parser.getBitCursor() + 7) >> 3))
+            <= mHeaderLength);
+
+    // Validate fields.
+    if (cpf != 0) {
+        ALOGE("DTSFrameScanner: ERROR - CPF not zero!");
+        return false;
+    }
+    if (nblks < DTS_MINIMUM_NBLKS) {
+        ALOGE("DTSFrameScanner: ERROR - nblks = %u", nblks);
+        return false;
+    }
+    if (fsize < DTS_MINIMUM_FSIZE) {
+        ALOGE("DTSFrameScanner: ERROR - fsize = %u", fsize);
+        return false;
+    }
+
+    int32_t sampleRate = kDTSSampleRateTable[rate];
+    if (sampleRate < 0) {
+        ALOGE("DTSFrameScanner: ERROR - invalid sampleRate = %d", sampleRate);
+        return false;
+    }
+    mSampleRate = (uint32_t) sampleRate;
+
+    mSampleFramesPerSyncFrame = (nblks + 1) * DTS_PCM_FRAMES_PER_BLOCK;
+    if (mSampleFramesPerSyncFrame <= IEC61937_MAX_SAMPLES_TYPE_I) {
+        mDataType = IEC61937_DATA_TYPE_DTS_I;
+    } else if (mSampleFramesPerSyncFrame <= IEC61937_MAX_SAMPLES_TYPE_II) {
+        mDataType = IEC61937_DATA_TYPE_DTS_II;
+    } else if (mSampleFramesPerSyncFrame <= IEC61937_MAX_SAMPLES_TYPE_III) {
+        mDataType = IEC61937_DATA_TYPE_DTS_III;
+    } else {
+        mDataType = IEC61937_DATA_TYPE_DTS_IV;
+        // TODO set bits 8,10
+    }
+
+    mFrameSizeBytes = fsize + 1;
+
+    mRateMultiplier = 1; // TODO what about "frequency extension"?
+    ALOGI_IF((mFormatDumpCount == 0),
+            "DTS frame rate = %d * %d, size = %d\n",
+            mSampleRate, mRateMultiplier, mFrameSizeBytes);
+    mFormatDumpCount++;
+    return true;
+}
+
+
+}  // namespace android
diff --git a/audio_utils/spdif/DTSFrameScanner.h b/audio_utils/spdif/DTSFrameScanner.h
new file mode 100644
index 0000000..883ded9
--- /dev/null
+++ b/audio_utils/spdif/DTSFrameScanner.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015, 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.
+ */
+
+#ifndef ANDROID_AUDIO_DTS_FRAME_SCANNER_H
+#define ANDROID_AUDIO_DTS_FRAME_SCANNER_H
+
+#include <stdint.h>
+#include <audio_utils/spdif/FrameScanner.h>
+
+namespace android {
+
+#define DTS_NUM_SAMPLE_RATE_TABLE_ENTRIES      16
+#define DTS_PCM_FRAMES_PER_BLOCK               32
+#define DTS_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK   128
+
+class DTSFrameScanner : public FrameScanner
+{
+public:
+    DTSFrameScanner();
+    virtual ~DTSFrameScanner();
+
+    virtual int getMaxChannels()   const { return 5 + 1; }
+
+    virtual int getMaxSampleFramesPerSyncFrame() const {
+        return  DTS_MAX_BLOCKS_PER_SYNC_FRAME_BLOCK * DTS_PCM_FRAMES_PER_BLOCK;
+    }
+
+    virtual int getSampleFramesPerSyncFrame() const {
+        return mSampleFramesPerSyncFrame;
+    }
+
+    virtual bool isFirstInBurst() { return true; }
+    virtual bool isLastInBurst() { return true; }
+    virtual void resetBurst()  { }
+
+protected:
+
+    int mSampleFramesPerSyncFrame;
+
+    virtual bool parseHeader();
+
+    static const uint8_t kSyncBytes[];
+    static const int32_t kDTSSampleRateTable[];
+
+};
+
+}  // namespace android
+#endif  // ANDROID_AUDIO_DTS_FRAME_SCANNER_H
diff --git a/audio_utils/spdif/FrameScanner.cpp b/audio_utils/spdif/FrameScanner.cpp
index 72edd20..80c1d94 100644
--- a/audio_utils/spdif/FrameScanner.cpp
+++ b/audio_utils/spdif/FrameScanner.cpp
@@ -1,36 +1,40 @@
 /*
-**
-** Copyright 2014, 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.
-*/
+ * Copyright 2014, 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.
+ */
 
 #define LOG_TAG "AudioSPDIF"
 
 #include <string.h>
+#include <assert.h>
 
 #include <utils/Log.h>
 #include <audio_utils/spdif/FrameScanner.h>
 
 namespace android {
 
-#define SPDIF_DATA_TYPE_AC3     1
-#define SPDIF_DATA_TYPE_E_AC3  21
-
-#define AC3_SYNCWORD_SIZE  2
-
-FrameScanner::FrameScanner(int dataType)
- : mSampleRate(0)
+FrameScanner::FrameScanner(int dataType,
+            const uint8_t *syncBytes,
+            uint32_t syncLength,
+            uint32_t headerLength)
+ : mBytesSkipped(0)
+ , mSyncBytes(syncBytes)
+ , mSyncLength(syncLength)
+ , mHeaderLength(headerLength)
+ , mCursor(0)
+ , mFormatDumpCount(0)
+ , mSampleRate(0)
  , mRateMultiplier(1)
  , mFrameSizeBytes(0)
  , mDataType(dataType)
@@ -42,256 +46,34 @@
 {
 }
 
-// ------------------- AC3 -----------------------------------------------------
-// These values are from the AC3 spec. Do not change them.
-const uint8_t AC3FrameScanner::kAC3SyncByte1 = 0x0B;
-const uint8_t AC3FrameScanner::kAC3SyncByte2 = 0x77;
-
-const uint16_t AC3FrameScanner::kAC3SampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES]
-    = { 48000, 44100, 32000 };
-
-// Table contains number of 16-bit words in an AC3 frame.
-const uint16_t AC3FrameScanner::kAC3FrameSizeTable[AC3_NUM_FRAME_SIZE_TABLE_ENTRIES]
-        [AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES] = {
-    { 64, 69, 96 },
-    { 64, 70, 96 },
-    { 80, 87, 120 },
-    { 80, 88, 120 },
-    { 96, 104, 144 },
-    { 96, 105, 144 },
-    { 112, 121, 168 },
-    { 112, 122, 168 },
-    { 128, 139, 192 },
-    { 128, 140, 192 },
-    { 160, 174, 240 },
-    { 160, 175, 240 },
-    { 192, 208, 288 },
-    { 192, 209, 288 },
-    { 224, 243, 336 },
-    { 224, 244, 336 },
-    { 256, 278, 384 },
-    { 256, 279, 384 },
-    { 320, 348, 480 },
-    { 320, 349, 480 },
-    { 384, 417, 576 },
-    { 384, 418, 576 },
-    { 448, 487, 672 },
-    { 448, 488, 672 },
-    { 512, 557, 768 },
-    { 512, 558, 768 },
-    { 640, 696, 960 },
-    { 640, 697, 960 },
-    { 768, 835, 1152 },
-    { 768, 836, 1152 },
-    { 896, 975, 1344 },
-    { 896, 976, 1344 },
-    { 1024, 1114, 1536 },
-    { 1024, 1115, 1536 },
-    { 1152, 1253, 1728 },
-    { 1152, 1254, 1728 },
-    { 1280, 1393, 1920 },
-    { 1280, 1394, 1920 }
-};
-
-const uint16_t AC3FrameScanner::kEAC3ReducedSampleRateTable[AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES]
-        = { 24000, 22050, 16000 };
-
-const uint16_t
-        AC3FrameScanner::kEAC3BlocksPerFrameTable[EAC3_NUM_BLOCKS_PER_FRAME_TABLE_ENTRIES]
-        = { 1, 2, 3, 6 };
-// -----------------------------------------------------------------------------
-
-// Scanner for AC3 byte streams.
-AC3FrameScanner::AC3FrameScanner()
- : FrameScanner(SPDIF_DATA_TYPE_AC3)
- , mState(STATE_EXPECTING_SYNC_1)
- , mBytesSkipped(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;
-}
-
-AC3FrameScanner::~AC3FrameScanner()
-{
-}
-
-int AC3FrameScanner::getSampleFramesPerSyncFrame() const
-{
-    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) {
-        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()
-{
-    // For EAC3 we don't know if we are the end until we see a
-    // frame that must be at the beginning. See isFirstInBurst().
-    return (mDataType != SPDIF_DATA_TYPE_E_AC3); // Just one AC3 frame per burst.
-}
-
-// Parse AC3 header.
-// Detect whether the stream is AC3 or EAC3. Extract data depending on type.
-// Sets mDataType, mFrameSizeBytes, mAudioBlocksPerSyncFrame,
-//      mSampleRate, mRateMultiplier, mLengthCode.
-//
-// @return next state for scanner
-AC3FrameScanner::State AC3FrameScanner::parseHeader()
-{
-    // Interpret bsid based on paragraph E2.3.1.6 of EAC3 spec.
-    int bsid = mHeaderBuffer[5] >> 3; // bitstream ID
-    // Check BSID to see if this is EAC3 or regular AC3
-    if ((bsid >= 10) && (bsid <= 16)) {
-        mDataType = SPDIF_DATA_TYPE_E_AC3;
-    } else if ((bsid >= 0) && (bsid <= 8)) {
-        mDataType = SPDIF_DATA_TYPE_AC3;
-    } else {
-        ALOGW("AC3 bsid = %d not supported", bsid);
-        return STATE_EXPECTING_SYNC_1;
-    }
-
-    // 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; // strmtyp in spec
-        mSubstreamID = (mHeaderBuffer[2] >> 3) & 0x07;
-
-        // 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);
-
-        int numblkscod = 3; // 6 blocks default
-        if (fscod == 3) {
-            int fscod2 = (mHeaderBuffer[4] >> 4) & 0x03;
-            if (fscod2 >= AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES) {
-                ALOGW("Invalid EAC3 fscod2 = %d\n", fscod2);
-                return STATE_EXPECTING_SYNC_1;
-            } else {
-                mSampleRate = kEAC3ReducedSampleRateTable[fscod2];
-            }
-        } else {
-            mSampleRate = kAC3SampleRateTable[fscod];
-            numblkscod = (mHeaderBuffer[4] >> 4) & 0x03;
-        }
-        mRateMultiplier = EAC3_RATE_MULTIPLIER; // per IEC 61973-3 Paragraph 5.3.3
-        // 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
-
-        if (fscod >= AC3_NUM_SAMPLE_RATE_TABLE_ENTRIES) {
-            ALOGW("Invalid AC3 sampleRateCode = %d\n", fscod);
-            return STATE_EXPECTING_SYNC_1;
-        } else if (frmsizcod >= AC3_NUM_FRAME_SIZE_TABLE_ENTRIES) {
-            ALOGW("Invalid AC3 frameSizeCode = %d\n", frmsizcod);
-            return STATE_EXPECTING_SYNC_1;
-        } else {
-            mSampleRate = kAC3SampleRateTable[fscod];
-            mRateMultiplier = 1;
-            mFrameSizeBytes = sizeof(uint16_t)
-                    * kAC3FrameSizeTable[frmsizcod][fscod];
-        }
-        mAudioBlocksPerSyncFrame = 6;
-    }
-    ALOGI_IF((mFormatDumpCount == 0),
-            "AC3 frame rate = %d * %d, size = %d, audioBlocksPerSyncFrame = %d\n",
-            mSampleRate, mRateMultiplier, mFrameSizeBytes, mAudioBlocksPerSyncFrame);
-    mFormatDumpCount++;
-    return STATE_GOT_HEADER;
-}
-
-// State machine that scans for AC3 headers in a byte stream.
+// State machine that scans for headers in a byte stream.
 // @return true if we have detected a complete and valid header.
-bool AC3FrameScanner::scan(uint8_t byte)
+bool FrameScanner::scan(uint8_t byte)
 {
-    State nextState = mState;
-    switch (mState) {
-    case STATE_GOT_HEADER:
-        nextState = STATE_EXPECTING_SYNC_1;
-        // deliberately fall through
-    case STATE_EXPECTING_SYNC_1:
-        if (byte == kAC3SyncByte1) {
-            nextState = STATE_EXPECTING_SYNC_2; // advance
+    bool result = false;
+    ALOGV("FrameScanner: byte = 0x%02X, mCursor = %d\n", byte, mCursor);
+    assert(mCursor < sizeof(mHeaderBuffer));
+    if (mCursor < mSyncLength) {
+        // match sync word
+        if (byte == mSyncBytes[mCursor]) {
+            mHeaderBuffer[mCursor++] = byte;
         } else {
             mBytesSkipped += 1; // skip unsynchronized data
+            mCursor = 0;
         }
-        break;
-
-    case STATE_EXPECTING_SYNC_2:
-        if (byte == kAC3SyncByte2) {
-            if (mBytesSkipped > 0) {
-                ALOGI("WARNING AC3 skipped %u bytes looking for a valid header.\n", mBytesSkipped);
-                mBytesSkipped = 0;
+    } else if (mCursor < mHeaderLength) {
+        // gather header for parsing
+        mHeaderBuffer[mCursor++] = byte;
+        if (mCursor >= mHeaderLength) {
+            if (parseHeader()) {
+                result = true;
+            } else {
+                ALOGE("FrameScanner: ERROR - parseHeader() failed.");
             }
-            mCursor = AC3_SYNCWORD_SIZE;
-            nextState = STATE_GATHERING; // advance
-        } else if (byte == kAC3SyncByte1) {
-            nextState = STATE_EXPECTING_SYNC_2; // resync
-        } else {
-            nextState = STATE_EXPECTING_SYNC_1; // restart
+            mCursor = 0;
         }
-        break;
-
-    case STATE_GATHERING:
-        mHeaderBuffer[mCursor++] = byte; // save for getSyncAddress()
-        if (mCursor >= sizeof(mHeaderBuffer)) {
-            nextState = parseHeader();
-        }
-        break;
-
-    default:
-        ALOGE("AC3FrameScanner: invalid state = %d\n", mState);
-        nextState = STATE_EXPECTING_SYNC_1; // restart
-        break;
     }
-    mState = nextState;
-    return mState == STATE_GOT_HEADER;
+    return result;
 }
 
 }  // namespace android
diff --git a/audio_utils/spdif/SPDIFEncoder.cpp b/audio_utils/spdif/SPDIFEncoder.cpp
index 99cde2a..251e367 100644
--- a/audio_utils/spdif/SPDIFEncoder.cpp
+++ b/audio_utils/spdif/SPDIFEncoder.cpp
@@ -1,20 +1,18 @@
 /*
-**
-** Copyright 2014, 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.
-*/
-
+ * Copyright 2014, 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 <stdint.h>
 #include <string.h>
@@ -23,6 +21,9 @@
 #include <utils/Log.h>
 #include <audio_utils/spdif/SPDIFEncoder.h>
 
+#include "AC3FrameScanner.h"
+#include "DTSFrameScanner.h"
+
 namespace android {
 
 // Burst Preamble defined in IEC61937-1
@@ -32,9 +33,8 @@
 static int32_t sEndianDetector = 1;
 #define isLittleEndian()  (*((uint8_t *)&sEndianDetector))
 
-SPDIFEncoder::SPDIFEncoder()
-  : mState(STATE_IDLE)
-  , mSampleRate(48000)
+SPDIFEncoder::SPDIFEncoder(audio_format_t format)
+  : mSampleRate(48000)
   , mBurstBuffer(NULL)
   , mBurstBufferSizeBytes(0)
   , mRateMultiplier(1)
@@ -42,9 +42,22 @@
   , mByteCursor(0)
   , mBitstreamNumber(0)
   , mPayloadBytesPending(0)
+  , mScanning(true)
 {
-    // TODO support other compressed audio formats
-    mFramer = new AC3FrameScanner();
+    switch(format) {
+        case AUDIO_FORMAT_AC3:
+        case AUDIO_FORMAT_E_AC3:
+            mFramer = new AC3FrameScanner();
+            break;
+        case AUDIO_FORMAT_DTS:
+        case AUDIO_FORMAT_DTS_HD:
+            mFramer = new DTSFrameScanner();
+            break;
+        default:
+            ALOGE("SPDIFEncoder: ERROR invalid audio format = 0x%08X", format);
+            mFramer = NULL;
+            break;
+    }
 
     mBurstBufferSizeBytes = sizeof(uint16_t)
             * SPDIF_ENCODED_CHANNEL_COUNT
@@ -56,9 +69,28 @@
     clearBurstBuffer();
 }
 
+SPDIFEncoder::SPDIFEncoder()
+    : SPDIFEncoder(AUDIO_FORMAT_AC3)
+{
+}
+
 SPDIFEncoder::~SPDIFEncoder()
 {
     delete[] mBurstBuffer;
+    delete mFramer;
+}
+
+bool SPDIFEncoder::isFormatSupported(audio_format_t format)
+{
+    switch(format) {
+        case AUDIO_FORMAT_AC3:
+        case AUDIO_FORMAT_E_AC3:
+        case AUDIO_FORMAT_DTS:
+        case AUDIO_FORMAT_DTS_HD:
+            return true;
+        default:
+            return false;
+    }
 }
 
 int SPDIFEncoder::getBytesPerOutputFrame()
@@ -182,13 +214,11 @@
 {
     size_t bytesLeft = numBytes;
     const uint8_t *data = (const uint8_t *)buffer;
-    ALOGV("SPDIFEncoder: state = %d, write(buffer[0] = 0x%02X, numBytes = %u)",
-        mState, (uint) *data, numBytes);
+    ALOGV("SPDIFEncoder: mScanning = %d, write(buffer[0] = 0x%02X, numBytes = %u)",
+        mScanning, (uint) *data, numBytes);
     while (bytesLeft > 0) {
-        State nextState = mState;
-        switch (mState) {
+        if (mScanning) {
         // Look for beginning of next encoded frame.
-        case STATE_IDLE:
             if (mFramer->scan(*data)) {
                 if (mByteCursor == 0) {
                     startDataBurst();
@@ -198,42 +228,31 @@
                     startDataBurst();
                 }
                 mPayloadBytesPending = startSyncFrame();
-                nextState = STATE_BURST;
+                mScanning = false;
             }
             data++;
             bytesLeft--;
-            break;
-
-        // Write payload until we hit end of frame.
-        case STATE_BURST:
-            {
-                size_t bytesToWrite = bytesLeft;
-                // Only write as many as we need to finish the frame.
-                if (bytesToWrite > mPayloadBytesPending) {
-                    bytesToWrite = mPayloadBytesPending;
-                }
-                writeBurstBufferBytes(data, bytesToWrite);
-
-                data += bytesToWrite;
-                bytesLeft -= bytesToWrite;
-                mPayloadBytesPending -= bytesToWrite;
-
-                // If we have all the payload then send a data burst.
-                if (mPayloadBytesPending == 0) {
-                    if (mFramer->isLastInBurst()) {
-                        flushBurstBuffer();
-                    }
-                    nextState = STATE_IDLE;
-                }
+        } else {
+            // Write payload until we hit end of frame.
+            size_t bytesToWrite = bytesLeft;
+            // Only write as many as we need to finish the frame.
+            if (bytesToWrite > mPayloadBytesPending) {
+                bytesToWrite = mPayloadBytesPending;
             }
-            break;
+            writeBurstBufferBytes(data, bytesToWrite);
 
-        default:
-            ALOGE("SPDIFEncoder: illegal state = %d\n", mState);
-            nextState = STATE_IDLE;
-            break;
+            data += bytesToWrite;
+            bytesLeft -= bytesToWrite;
+            mPayloadBytesPending -= bytesToWrite;
+
+            // If we have all the payload then send a data burst.
+            if (mPayloadBytesPending == 0) {
+                if (mFramer->isLastInBurst()) {
+                    flushBurstBuffer();
+                }
+                mScanning = true;
+            }
         }
-        mState = nextState;
     }
     return numBytes;
 }