Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/flac/FlacFrameParser.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "FlacFrameParser.h"
8
#include "nsTArray.h"
9
#include "OggCodecState.h"
10
#include "OpusParser.h"
11
#include "VideoUtils.h"
12
#include "BufferReader.h"
13
#include "mozilla/ResultExtensions.h"
14
15
namespace mozilla
16
{
17
18
0
#define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
19
0
#define FLAC_STREAMINFO_SIZE   34
20
21
0
#define BITMASK(x) ((1ULL << x)-1)
22
23
enum
24
{
25
  FLAC_METADATA_TYPE_STREAMINFO = 0,
26
  FLAC_METADATA_TYPE_PADDING,
27
  FLAC_METADATA_TYPE_APPLICATION,
28
  FLAC_METADATA_TYPE_SEEKTABLE,
29
  FLAC_METADATA_TYPE_VORBIS_COMMENT,
30
  FLAC_METADATA_TYPE_CUESHEET,
31
  FLAC_METADATA_TYPE_PICTURE,
32
  FLAC_METADATA_TYPE_INVALID = 127
33
};
34
35
FlacFrameParser::FlacFrameParser()
36
  : mMinBlockSize(0)
37
  , mMaxBlockSize(0)
38
  , mMinFrameSize(0)
39
  , mMaxFrameSize(0)
40
  , mNumFrames(0)
41
  , mFullMetadata(false)
42
  , mPacketCount(0)
43
0
{
44
0
}
45
46
FlacFrameParser::~FlacFrameParser()
47
0
{
48
0
}
49
50
uint32_t
51
FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket) const
52
0
{
53
0
  uint32_t extra = 4;
54
0
  if (aPacket[0] == 'f') {
55
0
    // This must be the first block read, which contains the fLaC signature.
56
0
    aPacket += 4;
57
0
    extra += 4;
58
0
  }
59
0
  return (BigEndian::readUint32(aPacket) & BITMASK(24)) + extra;
60
0
}
61
62
Result<Ok, nsresult>
63
FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength)
64
0
{
65
0
  if (aLength < 4 || aPacket[0] == 0xff) {
66
0
    // Not a header block.
67
0
    return Err(NS_ERROR_FAILURE);
68
0
  }
69
0
  BufferReader br(aPacket, aLength);
70
0
71
0
  mPacketCount++;
72
0
73
0
  if (aPacket[0] == 'f') {
74
0
    if (mPacketCount != 1 || memcmp(br.Read(4), "fLaC", 4) ||
75
0
        br.Remaining() != FLAC_STREAMINFO_SIZE + 4) {
76
0
      return Err(NS_ERROR_FAILURE);
77
0
    }
78
0
  }
79
0
  uint8_t blockHeader;
80
0
  MOZ_TRY_VAR(blockHeader, br.ReadU8());
81
0
  // blockType is a misnomer as it could indicate here either a packet type
82
0
  // should it points to the start of a Flac in Ogg metadata, or an actual
83
0
  // block type as per the flac specification.
84
0
  uint32_t blockType = blockHeader & 0x7f;
85
0
  bool lastBlock = blockHeader & 0x80;
86
0
87
0
  if (blockType == OGG_FLAC_METADATA_TYPE_STREAMINFO) {
88
0
    if (mPacketCount != 1 || memcmp(br.Read(4), "FLAC", 4) ||
89
0
        br.Remaining() != FLAC_STREAMINFO_SIZE + 12) {
90
0
      return Err(NS_ERROR_FAILURE);
91
0
    }
92
0
    uint32_t major;
93
0
    MOZ_TRY_VAR(major, br.ReadU8());
94
0
    if (major != 1) {
95
0
      // unsupported version;
96
0
      return Err(NS_ERROR_FAILURE);
97
0
    }
98
0
    MOZ_TRY(br.ReadU8()); // minor version
99
0
    uint32_t header;
100
0
    MOZ_TRY_VAR(header, br.ReadU16());
101
0
    mNumHeaders = Some(header);
102
0
    br.Read(4); // fLaC
103
0
    MOZ_TRY_VAR(blockType, br.ReadU8());
104
0
    blockType &= BITMASK(7);
105
0
    // First METADATA_BLOCK_STREAMINFO
106
0
    if (blockType != FLAC_METADATA_TYPE_STREAMINFO) {
107
0
      // First block must be a stream info.
108
0
      return Err(NS_ERROR_FAILURE);
109
0
    }
110
0
  }
111
0
112
0
  uint32_t blockDataSize;
113
0
  MOZ_TRY_VAR(blockDataSize, br.ReadU24());
114
0
  const uint8_t* blockDataStart = br.Peek(blockDataSize);
115
0
  if (!blockDataStart) {
116
0
    // Incomplete block.
117
0
    return Err(NS_ERROR_FAILURE);
118
0
  }
119
0
120
0
  switch (blockType) {
121
0
    case FLAC_METADATA_TYPE_STREAMINFO:
122
0
    {
123
0
      if (mPacketCount != 1 || blockDataSize != FLAC_STREAMINFO_SIZE) {
124
0
        // STREAMINFO must be the first metadata block found, and its size
125
0
        // is constant.
126
0
        return Err(NS_ERROR_FAILURE);
127
0
      }
128
0
129
0
      MOZ_TRY_VAR(mMinBlockSize, br.ReadU16());
130
0
      MOZ_TRY_VAR(mMaxBlockSize, br.ReadU16());
131
0
      MOZ_TRY_VAR(mMinFrameSize, br.ReadU24());
132
0
      MOZ_TRY_VAR(mMaxFrameSize, br.ReadU24());
133
0
134
0
      uint64_t blob;
135
0
      MOZ_TRY_VAR(blob, br.ReadU64());
136
0
      uint32_t sampleRate = (blob >> 44) & BITMASK(20);
137
0
      if (!sampleRate) {
138
0
        return Err(NS_ERROR_FAILURE);
139
0
      }
140
0
      uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1;
141
0
      if (numChannels > FLAC_MAX_CHANNELS) {
142
0
        return Err(NS_ERROR_FAILURE);
143
0
      }
144
0
      uint32_t bps = ((blob >> 36) & BITMASK(5)) + 1;
145
0
      if (bps > 24) {
146
0
        return Err(NS_ERROR_FAILURE);
147
0
      }
148
0
      mNumFrames = blob & BITMASK(36);
149
0
150
0
      mInfo.mMimeType = "audio/flac";
151
0
      mInfo.mRate = sampleRate;
152
0
      mInfo.mChannels = numChannels;
153
0
      mInfo.mBitDepth = bps;
154
0
      mInfo.mCodecSpecificConfig->AppendElements(blockDataStart, blockDataSize);
155
0
      auto duration = FramesToTimeUnit(mNumFrames, sampleRate);
156
0
      mInfo.mDuration = duration.IsValid() ? duration : media::TimeUnit::Zero();
157
0
      mParser = new OpusParser;
158
0
      break;
159
0
    }
160
0
    case FLAC_METADATA_TYPE_VORBIS_COMMENT:
161
0
    {
162
0
      if (!mParser) {
163
0
        // We must have seen a valid streaminfo first.
164
0
        return Err(NS_ERROR_FAILURE);
165
0
      }
166
0
      nsTArray<uint8_t> comments(blockDataSize + 8);
167
0
      comments.AppendElements("OpusTags", 8);
168
0
      comments.AppendElements(blockDataStart, blockDataSize);
169
0
      if (!mParser->DecodeTags(comments.Elements(), comments.Length())) {
170
0
        return Err(NS_ERROR_FAILURE);
171
0
      }
172
0
      break;
173
0
    }
174
0
    default:
175
0
      break;
176
0
  }
177
0
178
0
  if (mNumHeaders && mPacketCount > mNumHeaders.ref() + 1) {
179
0
    // Received too many header block. assuming invalid.
180
0
    return Err(NS_ERROR_FAILURE);
181
0
  }
182
0
183
0
  if (lastBlock || (mNumHeaders && mNumHeaders.ref() + 1 == mPacketCount)) {
184
0
    mFullMetadata = true;
185
0
  }
186
0
187
0
  return Ok();
188
0
}
189
190
int64_t
191
FlacFrameParser::BlockDuration(const uint8_t* aPacket, size_t aLength) const
192
0
{
193
0
  if (!mInfo.IsValid()) {
194
0
    return -1;
195
0
  }
196
0
  if (mMinBlockSize == mMaxBlockSize) {
197
0
    // block size is fixed, use this instead of looking at the frame header.
198
0
    return mMinBlockSize;
199
0
  }
200
0
  // TODO
201
0
  return 0;
202
0
}
203
204
Result<bool, nsresult>
205
FlacFrameParser::IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const
206
0
{
207
0
  // Ogg Flac header
208
0
  // The one-byte packet type 0x7F
209
0
  // The four-byte ASCII signature "FLAC", i.e. 0x46, 0x4C, 0x41, 0x43
210
0
211
0
  // Flac header:
212
0
  // "fLaC", the FLAC stream marker in ASCII, meaning byte 0 of the stream is 0x66, followed by 0x4C 0x61 0x43
213
0
214
0
  // If we detect either a ogg or plain flac header, then it must be valid.
215
0
  if (aLength < 4 || aPacket[0] == 0xff) {
216
0
    // A header is at least 4 bytes.
217
0
    return false;
218
0
  }
219
0
  if (aPacket[0] == 0x7f) {
220
0
    // Ogg packet
221
0
    BufferReader br(aPacket + 1, aLength - 1);
222
0
    const uint8_t* signature = br.Read(4);
223
0
    return signature && !memcmp(signature, "FLAC", 4);
224
0
  }
225
0
  BufferReader br(aPacket, aLength - 1);
226
0
  const uint8_t* signature = br.Read(4);
227
0
  if (signature && !memcmp(signature, "fLaC", 4)) {
228
0
    // Flac start header, must have STREAMINFO as first metadata block;
229
0
    uint32_t blockType;
230
0
    MOZ_TRY_VAR(blockType, br.ReadU8());
231
0
    blockType &= 0x7f;
232
0
    return blockType == FLAC_METADATA_TYPE_STREAMINFO;
233
0
  }
234
0
  char type = aPacket[0] & 0x7f;
235
0
  return type >= 1 && type <= 6;
236
0
}
237
238
MetadataTags*
239
FlacFrameParser::GetTags() const
240
0
{
241
0
  if (!mParser) {
242
0
    return nullptr;
243
0
  }
244
0
245
0
  MetadataTags* tags;
246
0
247
0
  tags = new MetadataTags;
248
0
  for (uint32_t i = 0; i < mParser->mTags.Length(); i++) {
249
0
    OggCodecState::AddVorbisComment(tags,
250
0
                                    mParser->mTags[i].Data(),
251
0
                                    mParser->mTags[i].Length());
252
0
  }
253
0
254
0
  return tags;
255
0
}
256
257
} // namespace mozilla