tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OggWriter.cpp (6310B)


      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 #include "OggWriter.h"
      6 
      7 #include "mozilla/ProfilerLabels.h"
      8 #include "prtime.h"
      9 
     10 #define LOG(args, ...)
     11 
     12 namespace mozilla {
     13 
     14 OggWriter::OggWriter() : mOggStreamState(), mOggPage(), mPacket() {
     15  if (NS_FAILED(Init())) {
     16    LOG("ERROR! Fail to initialize the OggWriter.");
     17  }
     18 }
     19 
     20 OggWriter::~OggWriter() {
     21  if (mInitialized) {
     22    ogg_stream_clear(&mOggStreamState);
     23  }
     24  // mPacket's data was always owned by us, no need to ogg_packet_clear.
     25 }
     26 
     27 nsresult OggWriter::Init() {
     28  MOZ_ASSERT(!mInitialized);
     29 
     30  // The serial number (serialno) should be a random number, for the current
     31  // implementation where the output file contains only a single stream, this
     32  // serialno is used to differentiate between files.
     33  srand(static_cast<unsigned>(PR_Now()));
     34  int rc = ogg_stream_init(&mOggStreamState, rand());
     35 
     36  mPacket.b_o_s = 1;
     37  mPacket.e_o_s = 0;
     38  mPacket.granulepos = 0;
     39  mPacket.packet = nullptr;
     40  mPacket.packetno = 0;
     41  mPacket.bytes = 0;
     42 
     43  mInitialized = (rc == 0);
     44 
     45  return (rc == 0) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
     46 }
     47 
     48 nsresult OggWriter::WriteEncodedTrack(
     49    const nsTArray<RefPtr<EncodedFrame>>& aData, uint32_t aFlags) {
     50  AUTO_PROFILER_LABEL("OggWriter::WriteEncodedTrack", OTHER);
     51 
     52  uint32_t len = aData.Length();
     53  for (uint32_t i = 0; i < len; i++) {
     54    if (aData[i]->mFrameType != EncodedFrame::OPUS_AUDIO_FRAME) {
     55      LOG("[OggWriter] wrong encoded data type!");
     56      return NS_ERROR_FAILURE;
     57    }
     58 
     59    // only pass END_OF_STREAM on the last frame!
     60    nsresult rv = WriteEncodedData(
     61        *aData[i]->mFrameData, aData[i]->mDuration,
     62        i < len - 1 ? (aFlags & ~ContainerWriter::END_OF_STREAM) : aFlags);
     63    if (NS_FAILED(rv)) {
     64      LOG("%p Failed to WriteEncodedTrack!", this);
     65      return rv;
     66    }
     67  }
     68  return NS_OK;
     69 }
     70 
     71 nsresult OggWriter::WriteEncodedData(const nsTArray<uint8_t>& aBuffer,
     72                                     int aDuration, uint32_t aFlags) {
     73  if (!mInitialized) {
     74    LOG("[OggWriter] OggWriter has not initialized!");
     75    return NS_ERROR_FAILURE;
     76  }
     77 
     78  MOZ_ASSERT(!ogg_stream_eos(&mOggStreamState),
     79             "No data can be written after eos has marked.");
     80 
     81  // Set eos flag to true, and once the eos is written to a packet, there must
     82  // not be anymore pages after a page has marked as eos.
     83  if (aFlags & ContainerWriter::END_OF_STREAM) {
     84    LOG("[OggWriter] Set e_o_s flag to true.");
     85    mPacket.e_o_s = 1;
     86  }
     87 
     88  mPacket.packet = const_cast<uint8_t*>(aBuffer.Elements());
     89  mPacket.bytes = aBuffer.Length();
     90  mPacket.granulepos += aDuration;
     91 
     92  // 0 returned on success. -1 returned in the event of internal error.
     93  // The data in the packet is copied into the internal storage managed by the
     94  // mOggStreamState, so we are free to alter the contents of mPacket after
     95  // this call has returned.
     96  int rc = ogg_stream_packetin(&mOggStreamState, &mPacket);
     97  if (rc < 0) {
     98    LOG("[OggWriter] Failed in ogg_stream_packetin! (%d).", rc);
     99    return NS_ERROR_FAILURE;
    100  }
    101 
    102  if (mPacket.b_o_s) {
    103    mPacket.b_o_s = 0;
    104  }
    105  mPacket.packetno++;
    106  mPacket.packet = nullptr;
    107 
    108  return NS_OK;
    109 }
    110 
    111 void OggWriter::ProduceOggPage(nsTArray<nsTArray<uint8_t>>* aOutputBufs) {
    112  aOutputBufs->AppendElement();
    113  aOutputBufs->LastElement().SetLength(mOggPage.header_len + mOggPage.body_len);
    114  memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header,
    115         mOggPage.header_len);
    116  memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len,
    117         mOggPage.body, mOggPage.body_len);
    118 }
    119 
    120 nsresult OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
    121                                     uint32_t aFlags) {
    122  int rc = -1;
    123  AUTO_PROFILER_LABEL("OggWriter::GetContainerData", OTHER);
    124  // Generate the oggOpus Header
    125  if (aFlags & ContainerWriter::GET_HEADER) {
    126    OpusMetadata* meta = static_cast<OpusMetadata*>(mMetadata.get());
    127    NS_ASSERTION(meta, "should have meta data");
    128    NS_ASSERTION(meta->GetKind() == TrackMetadataBase::METADATA_OPUS,
    129                 "should have Opus meta data");
    130 
    131    nsresult rv = WriteEncodedData(meta->mIdHeader, 0);
    132    NS_ENSURE_SUCCESS(rv, rv);
    133 
    134    rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
    135    NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
    136    ProduceOggPage(aOutputBufs);
    137 
    138    rv = WriteEncodedData(meta->mCommentHeader, 0);
    139    NS_ENSURE_SUCCESS(rv, rv);
    140 
    141    rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
    142    NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
    143 
    144    // Force generate a page even if the amount of packet data is not enough.
    145    // Usually do so after a header packet.
    146 
    147    ProduceOggPage(aOutputBufs);
    148  }
    149 
    150  // return value 0 means insufficient data has accumulated to fill a page, or
    151  // an internal error has occurred.
    152  while (ogg_stream_pageout(&mOggStreamState, &mOggPage) != 0) {
    153    ProduceOggPage(aOutputBufs);
    154  }
    155 
    156  if (aFlags & ContainerWriter::FLUSH_NEEDED) {
    157    // return value 0 means no packet to put into a page, or an internal error.
    158    if (ogg_stream_flush(&mOggStreamState, &mOggPage) != 0) {
    159      ProduceOggPage(aOutputBufs);
    160    }
    161    mIsWritingComplete = true;
    162  }
    163 
    164  // We always return NS_OK here since it's OK to call this without having
    165  // enough data to fill a page. It's the more common case compared to internal
    166  // errors, and we cannot distinguish the two.
    167  return NS_OK;
    168 }
    169 
    170 nsresult OggWriter::SetMetadata(
    171    const nsTArray<RefPtr<TrackMetadataBase>>& aMetadata) {
    172  MOZ_ASSERT(aMetadata.Length() == 1);
    173  MOZ_ASSERT(aMetadata[0]);
    174 
    175  AUTO_PROFILER_LABEL("OggWriter::SetMetadata", OTHER);
    176 
    177  if (aMetadata[0]->GetKind() != TrackMetadataBase::METADATA_OPUS) {
    178    LOG("wrong meta data type!");
    179    return NS_ERROR_FAILURE;
    180  }
    181  // Validate each field of METADATA
    182  mMetadata = static_cast<OpusMetadata*>(aMetadata[0].get());
    183  if (mMetadata->mIdHeader.Length() == 0) {
    184    LOG("miss mIdHeader!");
    185    return NS_ERROR_FAILURE;
    186  }
    187  if (mMetadata->mCommentHeader.Length() == 0) {
    188    LOG("miss mCommentHeader!");
    189    return NS_ERROR_FAILURE;
    190  }
    191 
    192  return NS_OK;
    193 }
    194 
    195 }  // namespace mozilla
    196 
    197 #undef LOG