• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017, 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 "RemoteMediaExtractor"
19 
20 #include <list>
21 #include <pthread.h>
22 #include <condition_variable>
23 #include <mutex>
24 
25 #include <utils/Log.h>
26 
27 #include <binder/IPCThreadState.h>
28 #include <cutils/properties.h>
29 #include <media/stagefright/InterfaceUtils.h>
30 #include <media/MediaMetricsItem.h>
31 #include <media/stagefright/MediaSource.h>
32 #include <media/stagefright/RemoteMediaExtractor.h>
33 
34 // still doing some on/off toggling here.
35 #define MEDIA_LOG       1
36 
37 namespace android {
38 
39 // key for media statistics
40 static const char *kKeyExtractor = "extractor";
41 
42 // attrs for media statistics
43 // NB: these are matched with public Java API constants defined
44 // in frameworks/base/media/java/android/media/MediaExtractor.java
45 // These must be kept synchronized with the constants there.
46 static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
47 static const char *kExtractorMime = "android.media.mediaextractor.mime";
48 static const char *kExtractorTracks = "android.media.mediaextractor.ntrk";
49 
50 // The following are not available in frameworks/base/media/java/android/media/MediaExtractor.java
51 // because they are not applicable or useful to that API.
52 static const char *kExtractorEntryPoint = "android.media.mediaextractor.entry";
53 static const char *kExtractorLogSessionId = "android.media.mediaextractor.logSessionId";
54 
55 static const char *kEntryPointSdk = "sdk";
56 static const char *kEntryPointWithJvm = "ndk-with-jvm";
57 static const char *kEntryPointNoJvm = "ndk-no-jvm";
58 static const char *kEntryPointOther = "other";
59 
RemoteMediaExtractor(MediaExtractor * extractor,const sp<DataSource> & source,const sp<RefBase> & plugin)60 RemoteMediaExtractor::RemoteMediaExtractor(
61         MediaExtractor *extractor,
62         const sp<DataSource> &source,
63         const sp<RefBase> &plugin)
64     :mExtractor(extractor),
65      mSource(source),
66      mExtractorPlugin(plugin) {
67 
68     mMetricsItem = nullptr;
69     if (MEDIA_LOG) {
70         mMetricsItem = mediametrics::Item::create(kKeyExtractor);
71 
72         // we're in the extractor service, we want to attribute to the app
73         // that invoked us.
74         int uid = IPCThreadState::self()->getCallingUid();
75         mMetricsItem->setUid(uid);
76 
77         // track the container format (mpeg, aac, wvm, etc)
78         size_t ntracks = extractor->countTracks();
79         mMetricsItem->setCString(kExtractorFormat, extractor->name());
80         // tracks (size_t)
81         mMetricsItem->setInt32(kExtractorTracks, ntracks);
82         // metadata
83         MetaDataBase pMetaData;
84         if (extractor->getMetaData(pMetaData) == OK) {
85             String8 xx = pMetaData.toString();
86             // 'titl' -- but this verges into PII
87             // 'mime'
88             const char *mime = nullptr;
89             if (pMetaData.findCString(kKeyMIMEType, &mime)) {
90                 mMetricsItem->setCString(kExtractorMime,  mime);
91             }
92             // what else is interesting and not already available?
93         }
94         // By default, we set the entry point to be "other". Clients of this
95         // class will override this value by calling setEntryPoint.
96         mMetricsItem->setCString(kExtractorEntryPoint, kEntryPointOther);
97     }
98 }
99 
100 static pthread_t myThread;
101 static std::list<sp<DataSource>> pending;
102 static std::mutex pending_mutex;
103 static std::condition_variable pending_added;
104 
closing_thread_func(void * arg)105 static void* closing_thread_func(void *arg) {
106     while (true) {
107         sp<DataSource> ds = nullptr;
108         std::unique_lock _lk(pending_mutex);
109         pending_added.wait(_lk, []{return !pending.empty();});
110         ALOGV("worker thread wake up with %zu entries", pending.size());
111         if (!pending.empty()) {
112             ds = pending.front();
113             (void) pending.pop_front();
114         }
115         _lk.unlock();       // unique_lock is not scoped
116         if (ds != nullptr) {
117             ds->close();
118         }
119     }
120 
121     ALOGE("[unexpected] worker thread quit");
122     return arg;
123 }
124 
125 // this can be '&ds' as long as the pending.push_back() bumps the
126 // reference counts to ensure the object lives long enough
start_close_thread(sp<DataSource> & ds)127 static void start_close_thread(sp<DataSource> &ds) {
128 
129     // make sure we have our (single) worker thread
130     static std::once_flag sCheckOnce;
131     std::call_once(sCheckOnce, [&](){
132         pthread_attr_t attr;
133         pthread_attr_init(&attr);
134         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
135         pthread_create(&myThread, &attr, closing_thread_func, nullptr);
136         pthread_attr_destroy(&attr);
137     });
138 
139     {
140         std::lock_guard _lm(pending_mutex);     // scoped, no explicit unlock
141         pending.push_back(ds);
142     }
143     pending_added.notify_one();     // get the worker thread going
144 }
145 
~RemoteMediaExtractor()146 RemoteMediaExtractor::~RemoteMediaExtractor() {
147     delete mExtractor;
148     // TODO(287851984) hook for changing behavior this dynamically, drop after testing
149     int8_t new_scheme = property_get_bool("debug.mediaextractor.delayedclose", 1);
150     if (new_scheme != 0) {
151         ALOGV("deferred close()");
152         start_close_thread(mSource);
153         mSource.clear();
154     } else {
155         ALOGV("immediate close()");
156         mSource->close();
157         mSource.clear();
158     }
159     mExtractorPlugin = nullptr;
160     // log the current record, provided it has some information worth recording
161     if (MEDIA_LOG) {
162         if (mMetricsItem != nullptr) {
163             if (mMetricsItem->count() > 0) {
164                 mMetricsItem->selfrecord();
165             }
166         }
167     }
168     if (mMetricsItem != nullptr) {
169         delete mMetricsItem;
170         mMetricsItem = nullptr;
171     }
172 }
173 
countTracks()174 size_t RemoteMediaExtractor::countTracks() {
175     return mExtractor->countTracks();
176 }
177 
getTrack(size_t index)178 sp<IMediaSource> RemoteMediaExtractor::getTrack(size_t index) {
179     MediaTrack *source = mExtractor->getTrack(index);
180     return (source == nullptr)
181             ? nullptr : CreateIMediaSourceFromMediaSourceBase(this, source, mExtractorPlugin);
182 }
183 
getTrackMetaData(size_t index,uint32_t flags)184 sp<MetaData> RemoteMediaExtractor::getTrackMetaData(size_t index, uint32_t flags) {
185     sp<MetaData> meta = new MetaData();
186     if (mExtractor->getTrackMetaData(*meta.get(), index, flags) == OK) {
187         return meta;
188     }
189     return nullptr;
190 }
191 
getMetaData()192 sp<MetaData> RemoteMediaExtractor::getMetaData() {
193     sp<MetaData> meta = new MetaData();
194     if (mExtractor->getMetaData(*meta.get()) == OK) {
195         return meta;
196     }
197     return nullptr;
198 }
199 
getMetrics(Parcel * reply)200 status_t RemoteMediaExtractor::getMetrics(Parcel *reply) {
201     if (mMetricsItem == nullptr || reply == nullptr) {
202         return UNKNOWN_ERROR;
203     }
204 
205     mMetricsItem->writeToParcel(reply);
206     return OK;
207 }
208 
flags() const209 uint32_t RemoteMediaExtractor::flags() const {
210     return mExtractor->flags();
211 }
212 
setMediaCas(const HInterfaceToken & casToken)213 status_t RemoteMediaExtractor::setMediaCas(const HInterfaceToken &casToken) {
214     return mExtractor->setMediaCas((uint8_t*)casToken.data(), casToken.size());
215 }
216 
name()217 String8 RemoteMediaExtractor::name() {
218     return String8(mExtractor->name());
219 }
220 
setEntryPoint(EntryPoint entryPoint)221 status_t RemoteMediaExtractor::setEntryPoint(EntryPoint entryPoint) {
222     const char* entryPointString;
223     switch (entryPoint) {
224       case EntryPoint::SDK:
225             entryPointString = kEntryPointSdk;
226             break;
227         case EntryPoint::NDK_WITH_JVM:
228             entryPointString = kEntryPointWithJvm;
229             break;
230         case EntryPoint::NDK_NO_JVM:
231             entryPointString = kEntryPointNoJvm;
232             break;
233         case EntryPoint::OTHER:
234             entryPointString = kEntryPointOther;
235             break;
236         default:
237             return BAD_VALUE;
238     }
239     mMetricsItem->setCString(kExtractorEntryPoint, entryPointString);
240     return OK;
241 }
242 
setLogSessionId(const String8 & logSessionId)243 status_t RemoteMediaExtractor::setLogSessionId(const String8& logSessionId) {
244     mMetricsItem->setCString(kExtractorLogSessionId, logSessionId.c_str());
245     return OK;
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 
250 // static
wrap(MediaExtractor * extractor,const sp<DataSource> & source,const sp<RefBase> & plugin)251 sp<IMediaExtractor> RemoteMediaExtractor::wrap(
252         MediaExtractor *extractor,
253         const sp<DataSource> &source,
254         const sp<RefBase> &plugin) {
255     if (extractor == nullptr) {
256         return nullptr;
257     }
258     return new RemoteMediaExtractor(extractor, source, plugin);
259 }
260 
261 }  // namespace android
262