content/media/ogg/OggWriter.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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

mercurial