需求:websocket 接收byte[] h264裸流,传输到c层使用ijkplayer 播放
ffmpeg播放H264裸流
功能已实现,为了方便,读取本地文件做为源数据,此工程亦可播放mp4之类文件
https://github.com/Malone1023/JoyPlayer
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
该函数支持filename/URL,在不大改的情况下,有两种方式可以完成播裸流的工作
1.管道文件
2.AndoidIO协议/新增URLProtocol自定义
AndoidIO之前的文章已经介绍过,本文使用管道文件来完成
注释openVideo()方法如下代码,catch exception同时注释
String scheme = mUri.getScheme();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && mSettings.getUsingMediaDataSource() && (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
mMediaPlayer.setDataSource(dataSource);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
} else {
mMediaPlayer.setDataSource(mUri.toString());
}
新增setDataSourceFd()方法并且在openVideo中调用,使得调用ijk中的 setDataSource(FileDescriptor fd)方法,并调用到
ijkplayer_jni.c中的static void IjkMediaPlayer_setDataSourceFd(JNIEnv *env, jobject thiz, jint fd2)方法
String fileName = "/mnt/sdcard/yy.264";
FileDescriptor descriptor = null;
FileOutputStream stream;
private void setDataSourceFd(){
try {
stream = new FileOutputStream(fileName);
descriptor = stream.getFD();
//此处只是为了调用到setDataSourceFd 并不需要实际文件
mMediaPlayer.setDataSource(descriptor);
}catch (Exception e){
e.printStackTrace();
}
}
2.修改ijkplayer_jni.c/IjkMediaPlayer_setDataSourceFd方法
新增:
umask(0);
if(mkfifo(fifoName, 0664) < 0){
if(errno == EEXIST){
MPTRACE("file exits\n");
}else{
perror("mkfifo error");
return -1;
}
}
int fd = open(fifoName, O_RDWR);
if(fd < 0){
MPTRACE("open fifo error\n");
perror("open fifo error");
return -1;
}
修改:
retval = ijkmp_set_data_source(mp, fifoName);
3.新增接收方法
int fd;
void ffp_write_data(FFPlayer *ffp,unsigned char * data,int len){
if(!fd){
fd = open(fifoName, O_WRONLY);
if(fd < 0){
perror("ffp_joy_write_data fifo error");
return -1;
}
}
write(fd,data,len);
}
这样 ijkPlayer就可以支持播放h264裸流啦。
可能遇到的问题:
1.ffmpeg h264 avformat_open_input err code: -1094995529 网上找的时候说各种情况的,本文遇到的问题是没打开ffmpeg对h264的支持(出现此问题可以先拿ffmpeg直接播该文件,能正常播放的话说明是跟我遇到的一样)
解决办法:
do-compile-ffmpeg.sh文件中新增如下配置,并重新编译ijk的ffmpeg库(执行compile-ffmpeg.sh,同时需要编译ijk的库,替换)
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=h264"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-protocol=udp"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-protocol=264"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-decoder=h264"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-parser=h264"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-hwaccel=h264_vaapi"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-hwaccel=h264_dxva2"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=mjpeg"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=rtsp"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=rtp"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-encoder=h264"
2.mkfifo Android Permission denied
在mkfifo的时候可能会出错,原因是需要设置对应的路径要正确
本文使用的路径 /data/data/com.abc/test.fifo(在安卓手机上跑)
3.java byte[] to c unsigned char* buf
static void
IjkMediaPlayer_set_joy_receive_data(JNIEnv* env ,jobject thiz,jbyteArray data, jint jlen)
{
jbyte* bBuffer = (*env)->GetByteArrayElements(env,data,0);
unsigned char* buf = (unsigned char*)bBuffer;
IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
ijkmp_set_joy_data(mp,buf,jlen);
}