Android 13 - Media框架(5)- NuPlayerDriver

2023-11-18

前面的章节中我们了解到上层调用setDataSource后,MediaPlayerService::Client(IMediaPlayer)会调用MediaPlayerFactory创建MediaPlayerBase。Android为我们提供了默认的播放器实现NuPlayer,NuPlayerDriver实现了MediaPlayerBase接口,内部调用并管理有NuPlayer,起着承上启下的作用。
本节代码参考:
frameworks/av/media/libmediaplayerservice/include/MediaPlayerInterface.h
frameworks/av/media/libmediaplayerservice/nuplayer

1、MediaPlayerBase

MediaPlayerBase 是一个抽象类,定义了播放器需要的基本接口,并给出了一些方法的默认实现,如果我们想要实现播放器并且接入MediaPlayer,那么必须要继承于 MediaPlayerBase。

MediaPlayerBase::Listener 是一个内部抽象类,通过它可以将事件上抛给上层 MediaPlayerService::Client,MediaPlayerService::Client 中有 Listener 的具体实现。

MediaPlayerBase::AudioSink 同样是内部抽象类,定义了 Audio Output 所需要的基本接口,这个类用于走AudioTrack / AudioFlinger 进行软件合成输出声音,MediaPlayerService::AudioOutput 是 software output 的具体实现。

MediaPlayerInterface 继承于 MediaPlayerBase,实现了 hardwareOutput 方法,它走的是 software output,需要用到上面提到的 AudioOutput。我们接下来要看的 NuPlayerDriver 继承自于这个类,所以声音走的软件合成。

MediaPlayerHWInterface 也是继承于 MediaPlayerBase,从名字上就可以看出它走的是 hardware output,是没有setAudioSink接口的,具体如何进行硬件合成需要 vendor 自己来实现。

通常来说,我们实现的播放器需要继承于 MediaPlayerInterface / MediaPlayerHWInterface,确定声音走软件还是硬件合成。如果我们的播放器需要两者都支持,也可以直接继承于MediaPlayerBase,或者继承于MediaPlayerInterface。

MediaPlayerBase 提供有基础而全面的播放接口,但是如果我们实现的播放器还有更多的功能,可以使用它提供的 invoke 函数来实现自定义接口的调用。


2、NuPlayerDriver

NuPlayerDriver 会对上层下发的指令进行处理,根据当前的状态调用 NuPlayer 对应的功能。

2.1、NuPlayerDriver调用机制的理解

NuPlayer 内部使用的是 AMessage - AHandler - ALooper 这一套异步消息处理机制,从观察来看播放控制接口,例如 setDataSource、prepare、start、pause、reset 这类播控制接口都是异步调用的(NuPlayer 没有 stop);设定或获取一些参数的接口,例如 getTrackInfo、getSelectedTrack 这类都是同步调用的。

回到 NuPlayerDriver 中来,通过搜索 mCondition.wait 来看哪些接口是同步调用的,查找到 setDataSource、setVideoSurfaceTexture、prepare 和 reset 这四个接口是同步调用的。为什么他们四个是同步调用的,而其他的 start、pause、stop 是异步调用的呢?

我的理解是这样:异步调用的接口会依赖同步调用的4个接口所创建的对象,例如 setDataSource 会创建出 NuPlayer 中的 Source 对象,如果这步是异步的,Source 还没有创建出来就调用 prepare,那就会出现空指针的错误了;又比如 reset 会销毁 MediaPlayerClient 及其内部的对象,如果是异步的,reset处理过程中重新调用其他接口,也是很有可能出现空指针的问题。所以这类会创建或者销毁成员对象的接口必须要进行同步调用!

而 start、pause、stop 这类接口并不会创建或者销毁某些成员变量,不会对前后调用产生影响,调用后会在 ALooper 中按照调用顺序执行,所以异步处理不会有问题。

2.2、NuPlayerDriver中的状态

