1 /*
2  * Copyright (C) 2009 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_NDEBUG 0
18 #define LOG_TAG "MediaExtractor"
19 #include <utils/Log.h>
20 #include <inttypes.h>
21 #include <pwd.h>
22 
23 #include "include/AMRExtractor.h"
24 #include "include/MP3Extractor.h"
25 #include "include/MPEG4Extractor.h"
26 #include "include/WAVExtractor.h"
27 #include "include/OggExtractor.h"
28 #include "include/MPEG2PSExtractor.h"
29 #include "include/MPEG2TSExtractor.h"
30 #include "include/DRMExtractor.h"
31 #include "include/WVMExtractor.h"
32 #include "include/FLACExtractor.h"
33 #include "include/AACExtractor.h"
34 #include "include/MidiExtractor.h"
35 
36 #include "matroska/MatroskaExtractor.h"
37 
38 #include <binder/IServiceManager.h>
39 #include <binder/MemoryDealer.h>
40 
41 #include <media/stagefright/foundation/ADebug.h>
42 #include <media/stagefright/foundation/AMessage.h>
43 #include <media/stagefright/DataSource.h>
44 #include <media/stagefright/MediaDefs.h>
45 #include <media/stagefright/MediaExtractor.h>
46 #include <media/stagefright/MetaData.h>
47 #include <media/IMediaExtractorService.h>
48 #include <cutils/properties.h>
49 #include <utils/String8.h>
50 #include <private/android_filesystem_config.h>
51 
52 
53 namespace android {
54 
MediaExtractor()55 MediaExtractor::MediaExtractor():
56     mIsDrm(false) {
57     if (!LOG_NDEBUG) {
58         uid_t uid = getuid();
59         struct passwd *pw = getpwuid(uid);
60         ALOGI("extractor created in uid: %d (%s)", getuid(), pw->pw_name);
61     }
62 
63 }
64 
65 
getMetaData()66 sp<MetaData> MediaExtractor::getMetaData() {
67     return new MetaData;
68 }
69 
flags() const70 uint32_t MediaExtractor::flags() const {
71     return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK;
72 }
73 
74 
75 
76 class RemoteDataSource : public BnDataSource {
77 public:
78     enum {
79         kBufferSize = 64 * 1024,
80     };
81 
82     static sp<IDataSource> wrap(const sp<DataSource> &source);
83     virtual ~RemoteDataSource();
84 
85     virtual sp<IMemory> getIMemory();
86     virtual ssize_t readAt(off64_t offset, size_t size);
87     virtual status_t getSize(off64_t* size);
88     virtual void close();
89     virtual uint32_t getFlags();
90     virtual String8 toString();
91     virtual sp<DecryptHandle> DrmInitialization(const char *mime);
92 
93 private:
94     sp<IMemory> mMemory;
95     sp<DataSource> mSource;
96     String8 mName;
97     RemoteDataSource(const sp<DataSource> &source);
98     DISALLOW_EVIL_CONSTRUCTORS(RemoteDataSource);
99 };
100 
101 
wrap(const sp<DataSource> & source)102 sp<IDataSource> RemoteDataSource::wrap(const sp<DataSource> &source) {
103     return new RemoteDataSource(source);
104 }
RemoteDataSource(const sp<DataSource> & source)105 RemoteDataSource::RemoteDataSource(const sp<DataSource> &source) {
106     mSource = source;
107     sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "RemoteDataSource");
108     mMemory = memoryDealer->allocate(kBufferSize);
109     if (mMemory == NULL) {
110         ALOGE("Failed to allocate memory!");
111     }
112     mName = String8::format("RemoteDataSource(%s)", mSource->toString().string());
113 }
~RemoteDataSource()114 RemoteDataSource::~RemoteDataSource() {
115     close();
116 }
getIMemory()117 sp<IMemory> RemoteDataSource::getIMemory() {
118     return mMemory;
119 }
readAt(off64_t offset,size_t size)120 ssize_t RemoteDataSource::readAt(off64_t offset, size_t size) {
121     ALOGV("readAt(%" PRId64 ", %zu)", offset, size);
122     return mSource->readAt(offset, mMemory->pointer(), size);
123 }
getSize(off64_t * size)124 status_t RemoteDataSource::getSize(off64_t* size) {
125     return mSource->getSize(size);
126 }
close()127 void RemoteDataSource::close() {
128     mSource = NULL;
129 }
getFlags()130 uint32_t RemoteDataSource::getFlags() {
131     return mSource->flags();
132 }
133 
toString()134 String8 RemoteDataSource::toString() {
135     return mName;
136 }
137 
DrmInitialization(const char * mime)138 sp<DecryptHandle> RemoteDataSource::DrmInitialization(const char *mime) {
139     return mSource->DrmInitialization(mime);
140 }
141 
142 // static
Create(const sp<DataSource> & source,const char * mime)143 sp<IMediaExtractor> MediaExtractor::Create(
144         const sp<DataSource> &source, const char *mime) {
145     ALOGV("MediaExtractor::Create %s", mime);
146 
147     char value[PROPERTY_VALUE_MAX];
148     if (property_get("media.stagefright.extractremote", value, NULL)
149             && (!strcmp("0", value) || !strcasecmp("false", value))) {
150         // local extractor
151         ALOGW("creating media extractor in calling process");
152         return CreateFromService(source, mime);
153     } else {
154         // Check if it's WVM, since WVMExtractor needs to be created in the media server process,
155         // not the extractor process.
156         String8 mime8;
157         float confidence;
158         sp<AMessage> meta;
159         if (SniffWVM(source, &mime8, &confidence, &meta) &&
160                 !strcasecmp(mime8, MEDIA_MIMETYPE_CONTAINER_WVM)) {
161             return new WVMExtractor(source);
162         }
163 
164         // Check if it's es-based DRM, since DRMExtractor needs to be created in the media server
165         // process, not the extractor process.
166         if (SniffDRM(source, &mime8, &confidence, &meta)) {
167             const char *drmMime = mime8.string();
168             ALOGV("Detected media content as '%s' with confidence %.2f", drmMime, confidence);
169             if (!strncmp(drmMime, "drm+es_based+", 13)) {
170                 // DRMExtractor sets container metadata kKeyIsDRM to 1
171                 return new DRMExtractor(source, drmMime + 14);
172             }
173         }
174 
175         // remote extractor
176         ALOGV("get service manager");
177         sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
178 
179         if (binder != 0) {
180             sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
181             sp<IMediaExtractor> ex = mediaExService->makeExtractor(RemoteDataSource::wrap(source), mime);
182             return ex;
183         } else {
184             ALOGE("extractor service not running");
185             return NULL;
186         }
187     }
188     return NULL;
189 }
190 
CreateFromService(const sp<DataSource> & source,const char * mime)191 sp<MediaExtractor> MediaExtractor::CreateFromService(
192         const sp<DataSource> &source, const char *mime) {
193 
194     ALOGV("MediaExtractor::CreateFromService %s", mime);
195     DataSource::RegisterDefaultSniffers();
196 
197     sp<AMessage> meta;
198 
199     String8 tmp;
200     if (mime == NULL) {
201         float confidence;
202         if (!source->sniff(&tmp, &confidence, &meta)) {
203             ALOGV("FAILED to autodetect media content.");
204 
205             return NULL;
206         }
207 
208         mime = tmp.string();
209         ALOGV("Autodetected media content as '%s' with confidence %.2f",
210              mime, confidence);
211     }
212 
213     bool isDrm = false;
214     // DRM MIME type syntax is "drm+type+original" where
215     // type is "es_based" or "container_based" and
216     // original is the content's cleartext MIME type
217     if (!strncmp(mime, "drm+", 4)) {
218         const char *originalMime = strchr(mime+4, '+');
219         if (originalMime == NULL) {
220             // second + not found
221             return NULL;
222         }
223         ++originalMime;
224         if (!strncmp(mime, "drm+es_based+", 13)) {
225             // DRMExtractor sets container metadata kKeyIsDRM to 1
226             return new DRMExtractor(source, originalMime);
227         } else if (!strncmp(mime, "drm+container_based+", 20)) {
228             mime = originalMime;
229             isDrm = true;
230         } else {
231             return NULL;
232         }
233     }
234 
235     MediaExtractor *ret = NULL;
236     if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
237             || !strcasecmp(mime, "audio/mp4")) {
238         ret = new MPEG4Extractor(source);
239     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
240         ret = new MP3Extractor(source, meta);
241     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
242             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
243         ret = new AMRExtractor(source);
244     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
245         ret = new FLACExtractor(source);
246     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
247         ret = new WAVExtractor(source);
248     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
249         ret = new OggExtractor(source);
250     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
251         ret = new MatroskaExtractor(source);
252     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
253         ret = new MPEG2TSExtractor(source);
254     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM) && getuid() == AID_MEDIA) {
255         // Return now.  WVExtractor should not have the DrmFlag set in the block below.
256         return new WVMExtractor(source);
257     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
258         ret = new AACExtractor(source, meta);
259     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
260         ret = new MPEG2PSExtractor(source);
261     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) {
262         ret = new MidiExtractor(source);
263     }
264 
265     if (ret != NULL) {
266        if (isDrm) {
267            ret->setDrmFlag(true);
268        } else {
269            ret->setDrmFlag(false);
270        }
271     }
272 
273     return ret;
274 }
275 
276 }  // namespace android
277