1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FlacFrameParser.h"
9 #include "OggCodecState.h"
10 #include "OpusParser.h"
11 #include "VideoUtils.h"
12 #include "BufferReader.h"
13 #include "mozilla/ResultExtensions.h"
14 #include "mozilla/Try.h"
18 #define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
19 #define FLAC_STREAMINFO_SIZE 34
21 #define BITMASK(x) ((1ULL << x) - 1)
24 FLAC_METADATA_TYPE_STREAMINFO
= 0,
25 FLAC_METADATA_TYPE_PADDING
,
26 FLAC_METADATA_TYPE_APPLICATION
,
27 FLAC_METADATA_TYPE_SEEKTABLE
,
28 FLAC_METADATA_TYPE_VORBIS_COMMENT
,
29 FLAC_METADATA_TYPE_CUESHEET
,
30 FLAC_METADATA_TYPE_PICTURE
,
31 FLAC_METADATA_TYPE_INVALID
= 127
34 FlacFrameParser::FlacFrameParser()
43 FlacFrameParser::~FlacFrameParser() = default;
45 uint32_t FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket
) const {
47 if (aPacket
[0] == 'f') {
48 // This must be the first block read, which contains the fLaC signature.
52 return (BigEndian::readUint32(aPacket
) & BITMASK(24)) + extra
;
55 Result
<Ok
, nsresult
> FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket
,
57 if (aLength
< 4 || aPacket
[0] == 0xff) {
58 // Not a header block.
59 return Err(NS_ERROR_FAILURE
);
61 BufferReader
br(aPacket
, aLength
);
65 if (aPacket
[0] == 'f') {
66 if (mPacketCount
!= 1 || memcmp(br
.Read(4), "fLaC", 4) ||
67 br
.Remaining() != FLAC_STREAMINFO_SIZE
+ 4) {
68 return Err(NS_ERROR_FAILURE
);
72 MOZ_TRY_VAR(blockHeader
, br
.ReadU8());
73 // blockType is a misnomer as it could indicate here either a packet type
74 // should it points to the start of a Flac in Ogg metadata, or an actual
75 // block type as per the flac specification.
76 uint32_t blockType
= blockHeader
& 0x7f;
77 bool lastBlock
= blockHeader
& 0x80;
79 if (blockType
== OGG_FLAC_METADATA_TYPE_STREAMINFO
) {
80 if (mPacketCount
!= 1 || memcmp(br
.Read(4), "FLAC", 4) ||
81 br
.Remaining() != FLAC_STREAMINFO_SIZE
+ 12) {
82 return Err(NS_ERROR_FAILURE
);
85 MOZ_TRY_VAR(major
, br
.ReadU8());
87 // unsupported version;
88 return Err(NS_ERROR_FAILURE
);
90 MOZ_TRY(br
.ReadU8()); // minor version
92 MOZ_TRY_VAR(header
, br
.ReadU16());
93 mNumHeaders
= Some(header
);
95 MOZ_TRY_VAR(blockType
, br
.ReadU8());
96 blockType
&= BITMASK(7);
97 // First METADATA_BLOCK_STREAMINFO
98 if (blockType
!= FLAC_METADATA_TYPE_STREAMINFO
) {
99 // First block must be a stream info.
100 return Err(NS_ERROR_FAILURE
);
104 uint32_t blockDataSize
;
105 MOZ_TRY_VAR(blockDataSize
, br
.ReadU24());
106 const uint8_t* blockDataStart
= br
.Peek(blockDataSize
);
107 if (!blockDataStart
) {
109 return Err(NS_ERROR_FAILURE
);
113 case FLAC_METADATA_TYPE_STREAMINFO
: {
114 if (mPacketCount
!= 1 || blockDataSize
!= FLAC_STREAMINFO_SIZE
) {
115 // STREAMINFO must be the first metadata block found, and its size
117 return Err(NS_ERROR_FAILURE
);
120 MOZ_TRY_VAR(mMinBlockSize
, br
.ReadU16());
121 MOZ_TRY_VAR(mMaxBlockSize
, br
.ReadU16());
122 MOZ_TRY_VAR(mMinFrameSize
, br
.ReadU24());
123 MOZ_TRY_VAR(mMaxFrameSize
, br
.ReadU24());
126 MOZ_TRY_VAR(blob
, br
.ReadU64());
127 uint32_t sampleRate
= (blob
>> 44) & BITMASK(20);
129 return Err(NS_ERROR_FAILURE
);
131 uint32_t numChannels
= ((blob
>> 41) & BITMASK(3)) + 1;
132 if (numChannels
> FLAC_MAX_CHANNELS
) {
133 return Err(NS_ERROR_FAILURE
);
135 uint32_t bps
= ((blob
>> 36) & BITMASK(5)) + 1;
137 return Err(NS_ERROR_FAILURE
);
139 mNumFrames
= blob
& BITMASK(36);
141 mInfo
.mMimeType
= "audio/flac";
142 mInfo
.mRate
= sampleRate
;
143 mInfo
.mChannels
= numChannels
;
144 mInfo
.mBitDepth
= bps
;
145 FlacCodecSpecificData flacCodecSpecificData
;
146 flacCodecSpecificData
.mStreamInfoBinaryBlob
->AppendElements(
147 blockDataStart
, blockDataSize
);
148 mInfo
.mCodecSpecificConfig
=
149 AudioCodecSpecificVariant
{std::move(flacCodecSpecificData
)};
150 auto duration
= media::TimeUnit(mNumFrames
, sampleRate
);
151 mInfo
.mDuration
= duration
.IsValid() ? duration
: media::TimeUnit::Zero();
152 mParser
= MakeUnique
<OpusParser
>();
155 case FLAC_METADATA_TYPE_VORBIS_COMMENT
: {
157 // We must have seen a valid streaminfo first.
158 return Err(NS_ERROR_FAILURE
);
160 nsTArray
<uint8_t> comments(blockDataSize
+ 8);
161 comments
.AppendElements("OpusTags", 8);
162 comments
.AppendElements(blockDataStart
, blockDataSize
);
163 if (!mParser
->DecodeTags(comments
.Elements(), comments
.Length())) {
164 return Err(NS_ERROR_FAILURE
);
172 if (mNumHeaders
&& mPacketCount
> mNumHeaders
.ref() + 1) {
173 // Received too many header block. assuming invalid.
174 return Err(NS_ERROR_FAILURE
);
177 if (lastBlock
|| (mNumHeaders
&& mNumHeaders
.ref() + 1 == mPacketCount
)) {
178 mFullMetadata
= true;
184 int64_t FlacFrameParser::BlockDuration(const uint8_t* aPacket
,
185 size_t aLength
) const {
186 if (!mInfo
.IsValid()) {
189 if (mMinBlockSize
== mMaxBlockSize
) {
190 // block size is fixed, use this instead of looking at the frame header.
191 return mMinBlockSize
;
197 Result
<bool, nsresult
> FlacFrameParser::IsHeaderBlock(const uint8_t* aPacket
,
198 size_t aLength
) const {
200 // The one-byte packet type 0x7F
201 // The four-byte ASCII signature "FLAC", i.e. 0x46, 0x4C, 0x41, 0x43
204 // "fLaC", the FLAC stream marker in ASCII, meaning byte 0 of the stream is
205 // 0x66, followed by 0x4C 0x61 0x43
207 // If we detect either a ogg or plain flac header, then it must be valid.
208 if (aLength
< 4 || aPacket
[0] == 0xff) {
209 // A header is at least 4 bytes.
212 if (aPacket
[0] == 0x7f) {
214 BufferReader
br(aPacket
+ 1, aLength
- 1);
215 const uint8_t* signature
= br
.Read(4);
216 return signature
&& !memcmp(signature
, "FLAC", 4);
218 BufferReader
br(aPacket
, aLength
- 1);
219 const uint8_t* signature
= br
.Read(4);
220 if (signature
&& !memcmp(signature
, "fLaC", 4)) {
221 // Flac start header, must have STREAMINFO as first metadata block;
223 MOZ_TRY_VAR(blockType
, br
.ReadU8());
225 return blockType
== FLAC_METADATA_TYPE_STREAMINFO
;
227 char type
= aPacket
[0] & 0x7f;
228 return type
>= 1 && type
<= 6;
231 UniquePtr
<MetadataTags
> FlacFrameParser::GetTags() const {
236 auto tags
= MakeUnique
<MetadataTags
>();
237 for (uint32_t i
= 0; i
< mParser
->mTags
.Length(); i
++) {
238 OggCodecState::AddVorbisComment(tags
, mParser
->mTags
[i
].Data(),
239 mParser
->mTags
[i
].Length());
245 } // namespace mozilla