我们再看下 NuPlayer 中的基本播放控制方法,总共有 setDataSourceAsyncprepareAsyncstartpauseresetAsync 这5个,是没有stop 的,NuPlayer 的 stop 就是用的 pause 来假装的。

接下来再看 NuPlayer 有哪些状态,状态如下:

    enum State {
        STATE_IDLE,
        STATE_SET_DATASOURCE_PENDING,
        STATE_UNPREPARED,
        STATE_PREPARING,
        STATE_PREPARED,
        STATE_RUNNING,
        STATE_PAUSED,
        STATE_RESET_IN_PROGRESS,
        STATE_STOPPED,                  // equivalent to PAUSED
        STATE_STOPPED_AND_PREPARING,    // equivalent to PAUSED, but seeking
        STATE_STOPPED_AND_PREPARED,     // equivalent to PAUSED, but seek complete
    };
  1. STATE_IDLE:MediaPlayerService::Client 调用 setDataSource 刚刚创建 NuPlayerDriver 时状态为 STATE_IDLE,reset 之后状态也变成 STATE_IDLE;
  2. STATE_SET_DATASOURCE_PENDING:调用 MediaPlayerBase setDataSource 后状态变成 STATE_SET_DATASOURCE_PENDING,这个过程可能会比较耗时,所以在等待过程中设置了这个中间状态,如果有人错误使用了多线程调用MediaPlayer,那么这个中间状态将会阻止这个错误调用;
  3. STATE_UNPREPARED:setDataSource 之后的状态置为 STATE_UNPREPARED;
  4. STATE_PREPARING:同样的,prepare过程可能会比较耗时,所以也设置了这个中间状态;prepare 有同步和异步两个版本,同步版本的作用和 SET_DATASOURCE_PENDING 作用相同;异步的版本会标记当前的状态为 preparing,当 prepareAsync 过程中调用 reset 销毁对象时,会直接退出 STATE_PREPARING 状态,进入到 reset 的状态中;
  5. STATE_RUNNING:正在播放的状态,这个状态下 isPlaying 接口返回值为 true;
  6. STATE_PAUSED:暂停状态为STATE_PAUSED,播放结束的状态也是STATE_PAUSED;
  7. STATE_STOPPED:停止播放状态;
  8. STATE_RESET_IN_PROGRESS:reset 的处理过程会将状态置为 STATE_RESET_IN_PROGRESS;
  9. STATE_STOPPED_AND_PREPARING:stop 之后需要重新 prepare 才能继续播放,stop 时资源都没有释放,所以是直接 seek 到文件起始位置播放;但是这里的 preparing 并不会影响 reset 的动作。
  10. STATE_STOPPED_AND_PREPARED:stop 之后 prepare 调用完成,状态置为 STATE_STOPPED_AND_PREPARED。

2.3、NuPlayerDriver中的状态切换

2.3.1、start

status_t NuPlayerDriver::start_l() {
    switch (mState) {
    	// 1、
        case STATE_UNPREPARED:
        {
            status_t err = prepare_l();
            CHECK_EQ(mState, STATE_PREPARED);
            FALLTHROUGH_INTENDED;
        }
        // 2
        case STATE_PAUSED:
        case STATE_STOPPED_AND_PREPARED:
        case STATE_PREPARED:
        {
            mPlayer->start();
            FALLTHROUGH_INTENDED;
        }
        // 3
        case STATE_RUNNING:
        {
            if (mAtEOS) {
                mPlayer->seekToAsync(0);
                mAtEOS = false;
                mPositionUs = -1;
            }
            break;
        }
        default:
            return INVALID_OPERATION;
    }
    mState = STATE_RUNNING;
    return OK;
}

从以上代码可以看出,start 的处理情况有4种:

  1. 当前状态为 STATE_UNPREPARED 调用 start:这种情况应该来说是不会出现的,因为在 MediaPlayer Native 层已经进行了状态处理;
  2. 当前状态为 STATE_PAUSED、STATE_STOPPED_AND_PREPARED、STATE_PREPARED:这三种状态下直接调用 start 即可;如果调用之前出现错误,则seek到0的位置;
  3. 当前状态为 STATE_RUNNING:不做处理;
  4. 其他状态调用 start 返回 INVALID_OPERATION,MediaPlayer Native 层抛出 Error;

2.3.2、pause

status_t NuPlayerDriver::pause() {
    ALOGD("pause(%p)", this);
    int unused;
    getCurrentPosition(&unused);

    Mutex::Autolock autoLock(mLock);

    switch (mState) {
    	// 1
        case STATE_PAUSED:
        case STATE_PREPARED:
            return OK;
		// 2
        case STATE_RUNNING:
            mState = STATE_PAUSED;
            notifyListener_l(MEDIA_PAUSED);
            mPlayer->pause();
            break;

        default:
            return INVALID_OPERATION;
    }

    return OK;
}

pause 的处理情况有3种:

  1. 状态为 STATE_PAUSED、STATE_PREPARED:不做处理;
  2. 状态为 STATE_RUNNING,调用 pause 方法;

2.3.3、stop

status_t NuPlayerDriver::stop() {
    ALOGD("stop(%p)", this);
    Mutex::Autolock autoLock(mLock);

    switch (mState) {
    	// 1
        case STATE_RUNNING:
            mPlayer->pause();
            FALLTHROUGH_INTENDED;
		// 2
        case STATE_PAUSED:
            mState = STATE_STOPPED;
            notifyListener_l(MEDIA_STOPPED);
            break;
		// 3
        case STATE_PREPARED:
        case STATE_STOPPED:
        case STATE_STOPPED_AND_PREPARING:
        case STATE_STOPPED_AND_PREPARED:
            mState = STATE_STOPPED;
            break;
		// 4
        default:
            return INVALID_OPERATION;
    }
    return OK;
}
  1. 状态为 STATE_RUNNING:调用 pause 方法,并将状态置为 STATE_STOPPED;
  2. 状态为 STATE_PAUSED:直接将状态置为 STATE_STOPPED;
  3. 状态为 STATE_PREPARED、STATE_STOPPED、STATE_STOPPED_AND_PREPARING、STATE_STOPPED_AND_PREPARED:这些状态下本就没有开始播放,所以直接将状态置为 STATE_STOPPED;
  4. 其他状态返回 INVALID_OPERATION;

2.3.4、reset

status_t NuPlayerDriver::reset() {
    ALOGD("reset(%p) at state %d", this, mState);

    updateMetrics("reset");
    logMetrics("reset");

    Mutex::Autolock autoLock(mLock);

    switch (mState) {
    	// 1
        case STATE_IDLE:
            return OK;
		// 2
        case STATE_SET_DATASOURCE_PENDING:
        case STATE_RESET_IN_PROGRESS:
            return INVALID_OPERATION;
		// 3
        case STATE_PREPARING:
        {
            CHECK(mIsAsyncPrepare);

            notifyListener_l(MEDIA_PREPARED);
            break;
        }

        default:
            break;
    }

    if (mState != STATE_STOPPED) {
        notifyListener_l(MEDIA_STOPPED);
    }

    mState = STATE_RESET_IN_PROGRESS;
    mPlayer->resetAsync();

    while (mState == STATE_RESET_IN_PROGRESS) {
        mCondition.wait(mLock);
    }
    return OK;
}

reset 的处理情况有3种:

  1. 状态为 STATE_IDLE:直接返回,不需要处理;
  2. 状态为 STATE_SET_DATASOURCE_PENDING、STATE_RESET_IN_PROGRESS:正在 reset 或者说正在初始化,同样也直接返回;
  3. 状态为 STATE_PREPARING:这时候正在 prepare,这时候直接回调上层 MEDIA_PREPARED,如果当前状态不是 stop则还要回调上层当前的状态为 STATE_STOPPED,所以我们上层应用在 prepareAsync、stop 的回调事件中应该判断当前 reset 是否被调用,如果被调用了则不应该 call start 方法,防止快切时出现问题;最后调用resetAsync,等待 reset 完成。

