You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
FFmpegAndroid/Live/src/main/cpp/live.c

490 lines
16 KiB

#include <jni.h>
#include <string.h>
#include "include/x264/x264.h"
#include <android/log.h>
#include "include/rtmp/rtmp.h"
#include "include/faac/faac.h"
#include <pthread.h>
#include "queue.h"
#include <stdint.h>
#include <malloc.h>
#define TAG "FrankLive"
#define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, TAG, format, ##__VA_ARGS__)
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, format, ##__VA_ARGS__)
x264_picture_t picture_in;
x264_picture_t picture_out;
x264_t *video_encode_handle;
faacEncHandle *audio_encode_handle;
uint32_t start_time;
pthread_cond_t cond;
pthread_mutex_t mutex;
char *url_path;
int is_pushing = FALSE;
unsigned long inputSamples;
unsigned long maxOutputBytes;
//子线程回调给Java需要用到JavaVM
JavaVM *javaVM;
//调用类
jobject jobject_error;
/***************与Java层对应**************/
//视频编码器打开失败
const int ERROR_VIDEO_ENCODER_OPEN = 0x01;
//视频帧编码失败
const int ERROR_VIDEO_ENCODE = 0x02;
//音频编码器打开失败
const int ERROR_AUDIO_ENCODER_OPEN = 0x03;
//音频帧编码失败
const int ERROR_AUDIO_ENCODE = 0x04;
//RTMP连接失败
const int ERROR_RTMP_CONNECT = 0x05;
//RTMP连接流失败
const int ERROR_RTMP_CONNECT_STREAM = 0x06;
//RTMP发送数据包失败
const int ERROR_RTMP_SEND_PACKET = 0x07;
/***************与Java层对应**************/
void add_rtmp_packet(RTMPPacket *pPacket);
void add_x264_body(uint8_t *buf, int len);
void add_x264_key_header(unsigned char sps[100], unsigned char pps[100], int len, int pps_len);
void add_aac_body(unsigned char *buf, int len);
void add_aac_header();
//当调用System.loadLibrary时,会回调这个方法
//jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
// javaVM = vm;
// return JNI_VERSION_1_6;
//}
//回调异常给java
void throw_error_to_java(int error_code) {
JNIEnv *env;
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
jclass jclazz = (*env)->GetObjectClass(env, jobject_error);
jmethodID jmethod = (*env)->GetMethodID(env, jclazz, "errorFromNative", "(I)V");
(*env)->CallVoidMethod(env, jobject_error, jmethod, error_code);
(*javaVM)->DetachCurrentThread(javaVM);
}
//推流线程
void *push_thread(void *args) {
//建立RTMP连接
RTMP *rtmp = RTMP_Alloc();
if (!rtmp) {
LOGE("RTMP_Alloc fail...");
goto end;
}
RTMP_Init(rtmp);
RTMP_SetupURL(rtmp, url_path);
LOGI("url_path=%s", url_path);
RTMP_EnableWrite(rtmp);
rtmp->Link.timeout = 10;
if (!RTMP_Connect(rtmp, NULL)) {
LOGE("RTMP_Connect fail...");
throw_error_to_java(ERROR_RTMP_CONNECT);
goto end;
}
LOGI("RTMP_Connect success...");
if (!RTMP_ConnectStream(rtmp, 0)) {
LOGE("RTMP_ConnectStream fail...");
throw_error_to_java(ERROR_RTMP_CONNECT_STREAM);
goto end;
}
LOGI("RTMP_ConnectStream success...");
//开始计时
start_time = RTMP_GetTime();
is_pushing = TRUE;
//发送一个ACC HEADER
add_aac_header();
//循环推流
while (is_pushing) {
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
//从队头去一个RTMP包出来
RTMPPacket *packet = queue_get_first();
if (packet) {
queue_delete_first();
//发送rtmp包,true代表rtmp内部有缓存
int ret = RTMP_SendPacket(rtmp, packet, TRUE);
if (!ret) {
LOGE("RTMP_SendPacket fail...");
RTMPPacket_Free(packet);
pthread_mutex_unlock(&mutex);
throw_error_to_java(ERROR_RTMP_SEND_PACKET);
goto end;
}
RTMPPacket_Free(packet);
}
pthread_mutex_unlock(&mutex);
}
end:
LOGI("free all the thing about rtmp...");
RTMP_Close(rtmp);
free(rtmp);
free(url_path);
return 0;
}
JNIEXPORT jint JNICALL
Java_com_frank_live_LiveUtil_native_1start(JNIEnv *env, jobject instance, jstring url_) {
const char *url = (*env)->GetStringUTFChars(env, url_, 0);
url_path = malloc(strlen(url) + 1);
memset(url_path, 0, strlen(url) + 1);
memcpy(url_path, url, strlen(url));
//创建队列
create_queue();
//初始化互斥锁和条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_t push_thread_id;
//创建消费线程推流
pthread_create(&push_thread_id, NULL, push_thread, NULL);
(*env)->ReleaseStringUTFChars(env, url_, url);
jobject_error = (*env)->NewGlobalRef(env, instance);
return 0;
}
//视频编码器x264参数配置
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_setVideoParam(JNIEnv *env, jobject instance, jint width, jint height,
jint bitRate, jint frameRate) {
x264_param_t param;
//默认设置
x264_param_default_preset(&param, "ultrafast", "zerolatency");
param.i_csp = X264_CSP_I420;//YUV420SP
param.i_width = width;
param.i_height = height;
param.b_vfr_input = 0;//码率控制不通过timebase和timestamp,通过fps控制
param.b_repeat_headers = 1;//每帧传入sps和pps,提高视频纠错能力
param.i_level_idc = 51;//最大level:resolution@frameRate
param.rc.i_rc_method = X264_RC_CRF;//控制恒定码率 CRF:恒定码率;CQP:恒定质量;ABR:平均码率
param.rc.i_bitrate = bitRate;//码率
param.rc.i_vbv_max_bitrate = (int) (bitRate * 1.2);//瞬间最大码率
param.i_fps_num = (uint32_t) frameRate;//帧率分子
param.i_fps_den = 1;//帧率分母
param.i_timebase_num = param.i_fps_den;//时间基分子
param.i_timebase_den = param.i_fps_num;//时间基分母
param.i_threads = 1;//编码线程数
//设置profile档次,"baseline"代表没有B帧
x264_param_apply_profile(&param, "baseline");
//初始化图像
x264_picture_alloc(&picture_in, param.i_csp, param.i_width, param.i_height);
//打开编码器
video_encode_handle = x264_encoder_open(&param);
if (video_encode_handle) {
LOGI("x264_encoder_open success...");
} else {
LOGE("x264_encoder_open fail...");
throw_error_to_java(ERROR_VIDEO_ENCODER_OPEN);
}
}
//音频编码器FAAC参数配置
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_setAudioParam(JNIEnv *env, jobject instance, jint sampleRate,
jint numChannels) {
inputSamples;
maxOutputBytes;
audio_encode_handle = faacEncOpen((unsigned long) sampleRate,
(unsigned int) numChannels, &inputSamples, &maxOutputBytes);
if (!audio_encode_handle) {
LOGE("faacEncOpen fail...");
throw_error_to_java(ERROR_AUDIO_ENCODER_OPEN);
return;
}
LOGI("faacEncOpen success...");
faacEncConfigurationPtr configPtr = faacEncGetCurrentConfiguration(audio_encode_handle);
configPtr->bandWidth = 0;
configPtr->mpegVersion = MPEG4;//MPEG版本
configPtr->outputFormat = 0;//包含ADTS头
configPtr->useTns = 1;//时域噪音控制
configPtr->useLfe = 0;
configPtr->allowMidside = 1;
configPtr->aacObjectType = LOW;
configPtr->quantqual = 100;//量化
configPtr->shortctl = SHORTCTL_NORMAL;
int result = faacEncSetConfiguration(audio_encode_handle, configPtr);
if (result) {
LOGI("faacEncSetConfiguration success...");
} else {
LOGE("faacEncSetConfiguration fail...");
throw_error_to_java(ERROR_AUDIO_ENCODER_OPEN);
}
}
//添加RTMPPacket包到队列中
void add_rtmp_packet(RTMPPacket *pPacket) {
pthread_mutex_lock(&mutex);
if (is_pushing) {
queue_append_last(pPacket);
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
//添加SPS和PPS header
void add_x264_key_header(unsigned char sps[100], unsigned char pps[100], int sps_len, int pps_len) {
int body_size = 16 + sps_len + pps_len;
RTMPPacket *packet = malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet, body_size);
RTMPPacket_Reset(packet);
unsigned char *body = (unsigned char *) packet->m_body;
int i = 0;
body[i++] = 0x17;//VideoHeadType 0-3:FrameType(KeyFrame=1);4-7:CodecId(AVC=7)
body[i++] = 0x00;//AVC PacketType
body[i++] = 0x00;//composition type 24bit
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x01;//AVC decoder configuration record
body[i++] = sps[1];
body[i++] = sps[2];
body[i++] = sps[3];
body[i++] = 0xFF;
body[i++] = 0xE1;//sps
body[i++] = (unsigned char) ((sps_len >> 8) & 0xFF);
body[i++] = (unsigned char) (sps_len & 0xFF);
memcpy(&body[i], sps, (size_t) sps_len);
i += sps_len;
body[i++] = 0x01;//pps
body[i++] = (unsigned char) ((pps_len >> 8) & 0xFF);
body[i++] = (unsigned char) (pps_len & 0xFF);
memcpy(&body[i], pps, (size_t) pps_len);
i += pps_len;
packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet->m_nBodySize = (uint32_t) body_size;
packet->m_nTimeStamp = 0;
packet->m_hasAbsTimestamp = 0;
packet->m_nChannel = 0x04;
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
add_rtmp_packet(packet);
}
//添加x264的body
void add_x264_body(uint8_t *buf, int len) {
if (buf[2] == 0x01) {//00 00 01
buf += 3;
len -= 3;
} else if (buf[3] == 0x01) {//00 00 00 01
buf += 4;
len -= 4;
}
int body_size = len + 9;//
RTMPPacket *packet = malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet, body_size);
RTMPPacket_Reset(packet);
unsigned char *body = (unsigned char *) packet->m_body;
int type = buf[0] & 0x1F;
if (type == NAL_SLICE_IDR) {//关键帧
body[0] = 0x17;
} else {
body[0] = 0x27;
}
body[1] = 0x01;
body[2] = 0x00;
body[3] = 0x00;
body[4] = 0x00;
body[5] = (unsigned char) ((len >> 24) & 0xFF);
body[6] = (unsigned char) ((len >> 16) & 0xFF);
body[7] = (unsigned char) ((len >> 8) & 0xFF);
body[8] = (unsigned char) (len & 0xFF);
memcpy(&body[9], buf, (size_t) len);
packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
packet->m_hasAbsTimestamp = 0;
packet->m_nBodySize = (uint32_t) body_size;
packet->m_nChannel = 0x04;
packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet->m_nTimeStamp = RTMP_GetTime() - start_time;
add_rtmp_packet(packet);
}
//添加AAC header
void add_aac_header() {
unsigned char *ppBuffer;
unsigned long pSize;
faacEncGetDecoderSpecificInfo(audio_encode_handle, &ppBuffer, &pSize);
int body_size = (int) (2 + pSize);
RTMPPacket *packet = malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet, body_size);
RTMPPacket_Reset(packet);
unsigned char *body = (unsigned char *) packet->m_body;
//两字节头
//soundFormat(4bits):10->aac;soundRate(2bits):3->44kHz;soundSize(1bit):1->pcm_16bit;soundType(1bit):1->stereo
body[0] = 0xAF;
//AACPacketType:0表示AAC sequence header
body[1] = 0x00;
memcpy(&body[2], ppBuffer, (size_t) pSize);
packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
packet->m_nBodySize = (uint32_t) body_size;
packet->m_nChannel = 4;
packet->m_nTimeStamp = 0;
packet->m_hasAbsTimestamp = 0;
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
add_rtmp_packet(packet);
free(ppBuffer);
}
//添加AAC body
void add_aac_body(unsigned char *buf, int len) {
int body_size = 2 + len;
RTMPPacket *packet = malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet, body_size);
RTMPPacket_Reset(packet);
unsigned char *body = (unsigned char *) packet->m_body;
//两字节头
//soundFormat(4bits):10->aac;soundRate(2bits):3->44kHz;soundSize(1bit):1->pcm_16bit;soundType(1bit):1->stereo
body[0] = 0xAF;
//PackageType(StreamType):0->AAC带ADTS头;1->AAC RAW
body[1] = 0x01;
memcpy(&body[2], buf, (size_t) len);
packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
packet->m_nBodySize = (uint32_t) body_size;
packet->m_nChannel = 4;
packet->m_nTimeStamp = RTMP_GetTime() - start_time;
packet->m_hasAbsTimestamp = 0;
packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
add_rtmp_packet(packet);
free(buf);
}
//推送音频流
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_pushAudio(JNIEnv *env, jobject instance, jbyteArray data_,
jint length) {
jbyte *data = (*env)->GetByteArrayElements(env, data_, NULL);
int *pcm_buf;
unsigned char *aac_buf;
pcm_buf = malloc(inputSamples * sizeof(int));
aac_buf = malloc(maxOutputBytes * sizeof(unsigned char));
int count = 0;
unsigned int buffer_size = (unsigned int) (length / 2);
unsigned short *buf = (unsigned short *) data;
while (count < buffer_size) {
int audio_length = (int) inputSamples;
if ((count + audio_length) >= buffer_size) {
audio_length = buffer_size - count;
}
int i;
for (i = 0; i < audio_length; i++) {
//每次从实时的pcm音频队列中读出量化位数为8的pcm数据
int sample_byte = ((int16_t *) buf + count)[i];
//用8个二进制位来表示一个采样量化点(模数转换)
pcm_buf[i] = sample_byte << 8;
}
count += inputSamples;
//调用FAAC编码,返回编码字节数
int bytes_len = faacEncEncode(audio_encode_handle, pcm_buf, (unsigned int) audio_length,
aac_buf, maxOutputBytes);
if (bytes_len <= 0) {
// throw_error_to_java(ERROR_AUDIO_ENCODE);
LOGE("音频编码失败...");
continue;
}
add_aac_body(aac_buf, bytes_len);
}
(*env)->ReleaseByteArrayElements(env, data_, data, 0);
if (pcm_buf != NULL) {
free(pcm_buf);
}
}
//推送视频流
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_pushVideoNew(JNIEnv *env, jobject instance, jbyteArray yPlane,
jbyteArray uPlane, jbyteArray vPlane) {
jbyte *y_plane = (*env)->GetByteArrayElements(env, yPlane, NULL);
jbyte *u_plane = (*env)->GetByteArrayElements(env, uPlane, NULL);
jbyte *v_plane = (*env)->GetByteArrayElements(env, vPlane, NULL);
jsize y_length = (*env)->GetArrayLength(env, yPlane);
jsize u_length = (*env)->GetArrayLength(env, uPlane);
jsize v_length = (*env)->GetArrayLength(env, vPlane);
//直接拷贝
memcpy(picture_in.img.plane[0], y_plane, (size_t) y_length);
memcpy(picture_in.img.plane[1], u_plane, (size_t) u_length);
memcpy(picture_in.img.plane[2], v_plane, (size_t) v_length);
int i;
x264_nal_t *nal = NULL;
int nal_num = -1;//NAL unit个数
//调用h264编码
if (x264_encoder_encode(video_encode_handle, &nal, &nal_num, &picture_in, &picture_out) < 0) {
LOGE("x264_encoder_encode fail");
throw_error_to_java(ERROR_VIDEO_ENCODE);
goto end;
}
if (nal_num <= 0) {
LOGE("nal_num <= 0");
goto end;
}
//使用RTMP推流
//关键帧(I帧)加上SPS和PPS
int sps_len = 0, pps_len = 0;
unsigned char sps[100];
unsigned char pps[100];
memset(sps, 0, 100);
memset(pps, 0, 100);
for (i = 0; i < nal_num; ++i) {
if (nal[i].i_type == NAL_SPS) {//sps
sps_len = nal[i].i_payload - 4;
memcpy(sps, nal[i].p_payload + 4, (size_t) 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, (size_t) pps_len);
add_x264_key_header(sps, pps, sps_len, pps_len);
} else {
add_x264_body(nal[i].p_payload, nal[i].i_payload);
}
}
end:
(*env)->ReleaseByteArrayElements(env, yPlane, y_plane, 0);
(*env)->ReleaseByteArrayElements(env, uPlane, u_plane, 0);
(*env)->ReleaseByteArrayElements(env, vPlane, v_plane, 0);
}
//停止推流
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_native_1stop(JNIEnv *env, jobject instance) {
is_pushing = FALSE;
LOGI("native_1stop");
}
//释放所有资源
JNIEXPORT void JNICALL
Java_com_frank_live_LiveUtil_native_1release(JNIEnv *env, jobject instance) {
//清除x264的picture缓存
x264_picture_clean(&picture_in);
x264_picture_clean(&picture_out);
//关闭音视频编码器
x264_encoder_close(video_encode_handle);
faacEncClose(audio_encode_handle);
//删除全局引用
(*env)->DeleteGlobalRef(env, jobject_error);
(*javaVM)->DestroyJavaVM(javaVM);
//销毁互斥锁和条件变量
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
//退出线程
pthread_exit(0);
}