Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webm/EbmlComposer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "EbmlComposer.h"
7
#include "mozilla/UniquePtr.h"
8
#include "mozilla/EndianUtils.h"
9
#include "libmkv/EbmlIDs.h"
10
#include "libmkv/EbmlWriter.h"
11
#include "libmkv/WebMElement.h"
12
#include "prtime.h"
13
#include "limits.h"
14
15
namespace mozilla {
16
17
// Timecode scale in nanoseconds
18
static const unsigned long TIME_CODE_SCALE = 1000000;
19
// The WebM header size without audio CodecPrivateData
20
static const int32_t DEFAULT_HEADER_SIZE = 1024;
21
22
void EbmlComposer::GenerateHeader()
23
0
{
24
0
  // Write the EBML header.
25
0
  EbmlGlobal ebml;
26
0
  // The WEbM header default size usually smaller than 1k.
27
0
  auto buffer = MakeUnique<uint8_t[]>(DEFAULT_HEADER_SIZE +
28
0
                                      mCodecPrivateData.Length());
29
0
  ebml.buf = buffer.get();
30
0
  ebml.offset = 0;
31
0
  writeHeader(&ebml);
32
0
  {
33
0
    EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
34
0
    Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
35
0
    {
36
0
      Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
37
0
      // Todo: We don't know the exact sizes of encoded data and
38
0
      // ignore this section.
39
0
      Ebml_EndSubElement(&ebml, &ebmlLocseg);
40
0
      writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
41
0
      {
42
0
        EbmlLoc trackLoc;
43
0
        Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
44
0
        {
45
0
          // Video
46
0
          if (mWidth > 0 && mHeight > 0) {
47
0
            writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
48
0
                            mWidth, mHeight,
49
0
                            mDisplayWidth, mDisplayHeight);
50
0
          }
51
0
          // Audio
52
0
          if (mCodecPrivateData.Length() > 0) {
53
0
            // Extract the pre-skip from mCodecPrivateData
54
0
            // then convert it to nanoseconds.
55
0
            // Details in OpusTrackEncoder.cpp.
56
0
            mCodecDelay =
57
0
              (uint64_t)LittleEndian::readUint16(mCodecPrivateData.Elements() + 10)
58
0
              * PR_NSEC_PER_SEC / 48000;
59
0
            // Fixed 80ms, convert into nanoseconds.
60
0
            uint64_t seekPreRoll = 80 * PR_NSEC_PER_MSEC;
61
0
            writeAudioTrack(&ebml, 0x2, 0x0, "A_OPUS", mSampleFreq,
62
0
                            mChannels, mCodecDelay, seekPreRoll,
63
0
                            mCodecPrivateData.Elements(),
64
0
                            mCodecPrivateData.Length());
65
0
          }
66
0
        }
67
0
        Ebml_EndSubElement(&ebml, &trackLoc);
68
0
      }
69
0
    }
70
0
    // The Recording length is unknown and
71
0
    // ignore write the whole Segment element size
72
0
  }
73
0
  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
74
0
             "write more data > EBML_BUFFER_SIZE");
75
0
  auto block = mClusterBuffs.AppendElement();
76
0
  block->SetLength(ebml.offset);
77
0
  memcpy(block->Elements(), ebml.buf, ebml.offset);
78
0
  mFlushState |= FLUSH_METADATA;
79
0
}
80
81
void EbmlComposer::FinishMetadata()
82
0
{
83
0
  if (mFlushState & FLUSH_METADATA) {
84
0
    // We don't remove the first element of mClusterBuffs because the
85
0
    // |mClusterHeaderIndex| may have value.
86
0
    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
87
0
    mFlushState &= ~FLUSH_METADATA;
88
0
  }
89
0
}
90
91
void EbmlComposer::FinishCluster()
92
0
{
93
0
  FinishMetadata();
94
0
  if (!(mFlushState & FLUSH_CLUSTER)) {
95
0
    // No completed cluster available.
96
0
    return;
97
0
  }
98
0
99
0
  MOZ_ASSERT(mClusterLengthLoc > 0);
100
0
  EbmlGlobal ebml;
101
0
  EbmlLoc ebmlLoc;
102
0
  ebmlLoc.offset = mClusterLengthLoc;
103
0
  ebml.offset = 0;
104
0
  for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
105
0
    ebml.offset += mClusterBuffs[i].Length();
106
0
  }
107
0
  ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
108
0
  Ebml_EndSubElement(&ebml, &ebmlLoc);
109
0
  // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
110
0
  // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
111
0
  for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
112
0
    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
113
0
  }
114
0
115
0
  mClusterHeaderIndex = 0;
