Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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 }