1、
JNIEXPORT void JNICALL Java_com_dongnaoedu_live_jni_PushNative_fireVideo
(JNIEnv *env, jobject jobj, jbyteArray buffer){
//视频数据转为YUV420P
//NV21->YUV420P
jbyte* nv21_buffer = (*env)->GetByteArrayElements(env,buffer,NULL);
jbyte* u = pic_in.img.plane[1];
jbyte* v = pic_in.img.plane[2];
//nv21 4:2:0 Formats, 12 Bits per Pixel
//nv21与yuv420p,y个数一致,uv位置对调
//nv21转yuv420p y = w*h,u/v=w*h/4
//nv21 = yvu yuv420p=yuv y=y u=y+1+1 v=y+1
memcpy(pic_in.img.plane[0], nv21_buffer, y_len);
int i;
for (i = 0; i < u_len; i++) {
*(u + i) = *(nv21_buffer + y_len + i * 2 + 1);
*(v + i) = *(nv21_buffer + y_len + i * 2);
}
//h264编码得到NALU数组
x264_nal_t *nal = NULL; //NAL
int n_nal = -1; //NALU的个数
//进行h264编码
if(x264_encoder_encode(video_encode_handle,&nal, &n_nal,&pic_in,&pic_out) < 0){
LOGE("%s","编码失败");
return;
}
//使用rtmp协议将h264编码的视频数据发送给流媒体服务器
//帧分为关键帧和普通帧,为了提高画面的纠错率,关键帧应包含SPS和PPS数据
int sps_len , pps_len;
unsigned char sps[100];
unsigned char pps[100];
memset(sps,0,100);
memset(pps,0,100);
//遍历NALU数组,根据NALU的类型判断
for(i=0; i < n_nal; i++){
if(nal[i].i_type == NAL_SPS){
//复制SPS数据
sps_len = nal[i].i_payload - 4;
memcpy(sps,nal[i].p_payload + 4,sps_len); //不复制四字节起始码
}else if(nal[i].i_type == NAL_PPS){
//复制PPS数据
pps_len = nal[i].i_payload - 4;
memcpy(pps,nal[i].p_payload + 4,pps_len); //不复制四字节起始码
//发送序列信息
//h264关键帧会包含SPS和PPS数据
add_264_sequence_header(pps,sps,pps_len,sps_len);
}else{
//发送帧信息
add_264_body(nal[i].p_payload,nal[i].i_payload);
}
}
}
2、
void *push_thread(void * arg){
//建立RTMP连接
RTMP *rtmp = RTMP_Alloc();
if(!rtmp){
LOGE("rtmp初始化失败");
goto end;
}
RTMP_Init(rtmp);
rtmp->Link.timeout = 5; //连接超时的时间
//设置流媒体地址
RTMP_SetupURL(rtmp,rtmp_path);
//发布rtmp数据流
RTMP_EnableWrite(rtmp);
//建立连接
if(!RTMP_Connect(rtmp,NULL)){
LOGE("%s","RTMP 连接失败");
goto end;
}
//计时
start_time = RTMP_GetTime();
if(!RTMP_ConnectStream(rtmp,0)){ //连接流
goto end;
}
for(;;){
//发送
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
//取出队列中的RTMPPacket
RTMPPacket *packet = queue_get_first();
if(packet){
queue_delete_first(); //移除
packet->m_nInfoField2 = rtmp->m_stream_id; //RTMP协议,stream_id数据
int i = RTMP_SendPacket(rtmp,packet,TRUE); //TRUE放入librtmp队列中,并不是立即发送
if(!i){
LOGE("RTMP 断开");
RTMPPacket_Free(packet);
pthread_mutex_unlock(&mutex);
goto end;
}
RTMPPacket_Free(packet);
}
pthread_mutex_unlock(&mutex);
}
end:
LOGI("%s","释放资源");
RTMP_Close(rtmp);
RTMP_Free(rtmp);
return 0;
}
3、
/**
* 发送h264帧信息
*/
void add_264_body(unsigned char *buf ,int len){
//去掉起始码(界定符)
if(buf[2] == 0x00){ //00 00 00 01
buf += 4;
len -= 4;
}else if(buf[2] == 0x01){ // 00 00 01
buf += 3;
len -= 3;
}
int body_size = len + 9;
RTMPPacket *packet = malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet,body_size);
unsigned char * body = packet->m_body;
//当NAL头信息中,type(5位)等于5,说明这是关键帧NAL单元
//buf[0] NAL Header与运算,获取type,根据type判断关键帧和普通帧
//00000101 & 00011111(0x1f) = 00000101
int type = buf[0] & 0x1f;
//Inter Frame 帧间压缩
body[0] = 0x27;//VideoHeaderTag:FrameType(2=Inter Frame)+CodecID(7=AVC)
//IDR I帧图像
if (type == NAL_SLICE_IDR) {
body[0] = 0x17;//VideoHeaderTag:FrameType(1=key frame)+CodecID(7=AVC)
}
//AVCPacketType = 1
body[1] = 0x01; /*nal unit,NALUs(AVCPacketType == 1)*/
body[2] = 0x00; //composition time 0x000000 24bit
body[3] = 0x00;
body[4] = 0x00;
//写入NALU信息,右移8位,一个字节的读取?
body[5] = (len >> 24) & 0xff;
body[6] = (len >> 16) & 0xff;
body[7] = (len >> 8) & 0xff;
body[8] = (len) & 0xff;
/*copy data*/
memcpy(&body[9], buf, len);
packet->m_hasAbsTimestamp = 0;
packet->m_nBodySize = body_size;
packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;//当前packet的类型:Video
packet->m_nChannel = 0x04;
packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
// packet->m_nTimeStamp = -1;
packet->m_nTimeStamp = RTMP_GetTime() - start_time;//记录了每一个tag相对于第一个tag(File Header)的相对时间
add_rtmp_packet(packet);
}