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 //#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 26 #include <android_runtime/AndroidRuntime.h> 27 #include <android_util_Binder.h> 28 #include <cutils/properties.h> 29 #include <utils/Log.h> 30 #include <utils/StrongPointer.h> 31 #include <media/IMediaHTTPService.h> 32 #include <media/NdkMediaError.h> 33 #include <media/NdkMediaDataSource.h> 34 #include <media/stagefright/DataSourceFactory.h> 35 #include <media/stagefright/InterfaceUtils.h> 36 #include <mediaplayer2/JavaVMHelper.h> 37 #include <mediaplayer2/JMedia2HTTPService.h> 38 39 #include "../../libstagefright/include/HTTPBase.h" 40 #include "../../libstagefright/include/NuCachedSource2.h" 41 #include "NdkMediaDataSourceCallbacksPriv.h" 42 43 44 using namespace android; 45 46 struct AMediaDataSource { 47 void *userdata; 48 AMediaDataSourceReadAt readAt; 49 AMediaDataSourceGetSize getSize; 50 AMediaDataSourceClose close; 51 AMediaDataSourceGetAvailableSize getAvailableSize; 52 sp<DataSource> mImpl; 53 uint32_t mFlags; 54 }; 55 56 NdkDataSource::NdkDataSource(AMediaDataSource *dataSource) 57 : mDataSource(AMediaDataSource_new()) { 58 AMediaDataSource_setReadAt(mDataSource, dataSource->readAt); 59 AMediaDataSource_setGetSize(mDataSource, dataSource->getSize); 60 AMediaDataSource_setClose(mDataSource, dataSource->close); 61 AMediaDataSource_setUserdata(mDataSource, dataSource->userdata); 62 AMediaDataSource_setGetAvailableSize(mDataSource, dataSource->getAvailableSize); 63 mDataSource->mImpl = dataSource->mImpl; 64 mDataSource->mFlags = dataSource->mFlags; 65 } 66 67 NdkDataSource::~NdkDataSource() { 68 AMediaDataSource_delete(mDataSource); 69 } 70 71 status_t NdkDataSource::initCheck() const { 72 return OK; 73 } 74 75 uint32_t NdkDataSource::flags() { 76 return mDataSource->mFlags; 77 } 78 79 ssize_t NdkDataSource::readAt(off64_t offset, void *data, size_t size) { 80 Mutex::Autolock l(mLock); 81 if (mDataSource->readAt == NULL || mDataSource->userdata == NULL) { 82 return -1; 83 } 84 return mDataSource->readAt(mDataSource->userdata, offset, data, size); 85 } 86 87 status_t NdkDataSource::getSize(off64_t *size) { 88 Mutex::Autolock l(mLock); 89 if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) { 90 return NO_INIT; 91 } 92 if (size != NULL) { 93 *size = mDataSource->getSize(mDataSource->userdata); 94 } 95 return OK; 96 } 97 98 String8 NdkDataSource::toString() { 99 return String8::format("NdkDataSource(pid %d, uid %d)", getpid(), getuid()); 100 } 101 102 String8 NdkDataSource::getMIMEType() const { 103 return String8("application/octet-stream"); 104 } 105 106 void NdkDataSource::close() { 107 if (mDataSource->close != NULL && mDataSource->userdata != NULL) { 108 mDataSource->close(mDataSource->userdata); 109 } 110 } 111 112 status_t NdkDataSource::getAvailableSize(off64_t offset, off64_t *sizeptr) { 113 off64_t size = -1; 114 if (mDataSource->getAvailableSize != NULL 115 && mDataSource->userdata != NULL 116 && sizeptr != NULL) { 117 size = mDataSource->getAvailableSize(mDataSource->userdata, offset); 118 *sizeptr = size; 119 } 120 return size >= 0 ? OK : UNKNOWN_ERROR; 121 } 122 123 static sp<MediaHTTPService> createMediaHttpServiceFromJavaObj(JNIEnv *env, jobject obj, int version) { 124 if (obj == NULL) { 125 return NULL; 126 } 127 switch (version) { 128 case 1: 129 return interface_cast<IMediaHTTPService>(ibinderForJavaObject(env, obj)); 130 case 2: 131 return new JMedia2HTTPService(env, obj); 132 default: 133 return NULL; 134 } 135 } 136 137 static sp<MediaHTTPService> createMediaHttpServiceTemplate( 138 JNIEnv *env, 139 const char *uri, 140 const char *clazz, 141 const char *method, 142 const char *signature, 143 int version) { 144 jobject service = NULL; 145 if (env == NULL) { 146 ALOGE("http service must be created from Java thread"); 147 return NULL; 148 } 149 150 jclass mediahttpclass = env->FindClass(clazz); 151 if (mediahttpclass == NULL) { 152 ALOGE("can't find Media(2)HttpService"); 153 env->ExceptionClear(); 154 return NULL; 155 } 156 157 jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, method, signature); 158 if (mediaHttpCreateMethod == NULL) { 159 ALOGE("can't find method"); 160 env->ExceptionClear(); 161 return NULL; 162 } 163 164 jstring juri = env->NewStringUTF(uri); 165 166 service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, juri); 167 env->DeleteLocalRef(juri); 168 169 env->ExceptionClear(); 170 sp<MediaHTTPService> httpService = createMediaHttpServiceFromJavaObj(env, service, version); 171 return httpService; 172 173 } 174 175 sp<MediaHTTPService> createMediaHttpService(const char *uri, int version) { 176 177 JNIEnv *env; 178 const char *clazz, *method, *signature; 179 180 switch (version) { 181 case 1: 182 env = AndroidRuntime::getJNIEnv(); 183 clazz = "android/media/MediaHTTPService"; 184 method = "createHttpServiceBinderIfNecessary"; 185 signature = "(Ljava/lang/String;)Landroid/os/IBinder;"; 186 break; 187 case 2: 188 env = JavaVMHelper::getJNIEnv(); 189 clazz = "android/media/Media2HTTPService"; 190 method = "createHTTPService"; 191 signature = "(Ljava/lang/String;)Landroid/media/Media2HTTPService;"; 192 break; 193 default: 194 return NULL; 195 } 196 197 return createMediaHttpServiceTemplate(env, uri, clazz, method, signature, version); 198 199 } 200 201 extern "C" { 202 203 EXPORT 204 AMediaDataSource* AMediaDataSource_new() { 205 AMediaDataSource *mSource = new AMediaDataSource(); 206 mSource->userdata = NULL; 207 mSource->readAt = NULL; 208 mSource->getSize = NULL; 209 mSource->close = NULL; 210 return mSource; 211 } 212 213 EXPORT 214 AMediaDataSource* AMediaDataSource_newUri( 215 const char *uri, 216 int numheaders, 217 const char * const *key_values) { 218 219 sp<MediaHTTPService> service = createMediaHttpService(uri, /* version = */ 1); 220 KeyedVector<String8, String8> headers; 221 for (int i = 0; i < numheaders; ++i) { 222 String8 key8(key_values[i * 2]); 223 String8 value8(key_values[i * 2 + 1]); 224 headers.add(key8, value8); 225 } 226 227 sp<DataSource> source = DataSourceFactory::CreateFromURI(service, uri, &headers); 228 if (source == NULL) { 229 ALOGE("AMediaDataSource_newUri source is null"); 230 return NULL; 231 } 232 ALOGI("AMediaDataSource_newUri source %s flags %u", source->toString().c_str(), source->flags()); 233 AMediaDataSource* aSource = convertDataSourceToAMediaDataSource(source); 234 aSource->mImpl = source; 235 aSource->mFlags = source->flags(); 236 return aSource; 237 } 238 239 EXPORT 240 void AMediaDataSource_delete(AMediaDataSource *mSource) { 241 ALOGV("dtor"); 242 if (mSource != NULL) { 243 delete mSource; 244 } 245 } 246 247 EXPORT 248 void AMediaDataSource_setUserdata(AMediaDataSource *mSource, void *userdata) { 249 mSource->userdata = userdata; 250 } 251 252 EXPORT 253 void AMediaDataSource_setReadAt(AMediaDataSource *mSource, AMediaDataSourceReadAt readAt) { 254 mSource->readAt = readAt; 255 } 256 257 EXPORT 258 void AMediaDataSource_setGetSize(AMediaDataSource *mSource, AMediaDataSourceGetSize getSize) { 259 mSource->getSize = getSize; 260 } 261 262 EXPORT 263 void AMediaDataSource_setClose(AMediaDataSource *mSource, AMediaDataSourceClose close) { 264 mSource->close = close; 265 } 266 267 EXPORT 268 void AMediaDataSource_close(AMediaDataSource *mSource) { 269 return mSource->close(mSource->userdata); 270 } 271 272 EXPORT 273 void AMediaDataSource_setGetAvailableSize(AMediaDataSource *mSource, 274 AMediaDataSourceGetAvailableSize getAvailableSize) { 275 mSource->getAvailableSize = getAvailableSize; 276 } 277 278 } // extern "C" 279 280