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