yasea
yasea copied to clipboard
关于Soft Encode拓展的问题
首先感谢作者的无私奉献!
在使用yasea开源项目的时候,我希望将SDK拓展成移动端当今主流的几种格式,如YV21、NV21、RGB等等......
通过对源码的理解,我发现ConvertToI420函数内部是在convert_to_i420.cc下,调用CanonicalFourCC函数,该函数返回video_common.h头文件中其对应的格式。
于是乎我对libenc.cc文件进行了如下函数的拓展和定义:
static jint libenc_SoftEncode(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jlong pts, jint format) {
// default use FOURCC_YV12
int softEncodeFormat = FOURCC_YV12;
// select soft encode format
switch (format) {
// YV12
case 0x00:
softEncodeFormat = FOURCC_YV12;
break;
// NV21
case 0x01:
softEncodeFormat = FOURCC_NV21;
break;
// RGBA
case 0x02:
softEncodeFormat = FOURCC_RGBA;
break;
// not support format
default:
LIBENC_LOGE("Fail to encode nalu , not support format!");
return JNI_ERR;
}
// LIBENC_LOGE("Start encode nalu , format is %d , softEncodeFormat is %d", format,
// softEncodeFormat);
jbyte *src_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) src_frame, src_width, src_height, need_flip, rotate_degree,
softEncodeFormat)) {
LIBENC_LOGE("Fail to encode nalu , convert_to_i420");
return JNI_ERR;
}
......
}
编译通过之后,我发现是黑屏的,所以我将解码出来的数据写到本地,用VLC进行播放,发现播放没问题,但是我发现数据好像有问题,不合符FIFO原则,想请问x264软编码内部编码的规则,是否在Java上层通过synchronized修饰就可以保证其操作的原子性和遵循FIFO的原则呢?
我已经脱离安卓开发了,现在手头没有设备了。看上去貌似SrsEncoder.java的数据没有往FlvMuxer
里送,不知道谁提交了PR改掉的,我审核不严,你可以自行调试一下,有结果请让我知晓,谢谢!
@begeekmyfriend 已经解决了,通过抓包发现libx264编码出来的数据只有第一帧带有SPS和PPS,我这边的需求是每一个I帧都要带SPS和PPS,于是我将编码的数据进行了如下处理就解决了我的问题: @Override public void onH264DataCallBack(byte[] h264, long pts, boolean isKeyFrame) {
if (sps_pps_header == null) {
int pos = 0;
int len = 0;
boolean existSPS_PPS = false;
for (int i = 0; i < h264.length; i++) {
if (!existSPS_PPS) {
// SPS
if (h264[i] == 0x00 && h264[i + 1] == 0x00 && h264[i + 2] == 0x00 && h264[i + 3] == 0x01
&& h264[i + 4] == 0x67) {
pos = i;
i += 5;
len += 5;
existSPS_PPS = true;
}
}
// SEI
if (h264[i] == 0x00 && h264[i + 1] == 0x00 && h264[i + 2] == 0x01 && h264[i + 3] == 0x06) {
sps_pps_header = new byte[len];
System.arraycopy(h264, pos, sps_pps_header, 0, len);
LiveLog.d("onH264DataCallBack sps and pps : " + LiveLog.BytesToHexString(sps_pps_header));
break;
}
len++;
}
}
long absTimeStamp = System.currentTimeMillis();// + 76
// absTimeStamp = pts;
LiveLog.d("onH264DataCallBack ------ pts = " + pts + " , isKeyFrame : " + isKeyFrame);
// I
if (isKeyFrame) {
out_h264 = new byte[sps_pps_header.length + h264.length];
System.arraycopy(sps_pps_header, 0, out_h264, 0, sps_pps_header.length);
System.arraycopy(h264, 0, out_h264, sps_pps_header.length, h264.length);
}
// P
else {
out_h264 = h264;
}
writeFile(out_h264);
if (mLiveImpl != null) {
mLiveImpl.onLiveData(mChannelId, (isKeyFrame ? Frame.FRAME_TYPE_I_FRAME : Frame.FRAME_TYPE_P_FRAME),
absTimeStamp, 0, out_h264);
}
}
但新的问题又来了,需求是实现两个摄像头同时进行实时视频传输,但我发现libx264目前不支持同时编码两路摄像头视频,我需要研究一下这块儿的处理,看是否有可行性。
每个人的配置环境不一样,你可以看一下x264的选项global_nal_header是否有帮助,有人反馈iOS解码必须开启该选项
多摄像头没有调试过,手机在进化中,你可以创建两个SrsPublisher
对象,至于x264,用多进程
@begeekmyfriend 我把预览做在framework里的SystemUI里面了,根据项目需求,因为硬编只有baseline,所以选择了x264软编,该项目很好的解决了我的问题,我只用移植x264的so库就行了,预览和发送等已经做了,另外关于前面的那个需求,就是每个关键帧(I帧)前面添加SPS/PPS的需求,我刚刚仔细阅读了x264源码,发现只要在openSoftEncoder函数将b_repeat_headers置为1就行了,x264会重复把SPS/PPS 放到关键帧前面,双录软编码的问题我会采用新的独立进程或子进程处理。
非常谢谢您的回复,祝您生活愉快!
的确,当时写这个配置是有用户指出使用苹果手机拉流硬解码不支持b_repeat_headers
,这也是你需要注意的一点。