1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "OggWriter"
18 
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <sys/prctl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 
25 #include <media/MediaSource.h>
26 #include <media/mediarecorder.h>
27 #include <media/stagefright/MediaBuffer.h>
28 #include <media/stagefright/MediaDefs.h>
29 #include <media/stagefright/MediaErrors.h>
30 #include <media/stagefright/MetaData.h>
31 #include <media/stagefright/OggWriter.h>
32 #include <media/stagefright/foundation/ADebug.h>
33 #include <media/stagefright/foundation/OpusHeader.h>
34 
35 extern "C" {
36 #include <ogg/ogg.h>
37 }
38 
39 // store the int32 value in little-endian order.
writeint(char * buf,int base,int32_t val)40 static inline void writeint(char *buf, int base, int32_t val) {
41     buf[base + 3] = ((val) >> 24) & 0xff;
42     buf[base + 2] = ((val) >> 16) & 0xff;
43     buf[base + 1] = ((val) >> 8) & 0xff;
44     buf[base] = (val)&0xff;
45 }
46 
47 // linkage between our header OggStreamState and the underlying ogg_stream_state
48 // so that consumers of our interface do not require the ogg headers themselves.
49 struct OggStreamState : public ogg_stream_state {};
50 
51 namespace android {
52 
OggWriter(int fd)53 OggWriter::OggWriter(int fd)
54       : mFd(dup(fd)),
55         mHaveAllCodecSpecificData(false),
56         mInitCheck(mFd < 0 ? NO_INIT : OK) {
57     // empty
58 }
59 
~OggWriter()60 OggWriter::~OggWriter() {
61     if (mStarted) {
62         reset();
63     }
64 
65     if (mFd != -1) {
66         close(mFd);
67         mFd = -1;
68     }
69 
70     free(mOs);
71 }
72 
initCheck() const73 status_t OggWriter::initCheck() const {
74     return mInitCheck;
75 }
76 
addSource(const sp<MediaSource> & source)77 status_t OggWriter::addSource(const sp<MediaSource>& source) {
78     if (mInitCheck != OK) {
79         return mInitCheck;
80     }
81 
82     if (mSource != NULL) {
83         return UNKNOWN_ERROR;
84     }
85 
86     // Support is limited to single track of Opus audio.
87     const char* mime;
88     source->getFormat()->findCString(kKeyMIMEType, &mime);
89     const char* opus = MEDIA_MIMETYPE_AUDIO_OPUS;
90     if (strncasecmp(mime, opus, strlen(opus))) {
91         ALOGE("Track (%s) other than %s is not supported", mime, opus);
92         return ERROR_UNSUPPORTED;
93     }
94 
95     mOs = (OggStreamState*) malloc(sizeof(ogg_stream_state));
96     if (ogg_stream_init((ogg_stream_state*)mOs, rand()) == -1) {
97         ALOGE("ogg stream init failed");
98         return UNKNOWN_ERROR;
99     }
100 
101     // Write Ogg headers.
102     int32_t nChannels = 0;
103     if (!source->getFormat()->findInt32(kKeyChannelCount, &nChannels)) {
104         ALOGE("Missing format keys for audio track");
105         source->getFormat()->dumpToLog();
106         return BAD_VALUE;
107     }
108     source->getFormat()->dumpToLog();
109 
110     int32_t sampleRate = 0;
111     if (!source->getFormat()->findInt32(kKeySampleRate, &sampleRate)) {
112         ALOGE("Missing format key for sample rate");
113         source->getFormat()->dumpToLog();
114         return UNKNOWN_ERROR;
115     }
116 
117     mSampleRate = sampleRate;
118     uint32_t type;
119     const void *header_data = NULL;
120     size_t packet_size = 0;
121 
122     if (!source->getFormat()->findData(kKeyOpusHeader, &type, &header_data, &packet_size)) {
123         ALOGV("opus header not found in format");
124     } else if (header_data && packet_size) {
125         writeOggHeaderPackets((unsigned char *)header_data, packet_size);
126     } else {
127         ALOGD("ignoring incomplete opus header data in format");
128     }
129 
130     mSource = source;
131     return OK;
132 }
133 
writeOggHeaderPackets(unsigned char * buf,size_t size)134 status_t OggWriter::writeOggHeaderPackets(unsigned char *buf, size_t size) {
135     ogg_packet op;
136     ogg_page og;
137     op.packet = buf;
138     op.bytes = size;
139     op.b_o_s = 1;
140     op.e_o_s = 0;
141     op.granulepos = 0;
142     op.packetno = 0;
143     ogg_stream_packetin((ogg_stream_state*)mOs, &op);
144 
145     int ret;
146     while ((ret = ogg_stream_flush((ogg_stream_state*)mOs, &og))) {
147         if (!ret) break;
148         write(mFd, og.header, og.header_len);
149         write(mFd, og.body, og.body_len);
150     }
151 
152 
153     const char* vendor_string = "libopus";
154     const int vendor_length = strlen(vendor_string);
155     int user_comment_list_length = 0;
156 
157     const int comments_length = 8 + 4 + vendor_length + 4 + user_comment_list_length;
158     char* comments = (char*)malloc(comments_length);
159     if (comments == NULL) {
160         ALOGE("failed to allocate ogg comment buffer");
161         return UNKNOWN_ERROR;
162     }
163     memcpy(comments, "OpusTags", 8);
164     writeint(comments, 8, vendor_length);
165     memcpy(comments + 12, vendor_string, vendor_length);
166     writeint(comments, 12 + vendor_length, user_comment_list_length);
167 
168     op.packet = (unsigned char*)comments;
169     op.bytes = comments_length;
170     op.b_o_s = 0;
171     op.e_o_s = 0;
172     op.granulepos = 0;
173     op.packetno = 1;
174     ogg_stream_packetin((ogg_stream_state*)mOs, &op);
175 
176     while ((ret = ogg_stream_flush((ogg_stream_state*)mOs, &og))) {
177         if (!ret) break;
178         write(mFd, og.header, og.header_len);
179         write(mFd, og.body, og.body_len);
180     }
181 
182     free(comments);
183     mHaveAllCodecSpecificData = true;
184     return OK;
185 }
186 
start(MetaData *)187 status_t OggWriter::start(MetaData* /* params */) {
188     if (mInitCheck != OK) {
189         return mInitCheck;
190     }
191 
192     if (mSource == NULL) {
193         return UNKNOWN_ERROR;
194     }
195 
196     if (mStarted && mPaused) {
197         mPaused = false;
198         mResumed = true;
199         return OK;
200     } else if (mStarted) {
201         // Already started, does nothing
202         return OK;
203     }
204 
205     status_t err = mSource->start();
206 
207     if (err != OK) {
208         return err;
209     }
210 
211     pthread_attr_t attr;
212     pthread_attr_init(&attr);
213     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
214 
215     mReachedEOS = false;
216     mDone = false;
217 
218     pthread_create(&mThread, &attr, ThreadWrapper, this);
219     pthread_attr_destroy(&attr);
220 
221     mStarted = true;
222 
223     return OK;
224 }
225 
pause()226 status_t OggWriter::pause() {
227     if (!mStarted) {
228         return OK;
229     }
230     mPaused = true;
231     return OK;
232 }
233 
reset()234 status_t OggWriter::reset() {
235     if (!mStarted) {
236         return OK;
237     }
238 
239     mDone = true;
240 
241     void* dummy;
242     pthread_join(mThread, &dummy);
243 
244     status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
245     {
246         status_t status = mSource->stop();
247         if (err == OK && (status != OK && status != ERROR_END_OF_STREAM)) {
248             err = status;
249         }
250     }
251 
252     mStarted = false;
253     return err;
254 }
255 
exceedsFileSizeLimit()256 bool OggWriter::exceedsFileSizeLimit() {
257     if (mMaxFileSizeLimitBytes == 0) {
258         return false;
259     }
260     return mEstimatedSizeBytes > mMaxFileSizeLimitBytes;
261 }
262 
exceedsFileDurationLimit()263 bool OggWriter::exceedsFileDurationLimit() {
264     if (mMaxFileDurationLimitUs == 0) {
265         return false;
266     }
267     return mEstimatedDurationUs > mMaxFileDurationLimitUs;
268 }
269 
270 // static
ThreadWrapper(void * me)271 void* OggWriter::ThreadWrapper(void* me) {
272     return (void*)(uintptr_t) static_cast<OggWriter*>(me)->threadFunc();
273 }
274 
threadFunc()275 status_t OggWriter::threadFunc() {
276     mEstimatedDurationUs = 0;
277     mEstimatedSizeBytes = 0;
278     bool stoppedPrematurely = true;
279     int64_t previousPausedDurationUs = 0;
280     int64_t maxTimestampUs = 0;
281     status_t err = OK;
282 
283     prctl(PR_SET_NAME, (unsigned long)"OggWriter", 0, 0, 0);
284 
285     while (!mDone) {
286         MediaBufferBase* buffer = nullptr;
287         err = mSource->read(&buffer);
288 
289         if (err != OK) {
290             ALOGW("failed to read next buffer");
291             break;
292         }
293 
294         if (mPaused) {
295             buffer->release();
296             buffer = nullptr;
297             continue;
298         }
299         mEstimatedSizeBytes += buffer->range_length();
300         if (exceedsFileSizeLimit()) {
301             buffer->release();
302             buffer = nullptr;
303             notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
304             ALOGW("estimated size(%" PRId64 ") exceeds limit (%" PRId64 ")",
305                   mEstimatedSizeBytes, mMaxFileSizeLimitBytes);
306             break;
307         }
308 
309         int32_t isCodecSpecific;
310         if ((buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecSpecific)
311              && isCodecSpecific)
312             || IsOpusHeader((uint8_t*)buffer->data() + buffer->range_offset(),
313                          buffer->range_length())) {
314             if (mHaveAllCodecSpecificData == false) {
315                 size_t opusHeadSize = 0;
316                 size_t codecDelayBufSize = 0;
317                 size_t seekPreRollBufSize = 0;
318                 void *opusHeadBuf = NULL;
319                 void *codecDelayBuf = NULL;
320                 void *seekPreRollBuf = NULL;
321                 GetOpusHeaderBuffers((uint8_t*)buffer->data() + buffer->range_offset(),
322                                     buffer->range_length(), &opusHeadBuf,
323                                     &opusHeadSize, &codecDelayBuf,
324                                     &codecDelayBufSize, &seekPreRollBuf,
325                                     &seekPreRollBufSize);
326                 writeOggHeaderPackets((unsigned char *)opusHeadBuf, opusHeadSize);
327             } else {
328                 ALOGV("ignoring later copy of CSD contained in info buffer");
329             }
330             buffer->release();
331             buffer = nullptr;
332             continue;
333         }
334 
335         if (mHaveAllCodecSpecificData == false) {
336             ALOGE("Did not get valid opus header before first sample data");
337             buffer->release();
338             buffer = nullptr;
339             err = ERROR_MALFORMED;
340             break;
341         }
342 
343         int64_t timestampUs;
344         CHECK(buffer->meta_data().findInt64(kKeyTime, &timestampUs));
345         if (timestampUs > mEstimatedDurationUs) {
346             mEstimatedDurationUs = timestampUs;
347         }
348         if (mResumed) {
349             previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000);
350             mResumed = false;
351         }
352 
353         timestampUs -= previousPausedDurationUs;
354 
355         ALOGV("time stamp: %" PRId64 ", previous paused duration: %" PRId64, timestampUs,
356               previousPausedDurationUs);
357         if (timestampUs > maxTimestampUs) {
358             maxTimestampUs = timestampUs;
359         }
360 
361         if (exceedsFileDurationLimit()) {
362             buffer->release();
363             buffer = nullptr;
364             notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
365             ALOGW("estimated duration(%" PRId64 " us) exceeds limit(%" PRId64 " us)",
366                   mEstimatedDurationUs, mMaxFileDurationLimitUs);
367             break;
368         }
369 
370         ogg_packet op;
371         ogg_page og;
372         op.packet = (uint8_t*)buffer->data() + buffer->range_offset();
373         op.bytes = (long)buffer->range_length();
374         op.b_o_s = 0;
375         op.e_o_s = mReachedEOS ? 1 : 0;
376         // granulepos is the total number of PCM audio samples @ 48 kHz, up to and
377         // including the current packet.
378         ogg_int64_t granulepos = (48000 * mEstimatedDurationUs) / 1000000;
379         op.granulepos = granulepos;
380 
381         // Headers are at packets 0 and 1.
382         op.packetno = 2 + (ogg_int32_t)mCurrentPacketId++;
383         ogg_stream_packetin((ogg_stream_state*)mOs, &op);
384         size_t n = 0;
385 
386         while (ogg_stream_flush((ogg_stream_state*)mOs, &og) > 0) {
387             write(mFd, og.header, og.header_len);
388             write(mFd, og.body, og.body_len);
389             n = n + og.header_len + og.body_len;
390         }
391 
392         if (n < buffer->range_length()) {
393             buffer->release();
394             buffer = nullptr;
395             err = ERROR_IO;
396             break;
397         }
398 
399         if (err != OK) {
400             break;
401         }
402 
403         stoppedPrematurely = false;
404 
405         buffer->release();
406         buffer = nullptr;
407     }
408 
409     // end of stream is an ok thing
410     if (err == ERROR_END_OF_STREAM) {
411         err = OK;
412     }
413 
414     if (err == OK && stoppedPrematurely) {
415         err = ERROR_MALFORMED;
416     }
417 
418     close(mFd);
419     mFd = -1;
420     mReachedEOS = true;
421 
422     return err;
423 }
424 
reachedEOS()425 bool OggWriter::reachedEOS() {
426     return mReachedEOS;
427 }
428 
429 }  // namespace android
430