content/media/ogg/OggWriter.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial