天天看点

android librtmp 推送h264流 aac流 基本过程总结四 推流x264android librtmp 推送h264流 aac流 基本过程总结三 推流x264

android librtmp 推送h264流 aac流 基本过程总结三 推流x264

1. x264初始化配置 注意:这里边我踩了一些坑,rtmp 推流过程中去掉nal中pps,sps,只允许一帧中只有一个nal片,之前测试初始化x264参数没注意到这个问题导致自己推流再nginx服务器中没有正确的切成ts流,播放异常。 另外需要注意推流的时候一定要跟faac流一个时间线,要不然会出现音视频不同步,或者视频中没有音频流的现象都是有可能发生的
typedef struct x264_args
{
	x264_t *encoder; //锟斤拷锟斤拷锟斤拷
        int64_t i_pts; //锟斤拷锟斤拷帧锟斤拷锟斤拷
        int outf;
	int width;
	int height;
	int init_flag;
	AVFrame * png_frame;
#if H264_TO_MP4_SUPPORT
	Mp4Args mp4_args;
#endif
	x264_picture_t pic_in;
	x264_picture_t pic_out;//x264锟斤拷x264_picture_t 锟斤拷示帧锟斤拷锟斤拷锟斤拷前锟斤拷锟斤拷锟斤拷
}X264Args;
           

定义x264普通的结构体

int X264encode_Init_Args_ForRtmp(X264Args *args, char *name,int width, int height)
{
	if (args == NULL )
	{
		return -1;
	}
	Thread_Lock_By_Kind(THREAD_LOCK_X264_ENCODE);
	FilterArgs * png_args = NULL ;
	int ret = 0;
	 int FPS = GOAL_FPS;
	 x264_param_t param;
	args->i_pts = 0;
	args->width = width;
	args->height = height;
	dmprint_string("encode start");
	x264_param_default_preset(¶m, "superfast", "zerolatency");//0延时 对实时性有保证

	 param.i_level_idc=30;//算法的复杂度

	 param.i_threads = X264_SYNC_LOOKAHEAD_AUTO;//linux可以设置0,主要为了防止开启多线程导致输出NAL片段多,这样保证一桢只有一个NAL
	 // 否则有几个线程,在编码成几个NALU。缺省为true。
	 param.b_sliced_threads = false;
	  param.rc.b_mb_tree = 0;//这个不为0,将导致编码延时帧...在实时编码时,必须为0
	  //    该参数设置是让每个I帧都附带sps/pps。//    param.b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面
//	  param.b_repeat_headers = 0;
	 param.i_width = width;
	 param.i_height = height;
	 param.i_fps_num = FPS;
	 param.i_fps_den = 1;
	 param.i_csp = X264_CSP_I420;//
	 param.i_keyint_min = FPS * 1;
	 param.i_keyint_max = FPS * 2;
	 param.rc.i_qp_min = 18; //
	 param.rc.i_qp_max =31; //xiaoyu 70
	 param.analyse.i_me_range = 16;
	 param.rc.i_qp_step = 2;
//	 param.rc.i_rc_method = X264_RC_CQP; 
//	 param.rc.i_qp_constant  = 23;

	 x264_param_apply_profile(&param, "baseline");//鏍稿骞朵慨鏀归儴鍒嗛潪娉曞弬鏁?
	 args->encoder = x264_encoder_open(&param);//鎵撳紑缂栫爜鍣?

 	x264_picture_alloc(&args->pic_in, X264_CSP_I420, width, height);//鐢宠涓€甯у唴瀛?
 #if LINUX_VERSION || ANDROID_VERSION
 	 free(args->pic_in.img.plane[0] );
#else
	 free( *( ( ( void **) args->pic_in.img.plane[0] ) - 1 ) );
#endif
	 //鐢宠珛铏曠悊鐨勭珐瀛樺唴瀛?
	if (is_watermark_flag)
	{
		png_args = get_filter_args(width,height,AV_PIX_FMT_YUV420P);
		Filter_Alloc_Frame(&args->png_frame,png_args);
		Frame_Add_WaterMark(width,height,AV_PIX_FMT_YUV420P,2);
		free(png_args);
	}
	args->init_flag = 1;
//end:
	if (ret < 0)
	{
		/*close(args->outf);//鍏抽棴鏂囦欢
		*/x264_encoder_close(args->encoder);//鍏抽棴缂栫爜鍣?

		if (is_watermark_flag)
		{
			is_watermark_flag = 0;
			Frame_Release_WaterMark(2);
			Filter_Free_Frame(&args->png_frame);
			args->png_frame = NULL;
		}
	//	mp4_for_h264_release(&args->mp4_args);
		args->init_flag = 0;
	}
	Thread_unLock_By_Kind(THREAD_LOCK_X264_ENCODE);
	 return ret;
}
           