116
0
  mClusterLengthLoc = 0;
117
0
  mClusterBuffs.Clear();
118
0
  mFlushState &= ~FLUSH_CLUSTER;
119
0
}
120
121
void
122
EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
123
0
{
124
0
  EbmlGlobal ebml;
125
0
  ebml.offset = 0;
126
0
127
0
  auto frameType = aFrame->GetFrameType();
128
0
  bool flush = false;
129
0
  bool isVP8IFrame = (frameType == EncodedFrame::FrameType::VP8_I_FRAME);
130
0
  if (isVP8IFrame) {
131
0
    FinishCluster();
132
0
    flush = true;
133
0
  } else {
134
0
    // Force it to calculate timecode using signed math via cast
135
0
    int64_t timeCode = (aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode) +
136
0
                       (mCodecDelay / PR_NSEC_PER_MSEC);
137
0
    if (timeCode < SHRT_MIN || timeCode > SHRT_MAX ) {
138
0
      // We're probably going to overflow (or underflow) the timeCode value later!
139
0
      FinishCluster();
140
0
      flush = true;
141
0
    }
142
0
  }
143
0
144
0
  auto block = mClusterBuffs.AppendElement();
145
0
  block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
146
0
  ebml.buf = block->Elements();
147
0
148
0
  if (flush) {
149
0
    EbmlLoc ebmlLoc;
150
0
    Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
151
0
    MOZ_ASSERT(mClusterBuffs.Length() > 0);
152
0
    // current cluster header array index
153
0
    mClusterHeaderIndex = mClusterBuffs.Length() - 1;
154
0
    mClusterLengthLoc = ebmlLoc.offset;
155
0
    // if timeCode didn't under/overflow before, it shouldn't after this
156
0
    mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
157
0
    Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
158
0
    mFlushState |= FLUSH_CLUSTER;
159
0
  }
160
0
161
0
  bool isOpus = (frameType == EncodedFrame::FrameType::OPUS_AUDIO_FRAME);
162
0
  // Can't underflow/overflow now
163
0
  int64_t timeCode = aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode;
164
0
  if (isOpus) {
165
0
    timeCode += mCodecDelay / PR_NSEC_PER_MSEC;
166
0
  }
167
0
  MOZ_ASSERT(timeCode >= SHRT_MIN && timeCode <= SHRT_MAX);
168
0
  writeSimpleBlock(&ebml, isOpus ? 0x2 : 0x1, static_cast<short>(timeCode), isVP8IFrame,
169
0
                   0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
170
0
                   aFrame->GetFrameData().Length());
171
0
  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
172
0
             aFrame->GetFrameData().Length(),
173
0
             "write more data > EBML_BUFFER_SIZE");
174
0
  block->SetLength(ebml.offset);
175
0
}
176
177
void
178
EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
179
                             uint32_t aDisplayWidth, uint32_t aDisplayHeight)
180
0
{
181
0
  MOZ_ASSERT(aWidth > 0, "Width should > 0");
182
0
  MOZ_ASSERT(aHeight > 0, "Height should > 0");
183
0
  MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
184
0
  MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
185
0
  mWidth = aWidth;
186
0
  mHeight = aHeight;
187
0
  mDisplayWidth = aDisplayWidth;
188
0
  mDisplayHeight = aDisplayHeight;
189
0
}
190
191
void
192
EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels)
193
0
{
194
0
  MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
195
0
  MOZ_ASSERT(aChannels > 0, "Channels should > 0");
196
0
  mSampleFreq = aSampleFreq;
197
0
  mChannels = aChannels;
198
0
}
199
200
void
201
EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
202
                            uint32_t aFlag)
203
0
{
204
0
  if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
205
0
      (aFlag & ContainerWriter::GET_HEADER))
206
0
  {
207
0
    FinishMetadata();
208
0
  }
209
0
  if (aFlag & ContainerWriter::FLUSH_NEEDED)
210
0
  {
211
0
    FinishCluster();
212
0
  }
213
0
  // aDestBufs may have some element
214
0
  for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
215
0
    aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
216
0
  }
217
0
  mClusterCanFlushBuffs.Clear();
218
0
}
219
220
EbmlComposer::EbmlComposer()
221
  : mFlushState(FLUSH_NONE)
222
  , mClusterHeaderIndex(0)
223
  , mClusterLengthLoc(0)
224
  , mCodecDelay(0)
225
  , mClusterTimecode(0)
226
  , mWidth(0)
227
  , mHeight(0)
228
  , mDisplayWidth(0)
229
  , mDisplayHeight(0)
230
  , mSampleFreq(0)
231
  , mChannels(0)
232
0
{}
233
234
} // namespace mozilla