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