Bug 1195723: [flac] P6. Add support for raw flac metadata decoding. r?kamidphish
We also move flac related files to their own flac folder.
MozReview-Commit-ID: YnV3HNbjZe
deleted file mode 100644
--- a/dom/media/FlacFrameParser.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/* -*- 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
deleted file mode 100644
--- a/dom/media/FlacFrameParser.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- 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_
new file mode 100644
--- /dev/null
+++ b/dom/media/flac/FlacFrameParser.cpp
@@ -0,0 +1,258 @@
+/* -*- 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 BITMASK(x) ((1ULL << x)-1)
+
+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)
+{
+}
+
+uint32_t
+FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket) const
+{
+ uint32_t extra = 4;
+ if (aPacket[0] == 'f') {
+ // This must be the first block read, which contains the fLaC signature.
+ aPacket += 4;
+ extra += 4;
+ }
+ return (BigEndian::readUint32(aPacket) & BITMASK(24)) + extra;
+}
+
+bool
+FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength)
+{
+ if (aLength < 4 || aPacket[0] == 0xff) {
+ // Not a header block.
+ return false;
+ }
+ AutoByteReader br(aPacket, aLength);
+
+ mPacketCount++;
+
+ if (aPacket[0] == 'f') {
+ if (mPacketCount != 1 || memcmp(br.Read(4), "fLaC", 4) ||
+ br.Remaining() != FLAC_STREAMINFO_SIZE + 4) {
+ return false;
+ }
+ aPacket += 4;
+ aLength -= 4;
+ }
+ uint8_t blockHeader = br.ReadU8();
+ // 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 = blockHeader & 0x7f;
+ bool lastBlock = blockHeader & 0x80;
+
+ 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 (mPacketCount != 1 || blockDataSize != FLAC_STREAMINFO_SIZE) {
+ // STREAMINFO must be the first metadata block found, and its size
+ // is constant.
+ 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 (lastBlock || (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
+{
+ // Ogg Flac header
+ // The one-byte packet type 0x7F
+ // The four-byte ASCII signature "FLAC", i.e. 0x46, 0x4C, 0x41, 0x43
+
+ // Flac header:
+ // "fLaC", the FLAC stream marker in ASCII, meaning byte 0 of the stream is 0x66, followed by 0x4C 0x61 0x43
+
+ // If we detect either a ogg or plain flac header, then it must be valid.
+ if (aLength < 4 || aPacket[0] == 0xff) {
+ // A header is at least 4 bytes.
+ return false;
+ }
+ if (aPacket[0] == 0x7f) {
+ // Ogg packet
+ AutoByteReader br(aPacket + 1, aLength - 1);
+ const uint8_t* signature = br.Read(4);
+ return signature && !memcmp(signature, "FLAC", 4);
+ }
+ AutoByteReader br(aPacket, aLength - 1);
+ const uint8_t* signature = br.Read(4);
+ if (signature && !memcmp(signature, "fLaC", 4)) {
+ // Flac start header, must have STREAMINFO as first metadata block;
+ if (!br.CanRead8()) {
+ return false;
+ }
+ uint32_t blockType = br.ReadU8() & 0x7f;
+ return blockType == FLAC_METADATA_TYPE_STREAMINFO;
+ }
+ char type = aPacket[0] & 0x7f;
+ return type >= 1 && type <= 6;
+}
+
+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/flac/FlacFrameParser.h
@@ -0,0 +1,64 @@
+/* -*- 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"
+#include "MediaResource.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;
+ // Return the length of the block header (METADATA_BLOCK_HEADER+
+ // METADATA_BLOCK_DATA), aPacket must point to at least 4
+ // bytes and to a valid block header start (as determined by IsHeaderBlock).
+ uint32_t HeaderBlockLength(const uint8_t* aPacket) 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_
new file mode 100644
--- /dev/null
+++ b/dom/media/flac/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ 'FlacFrameParser.h',
+]
+
+UNIFIED_SOURCES += [
+ 'FlacFrameParser.cpp',
+]
+
+CXXFLAGS += CONFIG['MOZ_LIBVPX_CFLAGS']
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -16,16 +16,17 @@ with Files('RTC*'):
BUG_COMPONENT = component_signaling
component_av = ('Core', 'WebRTC: Audio/Video')
with Files('GetUserMedia*'):
BUG_COMPONENT = component_av
DIRS += [
'encoder',
+ 'flac',
'gmp',
'gmp-plugin',
'gmp-plugin-openh264',
'imagecapture',
'mediasink',
'mediasource',
'ogg',
'platforms',
@@ -99,17 +100,16 @@ 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',
@@ -213,17 +213,16 @@ 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',