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(¶m, "baseline");//鏍稿骞朵慨鏀归儴鍒嗛潪娉曞弬鏁?
args->encoder = x264_encoder_open(¶m);//鎵撳紑缂栫爜鍣?
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单元,导致推过去的流解不出来,希望写这个博客对初学者有点帮助