一,从setDataSource开始,设置播放的数据源,可以时本地视频,也可以是网络链接
EnjoyPlayer.java
private String mPath = "/sdcard/mpeg.mp4";
public void setDataSource(String path) {
setDataSource(nativeHandler, path);
}
EnjoyPlayer.cpp中的 setDataSource,只是简单记录下path,已被prepare使用:
void EnjoyPlayer::setDataSource(const char *path_) {
//C语言的实现,这里地址用自己的成员属性记录,如果直接赋值给path,当path_被释放后,
// path的指向也就无效了,所以这里做一个深拷贝的操作,自己去为指针申请一段内存。
// path = static_cast<char *>(malloc(strlen(path_) + 1));
// memset((void *)path, 0, strlen(path)+1);
// memcpy((void *)path, (void *)path_, strlen(path_));
// C++的实现方式
path = new char[strlen(path_) +1];
strcpy(path, path_);
}
//解析媒体信息,放在一个单独的线程里面执行,比如要解析的是一个来自网络的数据,
void EnjoyPlayer::prepare() {
pthread_create(&prepareTask, 0, prepare_t, this);
}
媒体信息的处理过程:
1,avFormatContext用来保存,打开的媒体文件的构成及上下文信息。
2, 查找媒体流,获取音视频流。注意这里返回值大于等于0表示成功。
3, 针对音频流,视频流,分别获取解码器,虽然ACCodec叫做解码器,但是实际我们解码时并没有直接使用,解码时实际使用的是AVCodecContext,即解码信息上下文,
4,打开解码器
5,通过 AudioChannel,VideoChannel 对音频流,视频流分别处理
6, prepare完成后,通知java层,可以播放了,
void EnjoyPlayer::_prepare() {
//avFormatContext用来保存,打开的媒体文件的构成及上下文信息。
avFormatContext = avformat_alloc_context();
//第一个参数,是一个二级指针,可以修改外部实参,
// 第三个参数表示文件的封装格式avi,flv等,如果传nullptr,会自定识别,
// 第四个参数,表示一个配置信息,比如打开网络文件时,可以指定超时时间
//AVDictionary *options;
//av_dict_set(&options, "timeout", "3000000", 0);
int ret = avformat_open_input(&avFormatContext, path, nullptr, 0);
//打开文件失败,获取失败信息,
if (0 != ret) {
char *msg = av_err2str(ret);
LOGE("打开%s 失败,返回 %d ,错误描述 %s 。", path, ret, msg);
helper->onError(FFMPEG_CAN_NOT_OPEN_URL, THREAD_CHILD);
goto ERROR;
}
//查找媒体流,获取音视频流。注意这里返回值大于等于0表示成功。
ret = avformat_find_stream_info(avFormatContext, 0);
if (ret < 0) {
LOGE("查找媒体流 %s 失败,返回:%d 错误描述:%s", path, ret, av_err2str(ret));
helper->onError(FFMPEG_CAN_NOT_FIND_STREAMS, THREAD_CHILD);
goto ERROR;
}
//获取视频时长,默认返回值单位为微妙,这里转成秒数。
duration = avFormatContext->duration/AV_TIME_BASE;
//获取媒体文件中的媒体流(音频流,视频流)
for (int i = 0; i < avFormatContex