2. 编码一帧frame
//mode = 0 I420    1 NV21
int X264encodeAddframe_Args_ForRtmp(X264Args *args, uint8_t* jb, int nw, int nh, int isFrontCamera, 
				 int orientation, int isInversion, char *out, int *nal_len, int *nal_count, int mode)
{
	if (args == NULL || args->init_flag != 1)
			return -1;
	Thread_Lock_By_Kind(THREAD_LOCK_X264_ENCODE);
	int ret = 0;
	int len = h264_width*h264_height*3 >> 1;
	uint8_t* buffer = (uint8_t*) jb;
	x264_nal_t *nals = NULL;
	int nnal = 0;
	x264_nal_t *nal = NULL;
	char *pbuf = NULL;
	
	if (jb == NULL)
	{
		ret = -1;
		goto end;
	}
	pbuf = (char *)malloc(args->width * args->height * 3);
	if (pbuf == NULL)
		goto end;
	if (mode == 0)
	{
		detailYuvPic(buffer, (uint8_t *)pbuf, nw, nh, args->width,args->height, orientation , isInversion, isFrontCamera);
	}
	else
	{
		detailYuvPic_forAndroid(buffer, (uint8_t *)pbuf, nw, nh,args->width,args->height, orientation , isInversion, isFrontCamera);
	}

	if (is_watermark_flag)
	{
		 memcpy(args->png_frame->data[0],pbuf,len);
		Frame_Get_WaterMark(args->png_frame,args->png_frame, 2);
		buffer = args->png_frame->data[0];
		 
	}
	else
		buffer= (uint8_t *)pbuf;


	//yuv420:灏唝uvbuff 濉厖杩沺ic_in锛岄渶瑕佹墜鍔ㄦ寚瀹氳繖涓変釜鎸囬拡
	args->pic_in.img.plane[0] = buffer;
	args->pic_in.img.plane[1] = buffer+(args->height * args->width);
	args->pic_in.img.plane[2] = buffer+(args->height * args->width * 5 >> 2);
	args->pic_in.img.i_plane = 3;
	//鎵嬪姩鎸囧畾鍏跺榻愰暱搴?
	args->pic_in.img.i_stride[0] = args->width;
	args->pic_in.img.i_stride[1] = args->width >>1;
	args->pic_in.img.i_stride[2] = args->width >>1;
	//棰滆壊绌洪棿
	args->pic_in.img.i_csp = X264_CSP_I420;
	//pic_in.i_type = X264_TYPE_AUTO;
	//缂栫爜
	args->pic_in.i_pts = args->i_pts++;

	*nal_count = 0;
	ret = x264_encoder_encode(args->encoder, &nals, &nnal, &args->pic_in, &args->pic_out);//缂栫爜锛屽苟涓斿埗瀹歯al
	if (ret  > 0)
	{	
		len = 0;
		for (nal = nals; nal < nals + nnal; nal++) 
		{
			memcpy(out + len, nal->p_payload, nal->i_payload);
			len += nal->i_payload;
			*nal_len = nal->i_payload;
			*nal_count += 1;
			nal_len++;
		}
		dm_printf("count = %d", nnal);
	}	
	else
	{
		len = 0;
		*nal_count = 0;
	}
	args->i_pts += 66;
end:
	if (pbuf != NULL)
		free(pbuf);
	pbuf = NULL;
	Thread_unLock_By_Kind(THREAD_LOCK_X264_ENCODE);
	return ret;

}
           

3.释放编码资源

int X264encode_Finish_Args_ForRtmp(X264Args *args)
{
	if (args == NULL || args->init_flag != 1)
		return -1;
	Thread_Lock_By_Kind(THREAD_LOCK_X264_ENCODE);
	int num = 0;
	int ret = 0;
	dmprint_string("encode finish");
	num = x264_encoder_delayed_frames(args->encoder);
	dm_printf("num = %d\n",num);

	x264_encoder_close(args->encoder);//鍏抽棴缂栫爜鍣?
	if (is_watermark_flag)
	{
		is_watermark_flag = 0;
		Frame_Release_WaterMark(2);
	
	}
	if (args->png_frame != NULL)
	{
		Filter_Free_Frame(&args->png_frame);
		args->png_frame = NULL;
	}

	memset(args, 0, sizeof(X264Args));
	Thread_unLock_By_Kind(THREAD_LOCK_X264_ENCODE);
	return ret;
}
           

