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

来源:互联网 发布:申请域名后怎么使用 编辑:程序博客网 时间:2024/05/21 21:40

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_SUPPORTMp4Args mp4_args;#endifx264_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 NV21int 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]; }elsebuffer= (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);//缂栫爜锛屽苟涓斿埗瀹歯alif (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;}}elseret = -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单元,导致推过去的流解不出来,希望写这个博客对初学者有点帮助




1 0
原创粉丝点击