我是靠谱客的博主 老实裙子,最近开发中收集的这篇文章主要介绍Android10.0Auidio之MediaPlayer (三)正文总结:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

之前说过了MediaPlayer java层的到jni的故事,我们知道了jni是如何跟java层mediaplayer联系上的,包括mediaplayer的初始化,callback的调用等,今天在来简单聊下setDataSource。

正文

MediaPlayer的setDataSource方法主要如下

setDataSource(FileDescriptor)

setDataSource(String)

setDataSource(Context, Uri)

setDataSource(FileDescriptor, long, long)

setDataSource(MediaDataSource)

这里我们就说下这个比较常用的吧setDataSource(String)。传递一个path下来,先看源码


public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
@UnsupportedAppUsage
private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
{
String[] keys = null;
String[] values = null;
if (headers != null) {
keys = new String[headers.size()];
values = new String[headers.size()];
int i = 0;
for (Map.Entry<String, String> entry: headers.entrySet()) {
keys[i] = entry.getKey();
values[i] = entry.getValue();
++i;
}
}
setDataSource(path, keys, values, cookies);
}
@UnsupportedAppUsage
private void setDataSource(String path, String[] keys, String[] values,
List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
}
final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
setDataSource(is.getFD());
}
}

我们发现调到jni的是 setDataSource(is.getFD());


public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
// intentionally less than LONG_MAX
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;

兜兜转转,绕来绕去最终调到了native的_setDataSource,虽然setDataSource的每个方法最终调到native的方法不尽相同,但也大同小异。

我们知道,我们set一个path下来后,其实就是把对应的文件描述符传递了下来。继续分析

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
// 将mp的setDataSource的结果回调到java层
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

将结果通过process_media_player_call回调到上层应用,

关于mp->setDataSource(fd, offset, length)的等下文再说,这里先说jni。那么setDataSource的结果是如何回调上去的呢?我们继续看process_media_player_call

static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) {
// Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else {
// Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) BAD_VALUE ) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
// if the message is too long, don't bother displaying the status code
jniThrowException( env, exception, message);
} else {
char msg[256];
// append the status code to the message
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}

mp->notify(MEDIA_ERROR, opStatus, 0);这个最终就会调到JNIMediaPlayerListener::notify,这个具体可参照Android10.0Auidio之MediaPlayer(二),这里就不再多说了。

void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
//fields.post_event是不是很眼熟,就是上一篇我们说到postEventFromNative
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}

我们发现最终通过postEventFromNative回调上来了,我们继续看下java层的postEventFromNative方法,代码有点长,我节选其中一段吧


if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}

再看下handler是如何处理消息的,都是一些switch/case,这里说下回调上来的MEDIA_ERROR


case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
boolean error_was_handled = false;
OnErrorListener onErrorListener = mOnErrorListener;
if (onErrorListener != null) {
error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
}
{
mOnCompletionInternalListener.onCompletion(mMediaPlayer);
OnCompletionListener onCompletionListener = mOnCompletionListener;
if (onCompletionListener != null && ! error_was_handled) {
onCompletionListener.onCompletion(mMediaPlayer);
}
}
stayAwake(false);
return;

代码不是很多,但是有几点需要注意:

1.我们发现最终通过onErrorListener 或者onCompletionListener回调上去,也就是说我们setOnErrorListener和setOnCompletionListener一定要在setDataSource之前完成,否则就收不到了callback

2.如果报错了,我们不想继续播放可以onError中return true,这样就不会触发onCompletionListener.onCompletion的回调了。

总结:

MediaPlayer的java层跟jni层的交互就差不多了,我们在调用java层的api时其实都是通过jni调到native的,native通过java的方法回调上来。

下一篇我们开始真正分下下native的mediaplayer。

最后

以上就是老实裙子为你收集整理的Android10.0Auidio之MediaPlayer (三)正文总结:的全部内容,希望文章能够帮你解决Android10.0Auidio之MediaPlayer (三)正文总结:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部