我们要注意的是 MediaPlayerService::Client 和 NuPlayerDriver 的 reset 方法并不会销毁对象,只有等MediaPlayer Native 的 disconnect 调用完成 MediaPlayerService 中的对象才会销毁。

2.3.5、notifyPrepareCompleted

如果在 prepareAsync 的状态下调用 reset,prepare 事件处理完成后会调用回调事件 notifyPrepareCompleted,这里面会怎么处理呢?

void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
    ALOGV("notifyPrepareCompleted %d", err);

    Mutex::Autolock autoLock(mLock);

    if (mState != STATE_PREPARING) {
    	// 直接退出
        CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
        return;
    }

    CHECK_EQ(mState, STATE_PREPARING);

    mAsyncResult = err;

    if (err == OK) {
        mState = STATE_PREPARED;
        if (mIsAsyncPrepare) {
            notifyListener_l(MEDIA_PREPARED);
        }
    } else {
        mState = STATE_UNPREPARED;
        if (mIsAsyncPrepare) {
            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
        }
    }

    sp<MetaData> meta = mPlayer->getFileMeta();
    int32_t loop;
    if (meta != NULL
            && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
        mAutoLoop = true;
    }

    mCondition.broadcast();
}

从代码里可以看到,如果 prepareAsync 完成时,则直接退出回调方法,不会向上 Callback。

2.3.6、MEDIA_PLAYBACK_COMPLETE

视频播放结束后,NuPlayer 会调用 Callback 上抛 MEDIA_PLAYBACK_COMPLETE 事件,如果设置了 Loop 则自动 seek 到开头位置开始播放,否则调用 pause 方法进入暂停的状态,并且将 mAtEOS 设置为 true,最终将 MEDIA_PLAYBACK_COMPLETE 回调给上层。