4. rtmp 发送 sps,pps 头

static int send_video_sps_pps(RTMP* m_pRtmp, unsigned char *sps, int sps_len, unsigned char  *pps, int pps_len) 
                  
{
	RTMPPacket * packet;
	unsigned char * body;
	int i;
	int ret = 0;
	if (m_pRtmp == NULL)
		return -1;
	
	packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + 1024);
	memset(packet, 0, RTMP_HEAD_SIZE);

	packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
	body = (unsigned char *) packet->m_body;
	i = 0;
	body[i++] = 0x17;
	body[i++] = 0x00;

	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00;

	/*AVCDecoderConfigurationRecord*/
	body[i++] = 0x01;
	body[i++] = sps[1];
	body[i++] = sps[2];
	body[i++] = sps[3];
	body[i++] = 0xff;

	/*sps*/
	body[i++] = 0xe1;
	body[i++] = (sps_len >> 8) & 0xff;
	body[i++] = sps_len & 0xff;
	memcpy(&body[i], sps, sps_len);

	i += sps_len;

	/*pps*/
	body[i++] = 0x01;
	body[i++] = (pps_len >> 8) & 0xff;
	body[i++] = (pps_len) & 0xff;
	memcpy(&body[i], pps, pps_len);
	i += pps_len;

	packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
	packet->m_nBodySize = i;
	packet->m_nChannel = 0x04;

	packet->m_nTimeStamp = 0;
	packet->m_hasAbsTimestamp = 0;
	packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
	packet->m_nInfoField2 = m_pRtmp->m_stream_id;

	if (RTMP_IsConnected(m_pRtmp)) 
	{
		//调用发送接口
		int success = RTMP_SendPacket(m_pRtmp, packet, TRUE);
		if (success != 1) 
		{
			dm_printf("send_video_sps_pps fail");
			ret = -1;
		}
	}
	else
		ret = -1;
	free(packet);
	return ret;
}      

5.rtmp发送普通的NAL

static int send_rtmp_video(RTMP* m_pRtmp, unsigned char *data, int data_len, int timestamp) 
{
	int type;
	RTMPPacket * packet;
	unsigned char * body;
	unsigned char* buffer = data;
	unsigned int length = data_len;
	int ret = 0;
	if (m_pRtmp == NULL)	
		return -1;

	/*去掉帧界定符(这里可能2种,但是sps or  pps只能为 00 00 00 01)*/
	if (buffer[2] == 0x00) { /*00 00 00 01*/
		buffer += 4;
		length -= 4;
	} else if (buffer[2] == 0x01) { /*00 00 01*/
		buffer += 3;
		length -= 3;
	}
	type = buffer[0] & 0x1f;

	packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + length + 9);
	memset(packet, 0, RTMP_HEAD_SIZE);

	packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
	packet->m_nBodySize = length + 9;

	/*send video packet*/
	body = (unsigned char *) packet->m_body;
	memset(body, 0, length + 9);

	/*key frame*/
	body[0] = 0x27;
	if (type == NAL_SLICE_IDR) //此为关键帧
	{
		body[0] = 0x17;
	}

	body[1] = 0x01; /*nal unit*/
	body[2] = 0x00;
	body[3] = 0x00;
	body[4] = 0x00;

	body[5] = (length >> 24) & 0xff;
	body[6] = (length >> 16) & 0xff;
	body[7] = (length >> 8) & 0xff;
	body[8] = (length) & 0xff;

	/*copy data*/
	memcpy(&body[9], buffer, length);

	packet->m_nTimeStamp = timestamp;
	packet->m_hasAbsTimestamp = 0;
	packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
	packet->m_nInfoField2 = m_pRtmp->m_stream_id;
	packet->m_nChannel = 0x04;
	packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;

	if (RTMP_IsConnected(m_pRtmp)) 
	{
		// 调用发送接口
		ret = RTMP_SendPacket(m_pRtmp, packet, TRUE);
		if (ret != 1)
		{
			dm_printf("send_rtmp_video fail");
			ret = -1;
		} 
	} 
	else
	{
		dm_printf("send_rtmp_video RTMP is not ready");
		ret = -1;
	}
	free(packet);
	return ret;
}
           

6. 初始化rtmp

定义结构体

typedef struct rtmp_infor
{
	char *aac_buf;
	char *x264_buf;
	int nal_len[20];
	int nal_count;
	int audio_first;
	int video_first;
	int init_flag;
	RTMP *prtmp ;
	X264Args x264args;
	AudioEncodeFaac aacargs;
}RTMPInfor;
           
