blob: 0891f2a5a92005c1ab42a9d9beaa403653f33a85 [file] [log] [blame]
Robert Shih0df451b2017-12-08 14:16:50 -08001/*
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_NDEBUG 0
18#define LOG_TAG "NdkMediaDataSource"
19
20#include "NdkMediaDataSourcePriv.h"
21
22#include <inttypes.h>
23#include <jni.h>
24#include <unistd.h>
25
Jooyung Han99f4a732019-02-21 15:27:38 +090026#include <binder/IBinder.h>
Robert Shih0df451b2017-12-08 14:16:50 -080027#include <cutils/properties.h>
28#include <utils/Log.h>
29#include <utils/StrongPointer.h>
Robert Shih730af222018-09-14 14:02:57 -070030#include <media/IMediaHTTPService.h>
Robert Shih0df451b2017-12-08 14:16:50 -080031#include <media/NdkMediaError.h>
32#include <media/NdkMediaDataSource.h>
Robert Shih2568eb92018-09-19 10:00:46 -070033#include <media/stagefright/DataSourceFactory.h>
Robert Shih0df451b2017-12-08 14:16:50 -080034#include <media/stagefright/InterfaceUtils.h>
Robert Shih730af222018-09-14 14:02:57 -070035#include <mediaplayer2/JavaVMHelper.h>
36#include <mediaplayer2/JMedia2HTTPService.h>
Robert Shih0df451b2017-12-08 14:16:50 -080037
38#include "../../libstagefright/include/HTTPBase.h"
39#include "../../libstagefright/include/NuCachedSource2.h"
Robert Shih2568eb92018-09-19 10:00:46 -070040#include "NdkMediaDataSourceCallbacksPriv.h"
Robert Shih0df451b2017-12-08 14:16:50 -080041
Jooyung Han99f4a732019-02-21 15:27:38 +090042#include <mutex> // std::call_once,once_flag
43#include <dlfcn.h> // dlopen
44
Robert Shih0df451b2017-12-08 14:16:50 -080045using namespace android;
46
Jooyung Han99f4a732019-02-21 15:27:38 +090047// load libandroid_runtime.so lazily.
48// A vendor process may use libmediandk but should not depend on libandroid_runtime.
49// TODO(jooyung): remove duplicate (b/125550121)
50// frameworks/native/libs/binder/ndk/ibinder_jni.cpp
51namespace {
52
53typedef JNIEnv* (*getJNIEnv_t)();
54typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
55
56getJNIEnv_t getJNIEnv_;
57ibinderForJavaObject_t ibinderForJavaObject_;
58
59std::once_flag mLoadFlag;
60
61void load() {
62 std::call_once(mLoadFlag, []() {
63 void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
64 if (handle == nullptr) {
65 ALOGE("Could not open libandroid_runtime.");
66 return;
67 }
68
69 getJNIEnv_ = reinterpret_cast<getJNIEnv_t>(
70 dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv"));
71 if (getJNIEnv_ == nullptr) {
72 ALOGE("Could not find AndroidRuntime::getJNIEnv.");
73 // no return
74 }
75
76 ibinderForJavaObject_ = reinterpret_cast<ibinderForJavaObject_t>(
77 dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
78 if (ibinderForJavaObject_ == nullptr) {
79 ALOGE("Could not find ibinderForJavaObject.");
80 // no return
81 }
82 });
83}
84
85JNIEnv* getJNIEnv() {
86 load();
87 if (getJNIEnv_ == nullptr) {
88 return nullptr;
89 }
90 return (getJNIEnv_)();
91}
92
93sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
94 load();
95 if (ibinderForJavaObject_ == nullptr) {
96 return nullptr;
97 }
98 return (ibinderForJavaObject_)(env, obj);
99}
100
101} // namespace
102
Robert Shih0df451b2017-12-08 14:16:50 -0800103struct AMediaDataSource {
104 void *userdata;
105 AMediaDataSourceReadAt readAt;
106 AMediaDataSourceGetSize getSize;
Robert Shih45b88d22018-02-05 13:52:39 -0800107 AMediaDataSourceClose close;
Robert Shihca198ce2018-09-22 16:31:55 -0700108 AMediaDataSourceGetAvailableSize getAvailableSize;
Robert Shiha601f4a2018-10-01 15:10:35 -0700109 sp<DataSource> mImpl;
110 uint32_t mFlags;
Robert Shih0df451b2017-12-08 14:16:50 -0800111};
112
113NdkDataSource::NdkDataSource(AMediaDataSource *dataSource)
Robert Shihd4faf9e2018-01-21 17:52:25 -0800114 : mDataSource(AMediaDataSource_new()) {
Robert Shiha601f4a2018-10-01 15:10:35 -0700115 AMediaDataSource_setReadAt(mDataSource, dataSource->readAt);
116 AMediaDataSource_setGetSize(mDataSource, dataSource->getSize);
117 AMediaDataSource_setClose(mDataSource, dataSource->close);
118 AMediaDataSource_setUserdata(mDataSource, dataSource->userdata);
Robert Shihea388862018-11-20 02:11:30 -0800119 AMediaDataSource_setGetAvailableSize(mDataSource, dataSource->getAvailableSize);
Robert Shiha601f4a2018-10-01 15:10:35 -0700120 mDataSource->mImpl = dataSource->mImpl;
121 mDataSource->mFlags = dataSource->mFlags;
Robert Shihd4faf9e2018-01-21 17:52:25 -0800122}
123
124NdkDataSource::~NdkDataSource() {
125 AMediaDataSource_delete(mDataSource);
Robert Shih0df451b2017-12-08 14:16:50 -0800126}
127
128status_t NdkDataSource::initCheck() const {
129 return OK;
130}
131
Robert Shiha601f4a2018-10-01 15:10:35 -0700132uint32_t NdkDataSource::flags() {
133 return mDataSource->mFlags;
134}
135
Robert Shih0df451b2017-12-08 14:16:50 -0800136ssize_t NdkDataSource::readAt(off64_t offset, void *data, size_t size) {
137 Mutex::Autolock l(mLock);
Robert Shiha601f4a2018-10-01 15:10:35 -0700138 if (mDataSource->readAt == NULL || mDataSource->userdata == NULL) {
Robert Shih0df451b2017-12-08 14:16:50 -0800139 return -1;
140 }
141 return mDataSource->readAt(mDataSource->userdata, offset, data, size);
142}
143
144status_t NdkDataSource::getSize(off64_t *size) {
145 Mutex::Autolock l(mLock);
146 if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) {
147 return NO_INIT;
148 }
149 if (size != NULL) {
150 *size = mDataSource->getSize(mDataSource->userdata);
151 }
152 return OK;
153}
154
155String8 NdkDataSource::toString() {
156 return String8::format("NdkDataSource(pid %d, uid %d)", getpid(), getuid());
157}
158
159String8 NdkDataSource::getMIMEType() const {
160 return String8("application/octet-stream");
161}
162
Robert Shih45b88d22018-02-05 13:52:39 -0800163void NdkDataSource::close() {
164 if (mDataSource->close != NULL && mDataSource->userdata != NULL) {
165 mDataSource->close(mDataSource->userdata);
166 }
167}
168
Robert Shihca198ce2018-09-22 16:31:55 -0700169status_t NdkDataSource::getAvailableSize(off64_t offset, off64_t *sizeptr) {
170 off64_t size = -1;
171 if (mDataSource->getAvailableSize != NULL
172 && mDataSource->userdata != NULL
173 && sizeptr != NULL) {
174 size = mDataSource->getAvailableSize(mDataSource->userdata, offset);
175 *sizeptr = size;
176 }
177 return size >= 0 ? OK : UNKNOWN_ERROR;
178}
179
Robert Shih730af222018-09-14 14:02:57 -0700180static sp<MediaHTTPService> createMediaHttpServiceFromJavaObj(JNIEnv *env, jobject obj, int version) {
181 if (obj == NULL) {
182 return NULL;
183 }
Jooyung Han99f4a732019-02-21 15:27:38 +0900184 sp<IBinder> binder;
Robert Shih730af222018-09-14 14:02:57 -0700185 switch (version) {
186 case 1:
Jooyung Han99f4a732019-02-21 15:27:38 +0900187 binder = ibinderForJavaObject(env, obj);
188 if (binder == NULL) {
189 return NULL;
190 }
191 return interface_cast<IMediaHTTPService>(binder);
Robert Shih730af222018-09-14 14:02:57 -0700192 case 2:
193 return new JMedia2HTTPService(env, obj);
194 default:
195 return NULL;
196 }
197}
198
199static sp<MediaHTTPService> createMediaHttpServiceTemplate(
200 JNIEnv *env,
201 const char *uri,
202 const char *clazz,
203 const char *method,
204 const char *signature,
205 int version) {
206 jobject service = NULL;
207 if (env == NULL) {
208 ALOGE("http service must be created from Java thread");
209 return NULL;
210 }
211
212 jclass mediahttpclass = env->FindClass(clazz);
213 if (mediahttpclass == NULL) {
214 ALOGE("can't find Media(2)HttpService");
215 env->ExceptionClear();
216 return NULL;
217 }
218
219 jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, method, signature);
220 if (mediaHttpCreateMethod == NULL) {
221 ALOGE("can't find method");
222 env->ExceptionClear();
223 return NULL;
224 }
225
226 jstring juri = env->NewStringUTF(uri);
227
228 service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, juri);
229 env->DeleteLocalRef(juri);
230
231 env->ExceptionClear();
232 sp<MediaHTTPService> httpService = createMediaHttpServiceFromJavaObj(env, service, version);
233 return httpService;
234
235}
236
237sp<MediaHTTPService> createMediaHttpService(const char *uri, int version) {
238
239 JNIEnv *env;
240 const char *clazz, *method, *signature;
241
242 switch (version) {
243 case 1:
Jooyung Han99f4a732019-02-21 15:27:38 +0900244 env = getJNIEnv();
Robert Shih730af222018-09-14 14:02:57 -0700245 clazz = "android/media/MediaHTTPService";
246 method = "createHttpServiceBinderIfNecessary";
247 signature = "(Ljava/lang/String;)Landroid/os/IBinder;";
248 break;
249 case 2:
250 env = JavaVMHelper::getJNIEnv();
251 clazz = "android/media/Media2HTTPService";
252 method = "createHTTPService";
253 signature = "(Ljava/lang/String;)Landroid/media/Media2HTTPService;";
254 break;
255 default:
256 return NULL;
257 }
258
259 return createMediaHttpServiceTemplate(env, uri, clazz, method, signature, version);
260
261}
262
Robert Shih0df451b2017-12-08 14:16:50 -0800263extern "C" {
264
265EXPORT
266AMediaDataSource* AMediaDataSource_new() {
267 AMediaDataSource *mSource = new AMediaDataSource();
268 mSource->userdata = NULL;
269 mSource->readAt = NULL;
270 mSource->getSize = NULL;
Robert Shih45b88d22018-02-05 13:52:39 -0800271 mSource->close = NULL;
Robert Shih0df451b2017-12-08 14:16:50 -0800272 return mSource;
273}
274
275EXPORT
Robert Shih2568eb92018-09-19 10:00:46 -0700276AMediaDataSource* AMediaDataSource_newUri(
277 const char *uri,
278 int numheaders,
279 const char * const *key_values) {
280
281 sp<MediaHTTPService> service = createMediaHttpService(uri, /* version = */ 1);
282 KeyedVector<String8, String8> headers;
283 for (int i = 0; i < numheaders; ++i) {
284 String8 key8(key_values[i * 2]);
285 String8 value8(key_values[i * 2 + 1]);
286 headers.add(key8, value8);
287 }
288
289 sp<DataSource> source = DataSourceFactory::CreateFromURI(service, uri, &headers);
Robert Shih49fb89d2018-01-31 17:53:19 -0800290 if (source == NULL) {
291 ALOGE("AMediaDataSource_newUri source is null");
292 return NULL;
293 }
294 ALOGI("AMediaDataSource_newUri source %s flags %u", source->toString().c_str(), source->flags());
Robert Shiha601f4a2018-10-01 15:10:35 -0700295 AMediaDataSource* aSource = convertDataSourceToAMediaDataSource(source);
296 aSource->mImpl = source;
297 aSource->mFlags = source->flags();
298 return aSource;
Robert Shih2568eb92018-09-19 10:00:46 -0700299}
300
301EXPORT
Robert Shih0df451b2017-12-08 14:16:50 -0800302void AMediaDataSource_delete(AMediaDataSource *mSource) {
303 ALOGV("dtor");
304 if (mSource != NULL) {
305 delete mSource;
306 }
307}
308
309EXPORT
310void AMediaDataSource_setUserdata(AMediaDataSource *mSource, void *userdata) {
311 mSource->userdata = userdata;
312}
313
314EXPORT
315void AMediaDataSource_setReadAt(AMediaDataSource *mSource, AMediaDataSourceReadAt readAt) {
316 mSource->readAt = readAt;
317}
318
319EXPORT
320void AMediaDataSource_setGetSize(AMediaDataSource *mSource, AMediaDataSourceGetSize getSize) {
321 mSource->getSize = getSize;
322}
323
Robert Shih45b88d22018-02-05 13:52:39 -0800324EXPORT
325void AMediaDataSource_setClose(AMediaDataSource *mSource, AMediaDataSourceClose close) {
326 mSource->close = close;
327}
328
Robert Shihdabe9d92018-09-22 16:18:05 -0700329EXPORT
330void AMediaDataSource_close(AMediaDataSource *mSource) {
331 return mSource->close(mSource->userdata);
332}
333
Robert Shihca198ce2018-09-22 16:31:55 -0700334EXPORT
335void AMediaDataSource_setGetAvailableSize(AMediaDataSource *mSource,
336 AMediaDataSourceGetAvailableSize getAvailableSize) {
337 mSource->getAvailableSize = getAvailableSize;
338}
339
Robert Shih0df451b2017-12-08 14:16:50 -0800340} // extern "C"
341