void NuPlayerDriver::notifyListener_l(
        int msg, int ext1, int ext2, const Parcel *in) {
    switch (msg) {
        case MEDIA_PLAYBACK_COMPLETE:
        {
            if (mState != STATE_RESET_IN_PROGRESS) {
                if (mAutoLoop) {
                    audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
                    if (mAudioSink != NULL) {
                        streamType = mAudioSink->getAudioStreamType();
                    }
                    if (streamType == AUDIO_STREAM_NOTIFICATION) {
                        mAutoLoop = false;
                    }
                }
                // 1、如果有循环播放则seek到开始位置,然后直接返回
                if (mLooping || mAutoLoop) {
                    mPlayer->seekToAsync(0);
                    if (mAudioSink != NULL) {
                        mAudioSink->start();
                    }
                    return;
                }
				// 2、否则进入暂停状态
                mPlayer->pause();
                mState = STATE_PAUSED;
            }
            FALLTHROUGH_INTENDED;
        }

        case MEDIA_ERROR:
        {
            if (msg == MEDIA_ERROR) {
                Mutex::Autolock autoLock(mMetricsLock);
                if (mMetricsItem != NULL) {
                    mMetricsItem->setInt32(kPlayerError, ext1);
                    if (ext2 != 0) {
                        mMetricsItem->setInt32(kPlayerErrorCode, ext2);
                    }
                    mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
                }
            }
            // 3、将 mAtEOS 设置为true,标记当前状态
            mAtEOS = true;
            break;
        }

        default:
            break;
    }

    mLock.unlock();
    // 4、callback到上层
    sendEvent(msg, ext1, ext2, in);
    mLock.lock();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 13 - Media框架(5)- NuPlayerDriver 的相关文章

  • BLE 外设支持 Android-L 示例 [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我希望有一个适用于 Android L 的 BLE 外设模式的示例 我的代码给了我奇怪的错误 即广告商太多 这没有任何意
  • 意图?如何注册以获得传入电子邮件提醒?

    这里是极限 Android 开发新手 好吧 Android 开发新手 而不是一般开发 我想编写一个应用程序 当电子邮件进入设备时会收到通知 并根据电子邮件中的数据 主题 发件人 收件人等 采取各种操作 我认为我要做的是创建一个带有 Inte
  • 添加动态数据时 footable 出现问题

    我需要 jQuery Mobile 方面的一些帮助富表 http css tricks com footable a jquery plugin for responsive data tables 我正在表中动态添加数据 HTML tab
  • Android向后兼容技术

    我现在在开发基于最新 API 15 ICS 的 15 项活动 Android 应用程序方面取得了进展 现在我发现应用程序的主要功能主义者即使支持 android v4 也不向后兼容 例如 1 fragment事务动画 2 将StringSe
  • 在代码中旋转按钮(或其中的文本)

    我必须通过编码随机旋转按钮 或里面的文本 它是相同的 API级别低于11是否有button setRotate x 好吧 看了一下 答案是 很复杂 您可以使用旧的动画框架旋转按钮 例如像这样 Button button Button fin
  • 毕加索磁盘缓存

    我正在使用 Picasso 从 URL 加载图像 Picasso with getApplicationContext load product getImageUrl into imageView 据我所知 每次都会访问该网址 而不是缓存
  • 游标索引越界异常

    打开后出现光标索引越界错误 数据库 请任何人告诉我如何打开现有数据库 sqllite Android 我想在数据库上触发一个选择查询 检索一些信息 public void getPatient SQLiteDatabase db Strin
  • Android CursorAdapter、ListView 和后台线程

    我一直在开发的这个应用程序有包含数兆字节数据的数据库可供筛选 许多活动只是列表视图 通过数据库中的各个级别的数据下降 直到到达 文档 即从数据库中提取并显示在手机上的 HTML 我遇到的问题是 其中一些活动需要能够通过捕获击键并重新运行带有
  • 多语言 Android 应用程序:在电子邮件和密码字段中显示英文键盘

    我们正在开发一款多语言 Android 应用程序 针对英语和阿拉伯语 面临的问题是在登录和注册屏幕中 我们希望仅以英文文本输入用户名和密码字段 从而显示英文键盘 无论设备区域设置语言如何 已尝试在 edittext 中设置 inputtyp
  • 更改 Android 中的媒体音量?

    我可以更改媒体音量吗 如何 到目前为止我用过这个 setVolumeControlStream AudioManager STREAM MUSIC 但有一个搜索栏并且想要更改媒体音量 而不是铃声音量 那么有人可以告诉我如何更改媒体音量onC
  • 如何使用 Swipe 视图实现 Android TabLayout 设计支持库

    我将使用 android TabLayout 设计支持库 但我不知道如何使用滑动视图 这是我的代码 XML
  • Android:BATTERY_STATUS_DISCHARGING 和 BATTERY_STATUS_NOT_CHARGING 之间的区别

    我想知道这两个标志之间的区别 BatteryManager BATTERY STATUS DISCHARGING And BatteryManager BATTERY STATUS NOT CHARGING 我开发了一个使用这两个标志的应用
  • 我应该选择的最低 SDK 版本是多少? (截至2018年11月)

    据我所知 android studio 中默认的最小 SDK 设置是 15 我读到我应该增加它 因为没有多少人 或者可能没有 仍在使用该 android 版本 另外 我计划使用 android studio 中的一些新功能 这些功能仅适用于
  • Android - 多次实例化一个片段?

    我正在创建一个在 ListView 中显示数据的应用程序 数据分为两种类型 热门 收藏夹 我有一个活动和两个片段 片段根据类别显示项目列表 我为此使用了 ListView 然后我有两个fragment layouts 它们在设计上完全相同
  • 从Android客户端登录appengine

    我正在尝试登录应用程序引擎并访问应用程序引擎中的用户服务API 基本上我希望能够看到谁登录了我的 servlet 我正在使用从 android 获取 authtoken 然后从应用程序引擎获取 ASID 或 SACID cookie 的身份
  • SDK尚未初始化,请务必先调用FacebookSdk.sdkInitialize()

    我在实现 Facebook SDK 时遇到此错误 并且我tried https stackoverflow com questions 15490399 error inflating class com facebook widget l
  • Android 4.2 - Environment.getExternalStorageDirectory().getPath() 行为

    我一直在开发一个android应用程序 在上次更新到4 2之前 我使用 Environment getExternalStorageDirectory getPath 它返回了我 storage sdcard0 但自从更新后我现在得到了 s
  • 使用 RecyclerView.Adapter 在 onBindViewHolder() 内设置 onItemClickListener

    我有一个自定义对象 学生班 public class Student private String name private String age public String getName return name public void
  • 查询联系人 - 有时返回空游标

    我正在尝试查询联系人的显示名称 Override public void onActivityResult int requestCode int resultCode Intent data switch requestCode case
  • Android Espresso - 如果未选中,请单击复选框

    I have onView withId R id check box perform click 但我只想在尚未选中该复选框时执行此操作 我怎样才能在浓缩咖啡中做到这一点 我还想根据其之前的状态来切换复选框 开关 起初 我尝试用此方法打开

随机推荐

  • 同态加密的原理详解与go实践

    学习资料来源 知乎VenusBlockChain https zhuanlan zhihu com p 110210315 知乎刘巍然 https www zhihu com question 27645858 answer 3759850
  • 空间注意力机制_计算机视觉中attention机制的理解

    一 前言 简介 对于attention机制的理解 在看了attention is all you need这篇文章和参考网上一些文章之后 做一个简单的理解和总结 在 attention is all you need 的这篇文章中给出了在n
  • PySide6-控件教程-006-QLabel标签控件-信号

    QLabel 标签控件 本文摘录自我的开源教程 PySide6 代码式教程 QLabel CSDN 平台仅做镜像 答疑 纠错请至 GitHub 提交 issue 信号 QLabel的可用信号只有链接被悬停 链接被点击两种 具体如下 link
  • 金融安全视角农民投资理财的实证研究——以X县为例

    金融安全视角农民投资理财的实证研究 以X县为例 摘要 近年来 随着经济全球化进程的推进 国民收入逐年增加 人们的经济意识也在一定程度上提高 适当的投资和财务管理正在迅速发展 然而 大部分调查数据显示 农民面临的诸多问题还有很多其他原因 包括
  • Qt技巧:QTextEdit显示网络图片

    Qt5的QNetworkAccessManager 类可以很方便的访问网络资源 QNetworkRequest类可以用于发送网络请求 而QNetworkReply则负责接收处理网络资源 今天遇到一个问题 如何在QTextEdit上显示一张网
  • 字节跳动(今日头条),为何战斗力如此凶猛?

    本文转载自公众号 中产之路 年前 一位久未联系的朋友问京杭君 有没有研究过今日头条 还有没有上升空间 这位朋友在杭州阿里工作多年 后出来创业 有猎头联系他 今日头条要在杭州成立技术中心 招负责人 那时候 今日头条 还是这间公司最重要的产品
  • 编辑器Vim

    vi简介 vi是 Visual interface 的简称 它在Linux上的地位就仿佛Edit程序在DOS上一样 它可以执行输出 删除 查找 替换 块操作等众多文本操作 而且用户可以根据自己的需要对其进行定制 Vi不是一个排版程序 它不象
  • 蓝牙耳机BES 2300P 主从配对连接,以及主从自定义收发数据

    恒玄SDk预留了用户接口位于app ibrt customif cmd cpp 中 发送数据的前提是进行主从配对连接 sdk给与了两种模式 IBRT SEARCH UI 未定义时我们可以自己定义主从蓝牙地址 IBRT SEARCH UI 定
  • 服务器系统能耗,服务器能耗怎么计算

    服务器能耗怎么计算 内容精选 换一换 DESS磁盘扩容成功后 需要在裸金属服务器的操作系统中对扩容部分的磁盘分配分区 已登录裸金属服务器 详细操作请参见 裸金属服务器用户指南 中章节 登录Windows裸金属服务器 已挂载磁盘至裸金属服务器
  • JS实现约瑟夫环

    传说罗马人占领了乔塔帕特 41 个犹太人被围堵在一个山洞里 他们拒绝被俘虏 而决定集体自杀 大家决定了一个自杀方案 41 个人围成一个圈 由第 1 个人开始顺时针报数 每报数为 3 的人立刻自杀 然后再由下一个人重新从 1 开始报数 依旧是
  • 笔记本电脑微信视频对方却听不到声音

    我真的是把网上的所有教程 试遍了都没弄好 我自己突发奇想要不然更新下 结果就成功了 首先可以先看看是不是微信版本的原因 麦克风出现问题了 右击我的电脑 gt 属性 gt 设备管理器 gt 音频输入和输出 gt 右击麦克风 gt 更新驱动程序
  • 数值分析Matlab二维正态(高斯)分布以及协方差矩阵

    数值分析Matlab二维正态 高斯 分布以及协方差矩阵 主要是使用了matlab的mvnrnd产生随机的正态 高斯 分布二维矩阵 然后绘制出来 代码运行结果生成的正态分布实验数据如图 MATLAB代码 mu1 0 0 sigma1 4 2
  • ACM输入输出

    写在前面 主要记录一下ACM输入输出的写法 一 输入数值 1 给定N的定长多行输入 题目 https ac nowcoder com acm contest 5657 B 代码 include
  • 【Java】基于朴素贝叶斯算法破解基于哈希表的随机字符替换加密算法

    Java 基于朴素贝叶斯算法破解基于哈希表的随机字符替换加密算法 不用看了 这篇文章是错的 得出的结果也不是正确的结果 我想错了 因为这个解密算法的前提是已经知道哈希表的情况下去计算的 而实际上应该是只靠统计去分析密文 所以实际破解所需要的
  • Unity模型导入相关知识

    文章目录 常见的模型格式 模型导入参数设置 Model页签 Scene设置 Meshes设置 Geometry设置 Rig页签 Animation Type 动画类型 Avatar化身信息设置 Animation页签 基础信息设置 动画剪辑
  • 童年回忆——切水果(内含源码inscode一键运行)

    前言 作者主页 雪碧有白泡泡 个人网站 雪碧的个人网站 推荐专栏 java一站式服务 React从入门到精通 前端炫酷代码分享 从0到英雄 vue成神之路 uniapp 从构建到提升 从0到英雄 vue成神之路 解决算法 一个专栏就够了 架
  • vue项目中使用pdf.js预览pdf文件

    项目要求需要预览pdf文件 网上找了很久 大多数都是推荐pdf js 自己起了解了一下 最后决定用pdf js 但是发现 在vue中使用这个很少 所以我就写这一篇帮助一下vue使用pdfjs的朋友 其实 这和前端框架无关的 直接使用pdf
  • 网络模拟(Network_simulation)

    https en wikipedia org wiki Network simulation In computer network research network simulation is a technique whereby a
  • CocoaPods创建管理类库的步骤

    用到的命令 1 git clone https github com jackLeong MySDK git 克隆一份远程仓库 2 git add fileName 把文件纳入git的缓存区 可使用 代替目录下的所有文件 3 git sta
  • Android 13 - Media框架(5)- NuPlayerDriver

    前面的章节中我们了解到上层调用setDataSource后 MediaPlayerService Client IMediaPlayer 会调用MediaPlayerFactory创建MediaPlayerBase Android为我们提供