int RTMP_Send_Init(char *url, int width, int height, int sample_rate, int channel)
{
	int ret = 0;
	if (rtmp_infor.init_flag == 1)
	{
		dm_printf("RTMP_Send_Init again init");
		return -1;
	}
	if (url == NULL)
	{
		dm_printf("url is NULL");
		return -1;
	}
	if (width <= 0 || height <= 0 || channel <= 0)
	{
		dm_printf("width or height or channel is error");
		return -1;
	}
	pthread_mutex_lock(&thread_lock_rtmp_send);
	memset(&rtmp_infor, 0, sizeof(rtmp_infor));
	ret = RTMP_Connect(&rtmp_infor.prtmp, url);
	if (ret < 0)
	{
		dm_printf("RTMP_Connect fail");
		goto end;
	}
	ret = Audio_Encode_Init_Faac(&rtmp_infor.aacargs, sample_rate, channel);
	if (ret < 0)
	{
		dm_printf("Audio_Encode_Init_Faac fail");
		goto end;
	}
	ret = X264encode_Init_Args_ForRtmp(&rtmp_infor.x264args, NULL, width, height);
	if (ret < 0)
	{
		dm_printf("Audio_Encode_Init_Faac fail");
		goto end;
	}
	rtmp_infor.aac_buf = (char *)malloc(NB_SAMPLE << channel);
	rtmp_infor.x264_buf = (char *)malloc(width * height * 3 >> 1);
	if (rtmp_infor.aac_buf == NULL || rtmp_infor.x264_buf == NULL)
	{
		dm_printf("aac_buf or 264_buf alloc fail");
		ret = -1;
		goto end;
	}
	rtmp_infor.init_flag = 1;
end:
	if (ret < 0)
	{
		X264encode_Finish_Args_ForRtmp(&rtmp_infor.x264args);
		Audio_Encode_Release_Faac(&rtmp_infor.aacargs);
		RTMP_Close(&rtmp_infor.prtmp);
		if (rtmp_infor.aac_buf != NULL)
			free(rtmp_infor.aac_buf);
		if (rtmp_infor.x264_buf != NULL)
			free(rtmp_infor.x264_buf);
		memset(&rtmp_infor, 0, sizeof(rtmp_infor));
	}
	pthread_mutex_unlock(&thread_lock_rtmp_send);
	return ret;
}
           

7,rtmp发送一桢264流

int RTMP_Send_Video(unsigned char *data, int nw, int nh, int isFrontCamera, 
				 int orientation, int isInversion, int mode, int timestamp)
{
	int ret = 0;
	if (data == NULL)
		return -1;
	if (!rtmp_infor.init_flag)
	{
		dm_printf("RTMP_Send Not Init");
		return -1;
	}
	pthread_mutex_lock(&thread_lock_rtmp_send);
	ret = X264encodeAddframe_Args_ForRtmp(&rtmp_infor.x264args,(uint8_t *)data, nw, nh, isFrontCamera, 
			orientation,  isInversion, rtmp_infor.x264_buf, rtmp_infor.nal_len, &rtmp_infor.nal_count, mode);
	if (ret < 0)
		goto end;
	rtmp_send_video(rtmp_infor.prtmp, (unsigned char *)rtmp_infor.x264_buf, rtmp_infor.nal_len, rtmp_infor.nal_count, timestamp, &rtmp_infor.video_first);
end:
	pthread_mutex_unlock(&thread_lock_rtmp_send);
	return ret;
}
           

8,rtmp释放资源

int RTMP_Send_Release()
{
	if (!rtmp_infor.init_flag)
		return 0;
	pthread_mutex_lock(&thread_lock_rtmp_send);
	X264encode_Finish_Args_ForRtmp(&rtmp_infor.x264args);
	Audio_Encode_Release_Faac(&rtmp_infor.aacargs);
	RTMP_Close(&rtmp_infor.prtmp);
	if (rtmp_infor.aac_buf != NULL)
			free(rtmp_infor.aac_buf);
	if (rtmp_infor.x264_buf != NULL)
		free(rtmp_infor.x264_buf);
	memset(&rtmp_infor, 0, sizeof(rtmp_infor));
	pthread_mutex_unlock(&thread_lock_rtmp_send);
	return 0;
}
           
总结:rtmp发送264流,还是有一些坑要踩的,我认为我遇到最主要的坑,就是编码的时候一桢编出来很多NAL单元,导致推过去的流解不出来,希望写这个博客对初学者有点帮助

继续阅读