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 #ifndef MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
18 
19 #define MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
20 
21 #include <arpa/inet.h>
22 #include <stdio.h>
23 #include <map>
24 
25 #include <utils/Errors.h>
26 #include <utils/Log.h>
27 #include <utils/RefBase.h>
28 #include <media/MediaExtractorPluginApi.h>
29 #include <media/NdkMediaFormat.h>
30 
31 namespace android {
32 
33 class DataSourceBase;
34 class MetaDataBase;
35 struct MediaTrack;
36 
37 
38 class MediaTrackHelper;
39 
40 class MediaBufferHelper {
41 private:
42     friend CMediaTrack *wrap(MediaTrackHelper *);
43     CMediaBuffer *mBuffer;
44 public:
MediaBufferHelper(CMediaBuffer * buf)45     MediaBufferHelper(CMediaBuffer *buf) {
46         mBuffer = buf;
47     }
48 
~MediaBufferHelper()49     virtual ~MediaBufferHelper() {}
50 
release()51     virtual void release() {
52         mBuffer->release(mBuffer->handle);
53     }
54 
data()55     virtual void* data() {
56         return mBuffer->data(mBuffer->handle);
57     }
58 
size()59     virtual size_t size() {
60         return mBuffer->size(mBuffer->handle);
61     }
62 
range_offset()63     virtual size_t range_offset() {
64         return mBuffer->range_offset(mBuffer->handle);
65     }
66 
range_length()67     virtual size_t range_length() {
68         return mBuffer->range_length(mBuffer->handle);
69     }
70 
set_range(size_t offset,size_t length)71     virtual void set_range(size_t offset, size_t length) {
72         mBuffer->set_range(mBuffer->handle, offset, length);
73     }
meta_data()74     virtual AMediaFormat *meta_data() {
75         return mBuffer->meta_data(mBuffer->handle);
76     }
77 };
78 
79 class MediaBufferGroupHelper {
80 private:
81     CMediaBufferGroup *mGroup;
82     std::map<CMediaBuffer*, MediaBufferHelper*> mBufferHelpers;
83 public:
MediaBufferGroupHelper(CMediaBufferGroup * group)84     MediaBufferGroupHelper(CMediaBufferGroup *group) {
85         mGroup = group;
86     }
~MediaBufferGroupHelper()87     ~MediaBufferGroupHelper() {
88         // delete all entries in map
89         ALOGV("buffergroup %p map has %zu entries", this, mBufferHelpers.size());
90         for (auto it = mBufferHelpers.begin(); it != mBufferHelpers.end(); ++it) {
91             delete it->second;
92         }
93     }
94     bool init(size_t buffers, size_t buffer_size, size_t growthLimit = 0) {
95         return mGroup->init(mGroup->handle, buffers, buffer_size, growthLimit);
96     }
add_buffer(size_t size)97     void add_buffer(size_t size) {
98         mGroup->add_buffer(mGroup->handle, size);
99     }
100     media_status_t acquire_buffer(
101             MediaBufferHelper **buffer, bool nonBlocking = false, size_t requestedSize = 0) {
102         CMediaBuffer *buf = nullptr;
103         media_status_t ret =
104                 mGroup->acquire_buffer(mGroup->handle, &buf, nonBlocking, requestedSize);
105         if (ret == AMEDIA_OK && buf != nullptr) {
106             auto helper = mBufferHelpers.find(buf);
107             if (helper == mBufferHelpers.end()) {
108                 MediaBufferHelper* newHelper = new MediaBufferHelper(buf);
109                 mBufferHelpers.insert(std::make_pair(buf, newHelper));
110                 *buffer = newHelper;
111             } else {
112                 *buffer = helper->second;
113             }
114         } else {
115             *buffer = nullptr;
116         }
117         return ret;
118     }
has_buffers()119     bool has_buffers() {
120         return mGroup->has_buffers(mGroup->handle);
121     }
122 };
123 
124 class MediaTrackHelper {
125 public:
MediaTrackHelper()126     MediaTrackHelper() : mBufferGroup(nullptr) {
127     }
~MediaTrackHelper()128     virtual ~MediaTrackHelper() {
129         delete mBufferGroup;
130     }
131     virtual media_status_t start() = 0;
132     virtual media_status_t stop() = 0;
133     virtual media_status_t getFormat(AMediaFormat *format) = 0;
134 
135     class ReadOptions {
136     public:
137         enum SeekMode : int32_t {
138             SEEK_PREVIOUS_SYNC,
139             SEEK_NEXT_SYNC,
140             SEEK_CLOSEST_SYNC,
141             SEEK_CLOSEST,
142             SEEK_FRAME_INDEX,
143         };
144 
ReadOptions(uint32_t options,int64_t seekPosUs)145         ReadOptions(uint32_t options, int64_t seekPosUs) {
146             mOptions = options;
147             mSeekPosUs = seekPosUs;
148         }
getSeekTo(int64_t * time_us,SeekMode * mode)149         bool getSeekTo(int64_t *time_us, SeekMode *mode) const {
150             if ((mOptions & CMediaTrackReadOptions::SEEK) == 0) {
151                 return false;
152             }
153             *time_us = mSeekPosUs;
154             *mode = (SeekMode) (mOptions & 7);
155             return true;
156         }
getNonBlocking()157         bool getNonBlocking() const {
158             return mOptions & CMediaTrackReadOptions::NONBLOCKING;
159         }
160     private:
161         uint32_t mOptions;
162         int64_t mSeekPosUs;
163     };
164 
165     virtual media_status_t read(
166             MediaBufferHelper **buffer, const ReadOptions *options = NULL) = 0;
supportsNonBlockingRead()167     virtual bool supportsNonBlockingRead() { return false; }
168 protected:
169     friend CMediaTrack *wrap(MediaTrackHelper *track);
170     MediaBufferGroupHelper *mBufferGroup;
171 };
172 
wrap(MediaTrackHelper * track)173 inline CMediaTrack *wrap(MediaTrackHelper *track) {
174     if (track == nullptr) {
175         return nullptr;
176     }
177     CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack));
178     wrapper->data = track;
179     wrapper->free = [](void *data) -> void {
180         delete (MediaTrackHelper*)(data);
181     };
182     wrapper->start = [](void *data, CMediaBufferGroup *bufferGroup) -> media_status_t {
183         if (((MediaTrackHelper*)data)->mBufferGroup) {
184             // this shouldn't happen, but handle it anyway
185             delete ((MediaTrackHelper*)data)->mBufferGroup;
186         }
187         ((MediaTrackHelper*)data)->mBufferGroup = new MediaBufferGroupHelper(bufferGroup);
188         return ((MediaTrackHelper*)data)->start();
189     };
190     wrapper->stop = [](void *data) -> media_status_t {
191         return ((MediaTrackHelper*)data)->stop();
192     };
193     wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t {
194         return ((MediaTrackHelper*)data)->getFormat(meta);
195     };
196     wrapper->read = [](void *data, CMediaBuffer **buffer,  uint32_t options, int64_t seekPosUs)
197             -> media_status_t {
198         MediaTrackHelper::ReadOptions opts(options, seekPosUs);
199         MediaBufferHelper *buf = NULL;
200         media_status_t ret = ((MediaTrackHelper*)data)->read(&buf, &opts);
201         if (ret == AMEDIA_OK && buf != nullptr) {
202             *buffer = buf->mBuffer;
203         }
204         return ret;
205     };
206     wrapper->supportsNonBlockingRead = [](void *data) -> bool {
207                 return ((MediaTrackHelper*)data)->supportsNonBlockingRead();
208     };
209     return wrapper;
210 }
211 
212 
213 // extractor plugins can derive from this class which looks remarkably
214 // like MediaExtractor and can be easily wrapped in the required C API
215 class MediaExtractorPluginHelper
216 {
217 public:
~MediaExtractorPluginHelper()218     virtual ~MediaExtractorPluginHelper() {}
219     virtual size_t countTracks() = 0;
220     virtual MediaTrackHelper *getTrack(size_t index) = 0;
221 
222     enum GetTrackMetaDataFlags {
223         kIncludeExtensiveMetaData = 1
224     };
225     virtual media_status_t getTrackMetaData(
226             AMediaFormat *meta,
227             size_t index, uint32_t flags = 0) = 0;
228 
229     // Return container specific meta-data. The default implementation
230     // returns an empty metadata object.
231     virtual media_status_t getMetaData(AMediaFormat *meta) = 0;
232 
233     enum Flags {
234         CAN_SEEK_BACKWARD  = 1,  // the "seek 10secs back button"
235         CAN_SEEK_FORWARD   = 2,  // the "seek 10secs forward button"
236         CAN_PAUSE          = 4,
237         CAN_SEEK           = 8,  // the "seek bar"
238     };
239 
240     // If subclasses do _not_ override this, the default is
241     // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
flags()242     virtual uint32_t flags() const {
243         return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE;
244     };
245 
setMediaCas(const uint8_t *,size_t)246     virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) {
247         return AMEDIA_ERROR_INVALID_OPERATION;
248     }
249 
name()250     virtual const char * name() { return "<unspecified>"; }
251 
252 protected:
MediaExtractorPluginHelper()253     MediaExtractorPluginHelper() {}
254 
255 private:
256     MediaExtractorPluginHelper(const MediaExtractorPluginHelper &);
257     MediaExtractorPluginHelper &operator=(const MediaExtractorPluginHelper &);
258 };
259 
wrap(MediaExtractorPluginHelper * extractor)260 inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) {
261     CMediaExtractor *wrapper = (CMediaExtractor*) malloc(sizeof(CMediaExtractor));
262     wrapper->data = extractor;
263     wrapper->free = [](void *data) -> void {
264         delete (MediaExtractorPluginHelper*)(data);
265     };
266     wrapper->countTracks = [](void *data) -> size_t {
267         return ((MediaExtractorPluginHelper*)data)->countTracks();
268     };
269     wrapper->getTrack = [](void *data, size_t index) -> CMediaTrack* {
270         return wrap(((MediaExtractorPluginHelper*)data)->getTrack(index));
271     };
272     wrapper->getTrackMetaData = [](
273             void *data,
274             AMediaFormat *meta,
275             size_t index, uint32_t flags) -> media_status_t {
276         return ((MediaExtractorPluginHelper*)data)->getTrackMetaData(meta, index, flags);
277     };
278     wrapper->getMetaData = [](
279             void *data,
280             AMediaFormat *meta) -> media_status_t {
281         return ((MediaExtractorPluginHelper*)data)->getMetaData(meta);
282     };
283     wrapper->flags = [](
284             void *data) -> uint32_t {
285         return ((MediaExtractorPluginHelper*)data)->flags();
286     };
287     wrapper->setMediaCas = [](
288             void *data, const uint8_t *casToken, size_t size) -> media_status_t {
289         return ((MediaExtractorPluginHelper*)data)->setMediaCas(casToken, size);
290     };
291     wrapper->name = [](
292             void *data) -> const char * {
293         return ((MediaExtractorPluginHelper*)data)->name();
294     };
295     return wrapper;
296 }
297 
298 /* adds some convience methods */
299 class DataSourceHelper {
300 public:
DataSourceHelper(CDataSource * csource)301     explicit DataSourceHelper(CDataSource *csource) {
302         mSource = csource;
303     }
304 
DataSourceHelper(DataSourceHelper * source)305     explicit DataSourceHelper(DataSourceHelper *source) {
306         mSource = source->mSource;
307     }
308 
~DataSourceHelper()309     virtual ~DataSourceHelper() {}
310 
readAt(off64_t offset,void * data,size_t size)311     virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
312         return mSource->readAt(mSource->handle, offset, data, size);
313     }
314 
getSize(off64_t * size)315     virtual status_t getSize(off64_t *size) {
316         return mSource->getSize(mSource->handle, size);
317     }
318 
getUri(char * uriString,size_t bufferSize)319     bool getUri(char *uriString, size_t bufferSize) {
320         return mSource->getUri(mSource->handle, uriString, bufferSize);
321     }
322 
flags()323     virtual uint32_t flags() {
324         return mSource->flags(mSource->handle);
325     }
326 
327     // Convenience methods:
getUInt16(off64_t offset,uint16_t * x)328     bool getUInt16(off64_t offset, uint16_t *x) {
329         *x = 0;
330 
331         uint8_t byte[2];
332         if (readAt(offset, byte, 2) != 2) {
333             return false;
334         }
335 
336         *x = (byte[0] << 8) | byte[1];
337 
338         return true;
339     }
340 
341     // 3 byte int, returned as a 32-bit int
getUInt24(off64_t offset,uint32_t * x)342     bool getUInt24(off64_t offset, uint32_t *x) {
343         *x = 0;
344 
345         uint8_t byte[3];
346         if (readAt(offset, byte, 3) != 3) {
347             return false;
348         }
349 
350         *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
351 
352         return true;
353     }
354 
getUInt32(off64_t offset,uint32_t * x)355     bool getUInt32(off64_t offset, uint32_t *x) {
356         *x = 0;
357 
358         uint32_t tmp;
359         if (readAt(offset, &tmp, 4) != 4) {
360             return false;
361         }
362 
363         *x = ntohl(tmp);
364 
365         return true;
366     }
367 
getUInt64(off64_t offset,uint64_t * x)368     bool getUInt64(off64_t offset, uint64_t *x) {
369         *x = 0;
370 
371         uint64_t tmp;
372         if (readAt(offset, &tmp, 8) != 8) {
373             return false;
374         }
375 
376         *x = ((uint64_t)ntohl(tmp & 0xffffffff) << 32) | ntohl(tmp >> 32);
377 
378         return true;
379     }
380 
381     // read either int<N> or int<2N> into a uint<2N>_t, size is the int size in bytes.
getUInt16Var(off64_t offset,uint16_t * x,size_t size)382     bool getUInt16Var(off64_t offset, uint16_t *x, size_t size) {
383         if (size == 2) {
384             return getUInt16(offset, x);
385         }
386         if (size == 1) {
387             uint8_t tmp;
388             if (readAt(offset, &tmp, 1) == 1) {
389                 *x = tmp;
390                 return true;
391             }
392         }
393         return false;
394     }
395 
getUInt32Var(off64_t offset,uint32_t * x,size_t size)396     bool getUInt32Var(off64_t offset, uint32_t *x, size_t size) {
397         if (size == 4) {
398             return getUInt32(offset, x);
399         }
400         if (size == 2) {
401             uint16_t tmp;
402             if (getUInt16(offset, &tmp)) {
403                 *x = tmp;
404                 return true;
405             }
406         }
407         return false;
408     }
409 
getUInt64Var(off64_t offset,uint64_t * x,size_t size)410     bool getUInt64Var(off64_t offset, uint64_t *x, size_t size) {
411         if (size == 8) {
412             return getUInt64(offset, x);
413         }
414         if (size == 4) {
415             uint32_t tmp;
416             if (getUInt32(offset, &tmp)) {
417                 *x = tmp;
418                 return true;
419             }
420         }
421         return false;
422     }
423 
424 protected:
425     CDataSource *mSource;
426 };
427 
428 
429 
430 // helpers to create a media_uuid_t from a string literal
431 
432 // purposely not defined anywhere so that this will fail to link if
433 // expressions below are not evaluated at compile time
434 int invalid_uuid_string(const char *);
435 
436 template <typename T, size_t N>
_digitAt_(const T (& s)[N],const size_t n)437 constexpr uint8_t _digitAt_(const T (&s)[N], const size_t n) {
438     return s[n] >= '0' && s[n] <= '9' ? s[n] - '0'
439             : s[n] >= 'a' && s[n] <= 'f' ? s[n] - 'a' + 10
440                     : s[n] >= 'A' && s[n] <= 'F' ? s[n] - 'A' + 10
441                             : invalid_uuid_string("uuid: bad digits");
442 }
443 
444 template <typename T, size_t N>
_hexByteAt_(const T (& s)[N],size_t n)445 constexpr uint8_t _hexByteAt_(const T (&s)[N], size_t n) {
446     return (_digitAt_(s, n) << 4) + _digitAt_(s, n + 1);
447 }
448 
_assertIsDash_(char c)449 constexpr bool _assertIsDash_(char c) {
450     return c == '-' ? true : invalid_uuid_string("Wrong format");
451 }
452 
453 template <size_t N>
constUUID(const char (& s)[N])454 constexpr media_uuid_t constUUID(const char (&s) [N]) {
455     static_assert(N == 37, "uuid: wrong length");
456     return
457             _assertIsDash_(s[8]),
458             _assertIsDash_(s[13]),
459             _assertIsDash_(s[18]),
460             _assertIsDash_(s[23]),
461             media_uuid_t {{
462                 _hexByteAt_(s, 0),
463                 _hexByteAt_(s, 2),
464                 _hexByteAt_(s, 4),
465                 _hexByteAt_(s, 6),
466                 _hexByteAt_(s, 9),
467                 _hexByteAt_(s, 11),
468                 _hexByteAt_(s, 14),
469                 _hexByteAt_(s, 16),
470                 _hexByteAt_(s, 19),
471                 _hexByteAt_(s, 21),
472                 _hexByteAt_(s, 24),
473                 _hexByteAt_(s, 26),
474                 _hexByteAt_(s, 28),
475                 _hexByteAt_(s, 30),
476                 _hexByteAt_(s, 32),
477                 _hexByteAt_(s, 34),
478             }};
479 }
480 // Convenience macro to create a media_uuid_t from a string literal, which should
481 // be formatted as "12345678-1234-1234-1234-123456789abc", as generated by
482 // e.g. https://www.uuidgenerator.net/ or the 'uuidgen' linux command.
483 // Hex digits may be upper or lower case.
484 //
485 // The macro call is otherwise equivalent to specifying the structure directly
486 // (e.g. UUID("7d613858-5837-4a38-84c5-332d1cddee27") is the same as
487 //       {{0x7d, 0x61, 0x38, 0x58, 0x58, 0x37, 0x4a, 0x38,
488 //         0x84, 0xc5, 0x33, 0x2d, 0x1c, 0xdd, 0xee, 0x27}})
489 
490 #define UUID(str) []{ constexpr media_uuid_t uuid = constUUID(str); return uuid; }()
491 
492 }  // namespace android
493 
494 #endif  // MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
495