yasea icon indicating copy to clipboard operation
yasea copied to clipboard

关于Soft Encode拓展的问题

Open Mr-Jiang opened this issue 5 years ago • 6 comments

首先感谢作者的无私奉献!

在使用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的原则呢?

Mr-Jiang avatar Nov 16 '19 02:11 Mr-Jiang

我已经脱离安卓开发了,现在手头没有设备了。看上去貌似SrsEncoder.java的数据没有往FlvMuxer里送,不知道谁提交了PR改掉的,我审核不严,你可以自行调试一下,有结果请让我知晓,谢谢!

begeekmyfriend avatar Nov 16 '19 03:11 begeekmyfriend

@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目前不支持同时编码两路摄像头视频,我需要研究一下这块儿的处理,看是否有可行性。

Mr-Jiang avatar Nov 16 '19 07:11 Mr-Jiang

每个人的配置环境不一样,你可以看一下x264的选项global_nal_header是否有帮助,有人反馈iOS解码必须开启该选项

begeekmyfriend avatar Nov 16 '19 09:11 begeekmyfriend

多摄像头没有调试过,手机在进化中,你可以创建两个SrsPublisher对象,至于x264,用多进程

begeekmyfriend avatar Nov 16 '19 09:11 begeekmyfriend

@begeekmyfriend 我把预览做在framework里的SystemUI里面了,根据项目需求,因为硬编只有baseline,所以选择了x264软编,该项目很好的解决了我的问题,我只用移植x264的so库就行了,预览和发送等已经做了,另外关于前面的那个需求,就是每个关键帧(I帧)前面添加SPS/PPS的需求,我刚刚仔细阅读了x264源码,发现只要在openSoftEncoder函数将b_repeat_headers置为1就行了,x264会重复把SPS/PPS 放到关键帧前面,双录软编码的问题我会采用新的独立进程或子进程处理。

非常谢谢您的回复,祝您生活愉快!

Mr-Jiang avatar Nov 16 '19 12:11 Mr-Jiang

的确,当时写这个配置是有用户指出使用苹果手机拉流硬解码不支持b_repeat_headers,这也是你需要注意的一点。

begeekmyfriend avatar Nov 16 '19 13:11 begeekmyfriend