Bug 1195723: [flac] P1. Primitive flac frame parser. r?kamidphish draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 04 Aug 2016 17:09:34 +1000
changeset 404110 0b6231660e489f7966e001d50e48d58ec5705dba
parent 404109 c664bb7ee67b0f72f15000512ca25da76f5df67e
child 404111 d88bfebd53174ad8cd95ed78e9da52cc7828153e
push id27118
push userbmo:jyavenard@mozilla.com
push dateMon, 22 Aug 2016 22:58:57 +0000
reviewerskamidphish
bugs1195723
milestone51.0a1
Bug 1195723: [flac] P1. Primitive flac frame parser. r?kamidphish Currently only handle metadata decoding. MozReview-Commit-ID: JL4vqtY5kUS
dom/media/FlacFrameParser.cpp
dom/media/FlacFrameParser.h
dom/media/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/FlacFrameParser.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FlacFrameParser.h"
+#include "mp4_demuxer/ByteReader.h"
+#include "nsTArray.h"
+#include "OggCodecState.h"
+#include "VideoUtils.h"
+
+namespace mozilla
+{
+
+class AutoByteReader : public mp4_demuxer::ByteReader
+{
+public:
+  AutoByteReader(const uint8_t* aData, size_t aSize)
+    : mp4_demuxer::ByteReader(aData, aSize)
+  {
+
+  }
+  virtual ~AutoByteReader()
+  {
+    DiscardRemaining();
+  }
+};
+
+#define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
+#define FLAC_STREAMINFO_SIZE   34
+#define FLAC_MAX_CHANNELS       8
+#define FLAC_MIN_BLOCKSIZE     16
+#define FLAC_MAX_BLOCKSIZE  65535
+#define FLAC_MIN_FRAME_SIZE    11
+
+#define BITMASK(x) ((1ULL << x)-1)
+
+enum
+{
+  FLAC_CHMODE_INDEPENDENT = 0,
+  FLAC_CHMODE_LEFT_SIDE,
+  FLAC_CHMODE_RIGHT_SIDE,
+  FLAC_CHMODE_MID_SIDE,
+};
+
+enum
+{
+  FLAC_METADATA_TYPE_STREAMINFO = 0,
+  FLAC_METADATA_TYPE_PADDING,
+  FLAC_METADATA_TYPE_APPLICATION,
+  FLAC_METADATA_TYPE_SEEKTABLE,
+  FLAC_METADATA_TYPE_VORBIS_COMMENT,
+  FLAC_METADATA_TYPE_CUESHEET,
+  FLAC_METADATA_TYPE_PICTURE,
+  FLAC_METADATA_TYPE_INVALID = 127
+};
+
+FlacFrameParser::FlacFrameParser()
+  : mMinBlockSize(0)
+  , mMaxBlockSize(0)
+  , mMinFrameSize(0)
+  , mMaxFrameSize(0)
+  , mNumFrames(0)
+  , mFullMetadata(false)
+  , mPacketCount(0)
+{
+}
+
+bool
+FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength)
+{
+  if (!aLength || aPacket[0] == 0xff) {
+    // Not a header block.
+    return false;
+  }
+  AutoByteReader br(aPacket, aLength);
+
+  mPacketCount++;
+
+  // blockType is a misnomer as it could indicate here either a packet type
+  // should it points to the start of a Flac in Ogg metadata, or an actual
+  // block type as per the flac specification.
+  uint32_t blockType = br.ReadU8() & 0x7f;
+
+  if (blockType == OGG_FLAC_METADATA_TYPE_STREAMINFO) {
+    if (mPacketCount != 1 || memcmp(br.Read(4), "FLAC", 4) ||
+        br.Remaining() != FLAC_STREAMINFO_SIZE + 12) {
+      return false;
+    }
+    uint32_t major = br.ReadU8();
+    if (major != 1) {
+      // unsupported version;
+      return false;
+    }
+    br.ReadU8(); // minor version
+    mNumHeaders = Some(uint32_t(br.ReadU16()));
+    br.Read(4); // fLaC
+    blockType = br.ReadU8() & BITMASK(7);
+    // First METADATA_BLOCK_STREAMINFO
+    if (blockType != FLAC_METADATA_TYPE_STREAMINFO) {
+      // First block must be a stream info.
+      return false;
+    }
+  }
+
+  uint32_t blockDataSize = br.ReadU24();
+  const uint8_t* blockDataStart = br.Peek(blockDataSize);
+  if (!blockDataStart) {
+    // Incomplete block.
+    return false;
+  }
+
+  switch (blockType) {
+    case FLAC_METADATA_TYPE_STREAMINFO:
+    {
+      if (blockDataSize != FLAC_STREAMINFO_SIZE) {
+        return false;
+      }
+
+      mMinBlockSize = br.ReadU16();
+      mMaxBlockSize = br.ReadU16();
+      mMinFrameSize = br.ReadU24();
+      mMaxFrameSize = br.ReadU24();
+
+      uint64_t blob = br.ReadU64();
+      uint32_t sampleRate = (blob >> 44) & BITMASK(20);
+      if (!sampleRate) {
+        return false;
+      }
+      uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1;
+      if (numChannels > FLAC_MAX_CHANNELS) {
+        return false;
+      }
+      uint32_t bps = ((blob >> 38) & BITMASK(5)) + 1;
+      if (bps > 24) {
+        return false;
+      }
+      mNumFrames = blob & BITMASK(36);
+
+      mInfo.mMimeType = "audio/flac";
+      mInfo.mRate = sampleRate;
+      mInfo.mChannels = numChannels;
+      mInfo.mBitDepth = bps;
+      mInfo.mCodecSpecificConfig->AppendElements(blockDataStart, blockDataSize);
+      CheckedInt64 duration =
+        SaferMultDiv(mNumFrames, USECS_PER_S, sampleRate);
+      mInfo.mDuration = duration.isValid() ? duration.value() : 0;
+      mParser = new OpusParser;
+      break;
+    }
+    case FLAC_METADATA_TYPE_VORBIS_COMMENT:
+    {
+      if (!mParser) {
+        // We must have seen a valid streaminfo first.
+        return false;
+      }
+      nsTArray<uint8_t> comments(blockDataSize + 8);
+      comments.AppendElements("OpusTags", 8);
+      comments.AppendElements(blockDataStart, blockDataSize);
+      if (!mParser->DecodeTags(comments.Elements(), comments.Length())) {
+        return false;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  if (mNumHeaders && mPacketCount > mNumHeaders.ref() + 1) {
+    // Received too many header block. assuming invalid.
+    return false;
+  }
+
+  if (mNumHeaders && mNumHeaders.ref() + 1 == mPacketCount) {
+    mFullMetadata = true;
+  }
+
+  return true;
+}
+
+int64_t
+FlacFrameParser::BlockDuration(const uint8_t* aPacket, size_t aLength) const
+{
+  if (!mInfo.IsValid()) {
+    return -1;
+  }
+  if (mMinBlockSize == mMaxBlockSize) {
+    // block size is fixed, use this instead of looking at the frame header.
+    return mMinBlockSize;
+  }
+  // TODO
+  return 0;
+}
+
+bool
+FlacFrameParser::IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const
+{
+  return aLength && aPacket[0] != 0xff;
+}
+
+MetadataTags*
+FlacFrameParser::GetTags() const
+{
+  MetadataTags* tags;
+
+  tags = new MetadataTags;
+  for (uint32_t i = 0; i < mParser->mTags.Length(); i++) {
+    OggCodecState::AddVorbisComment(tags,
+                                    mParser->mTags[i].Data(),
+                                    mParser->mTags[i].Length());
+  }
+
+  return tags;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/FlacFrameParser.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef FLAC_FRAME_PARSER_H_
+#define FLAC_FRAME_PARSER_H_
+
+#include "mozilla/Maybe.h"
+#include "nsAutoPtr.h"
+#include "MediaDecoder.h" // For MetadataTags
+#include "MediaInfo.h"
+
+namespace mozilla
+{
+
+class OpusParser;
+
+// Decode a Flac Metadata block contained in either a ogg packet
+// (https://xiph.org/flac/ogg_mapping.html) or in flac container
+// (https://xiph.org/flac/format.html#frame_header)
+
+class FlacFrameParser
+{
+public:
+  FlacFrameParser();
+
+  bool IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const;
+  bool DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength);
+  bool HasFullMetadata() const { return mFullMetadata; }
+  // Return the duration in frames found in the block. -1 if error
+  // such as invalid packet.
+  int64_t BlockDuration(const uint8_t* aPacket, size_t aLength) const;
+
+  // Return a hash table with tag metadata.
+  MetadataTags* GetTags() const;
+
+  AudioInfo mInfo;
+
+private:
+  bool ReconstructFlacGranulepos(void);
+  Maybe<uint32_t> mNumHeaders;
+  uint32_t mMinBlockSize;
+  uint32_t mMaxBlockSize;
+  uint32_t mMinFrameSize;
+  uint32_t mMaxFrameSize;
+  uint64_t mNumFrames;
+  bool mFullMetadata;
+  uint32_t mPacketCount;
+
+  // Used to decode the vorbis comment metadata.
+  nsAutoPtr<OpusParser> mParser;
+
+};
+
+}
+
+#endif // FLAC_FRAME_PARSER_H_
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -99,16 +99,17 @@ EXPORTS += [
     'Benchmark.h',
     'BufferMediaResource.h',
     'CubebUtils.h',
     'DecoderDoctorDiagnostics.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
     'EncodedBufferCache.h',
     'FileBlockCache.h',
+    'FlacFrameParser.h',
     'FrameStatistics.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
     'MediaDecoder.h',
     'MediaDecoderOwner.h',
@@ -212,16 +213,17 @@ UNIFIED_SOURCES += [
     'AudioTrackList.cpp',
     'Benchmark.cpp',
     'CanvasCaptureMediaStream.cpp',
     'CubebUtils.cpp',
     'DecoderDoctorDiagnostics.cpp',
     'DOMMediaStream.cpp',
     'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
+    'FlacFrameParser.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderReaderWrapper.cpp',