diff --git a/Live/CMakeLists.txt b/Live/CMakeLists.txt index a784af9..e938a63 100644 --- a/Live/CMakeLists.txt +++ b/Live/CMakeLists.txt @@ -32,12 +32,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") include_directories(main/cpp/include) add_library(live - SHARED - src/main/cpp/live.c - src/main/cpp/queue.c + SHARED src/main/cpp/AudioStream.cpp src/main/cpp/VideoStream.cpp - src/main/cpp/RtmpPusher.cpp) + src/main/cpp/RtmpPusher.cpp) find_library( log-lib log ) diff --git a/Live/src/main/cpp/live.c b/Live/src/main/cpp/live.c deleted file mode 100644 index 36916de..0000000 --- a/Live/src/main/cpp/live.c +++ /dev/null @@ -1,490 +0,0 @@ -#include -#include -#include "include/x264/x264.h" -#include -#include "include/rtmp/rtmp.h" -#include "include/faac/faac.h" -#include -#include "queue.h" -#include -#include - -#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(¶m, "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(¶m, "baseline"); - //初始化图像 - x264_picture_alloc(&picture_in, param.i_csp, param.i_width, param.i_height); - //打开编码器 - video_encode_handle = x264_encoder_open(¶m); - 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); -} \ No newline at end of file diff --git a/Live/src/main/cpp/queue.c b/Live/src/main/cpp/queue.c deleted file mode 100644 index 6942c44..0000000 --- a/Live/src/main/cpp/queue.c +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include - -typedef struct queue_node { - struct queue_node* prev; - struct queue_node* next; - void *p;//节点的值 -} node; - -// 表头 -static node *phead = NULL; -static int count = 0; - -static node* create_node(void *pval) { - node *pnode = NULL; - pnode = (node *) malloc(sizeof(node)); - if (pnode) { - // 默认的,pnode的前一节点和后一节点都指向它自身 - pnode->prev = pnode->next = pnode; - // 节点的值为pval - pnode->p = pval; - } - return pnode; -} - -/** - * 创建双向链表 - * @return 成功,返回0;否则,返回-1。 - */ -int create_queue() { - phead = create_node(NULL); - if (!phead) { - return -1; - } - count = 0; - return 0; -} - -/** - * 判断双向链表是否为空 - * @return 0为空 - */ -int queue_is_empty() { - return count == 0; -} - -/** - * 获取双向链表的大小 - * @return size - */ -int queue_size() { - return count; -} - -/** - * 获取链表中指定位置的节点 - */ - -static node* get_node(int index) { - if (index < 0 || index >= count) { - return NULL; - } - if (index <= (count / 2)) { - int i = 0; - node *pnode = phead->next; - while ((i++) < index) - pnode = pnode->next; - return pnode; - } - int j = 0; - int rindex = count - index - 1; - node *rnode = phead->prev; - while ((j++) < rindex) - rnode = rnode->prev; - return rnode; -} - -/** - * 获取链表中指定位置的元素 - * @param index index - * @return 指定位置的元素 - */ -void* queue_get(int index) { - node *pindex = get_node(index); - if (!pindex) { - return NULL; - } - return pindex->p; -} - -/** - * 获取链表中第1个元素的值 - * @return 第1个元素的值 - */ -void* queue_get_first() { - return queue_get(0); -} - -/** - * 获取链表中最后1个元素的值 - * @return 最后1个元素的值 - */ -void* queue_get_last() { - return queue_get(count - 1); -} - -/** - * 插入到index位置 - * @param index index - * @param pval pval - * @return 是否插入成功 - */ -int queue_insert(int index, void* pval) { - // 插入表头 - if (index == 0) - return queue_insert_first(pval); - // 获取要插入的位置对应的节点 - node *pindex = get_node(index); - if (!pindex) - return -1; -// 创建“节点” - node *pnode = create_node(pval); - if (!pnode) - return -1; - pnode->prev = pindex->prev; - pnode->next = pindex; - pindex->prev->next = pnode; - pindex->prev = pnode; - // 节点个数+1 - count++; - return 0; -} - -/** - * 插入到链表头 - * @param pval - * @return 是否插入成功 - */ -int queue_insert_first(void *pval) { - node *pnode = create_node(pval); - if (!pnode) - return -1; - pnode->prev = phead; - pnode->next = phead->next; - phead->next->prev = pnode; - phead->next = pnode; - count++; - return 0; -} - -/** - * 插入链表尾 - * @param pval - * @return 是否插入成功 - */ -int queue_append_last(void *pval) { - node *pnode = create_node(pval); - if (!pnode) - return -1; - pnode->next = phead; - pnode->prev = phead->prev; - phead->prev->next = pnode; - phead->prev = pnode; - count++; - return 0; -} - -/** - * 删除双向链表中index位置的节点 - * @param index index - * @return 成功,返回0;否则,返回-1 - */ -int queue_delete(int index) { - node *pindex = get_node(index); - if (!pindex) { - return -1; - } - pindex->next->prev = pindex->prev; - pindex->prev->next = pindex->next; - free(pindex); - count--; - return 0; -} - -/** - * 删除第一个节点 - */ -int queue_delete_first() { - return queue_delete(0); -} - -/* - * 删除最后一个节点 - */ -int queue_delete_last() { - return queue_delete(count - 1); -} - -/** - * 释放双向链表 - * @return 成功,返回0;否则,返回-1。 - */ -int destroy_queue() { - if (!phead) { - return -1; - } - node *pnode = phead->next; - node *ptmp = NULL; - while (pnode != phead) { - ptmp = pnode; - pnode = pnode->next; - free(ptmp); - } - free(phead); - phead = NULL; - count = 0; - return 0; -} diff --git a/Live/src/main/cpp/queue.h b/Live/src/main/cpp/queue.h deleted file mode 100644 index 1dbdd29..0000000 --- a/Live/src/main/cpp/queue.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _QUQEUE_H -#define _QUQEUE_H -// 新建“双向链表”。成功,返回表头;否则,返回NULL -extern int create_queue(); -// 撤销“双向链表”。成功,返回0;否则,返回-1 -extern int destroy_queue(); -// “双向链表是否为空”。为空的话返回1;否则,返回0。 -extern int queue_is_empty(); -// 返回“双向链表的大小” -extern int queue_size(); -// 获取“双向链表中第index位置的元素”。成功,返回节点指针;否则,返回NULL。 -extern void* queue_get(int index); -// 获取“双向链表中第1个元素”。成功,返回节点指针;否则,返回NULL。 -extern void* queue_get_first(); -// 获取“双向链表中最后1个元素”。成功,返回节点指针;否则,返回NULL。 -extern void* queue_get_last(); -// 将“value”插入到index位置。成功,返回0;否则,返回-1。 -extern int queue_insert(int index, void *pval); -// 将“value”插入到表头位置。成功,返回0;否则,返回-1。 -extern int queue_insert_first(void *pval); -// 将“value”插入到末尾位置。成功,返回0;否则,返回-1。 -extern int queue_append_last(void *pval); -// 删除“双向链表中index位置的节点”。成功,返回0;否则,返回-1 -extern int queue_delete(int index); -// 删除第一个节点。成功,返回0;否则,返回-1 -extern int queue_delete_first(); -// 删除组后一个节点。成功,返回0;否则,返回-1 -extern int queue_delete_last(); -#endif//_QUQEUE_H diff --git a/Live/src/main/java/com/frank/live/LiveUtil.java b/Live/src/main/java/com/frank/live/LiveUtil.java deleted file mode 100644 index c83ede0..0000000 --- a/Live/src/main/java/com/frank/live/LiveUtil.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.frank.live; - -import com.frank.live.listener.LiveStateChangeListener; - -/** - * 直播核心类 - * Created by frank on 2017/8/15. - */ - -public class LiveUtil { - - static { - System.loadLibrary("live"); - } - - private native int native_start(String url); - - private native void setVideoParam(int width, int height, int bitRate, int frameRate); - - private native void setAudioParam(int sampleRate, int numChannels); - - private native void pushVideoNew(byte[] yPlane, byte[] uPlane, byte[] vPlane); - - private native void pushAudio(byte[] data, int length); - - private native void native_stop(); - - private native void native_release(); - - //视频编码器打开失败 - private final static int ERROR_VIDEO_ENCODER_OPEN = 0x01; - //视频帧编码失败 - private final static int ERROR_VIDEO_ENCODE = 0x02; - //音频编码器打开失败 - private final static int ERROR_AUDIO_ENCODER_OPEN = 0x03; - //音频帧编码失败 - private final static int ERROR_AUDIO_ENCODE = 0x04; - //RTMP连接失败 - private final static int ERROR_RTMP_CONNECT = 0x05; - //RTMP连接流失败 - private final static int ERROR_RTMP_CONNECT_STREAM = 0x06; - //RTMP发送数据包失败 - private final static int ERROR_RTMP_SEND_PACKAT = 0x07; - - private LiveStateChangeListener liveStateChangeListener; - - public LiveUtil() { - } - - public int startPush(String url) { - return native_start(url); - } - - public void setVideoParams(int width, int height, int bitRate, int frameRate) { - setVideoParam(width, height, bitRate, frameRate); - } - - public void setAudioParams(int sampleRate, int numChannels) { - setAudioParam(sampleRate, numChannels); - } - - public void pushVideoData(byte[] yPlane, byte[] uPlane, byte[] vPlane) { - pushVideoNew(yPlane, uPlane, vPlane); - } - - public void pushAudioData(byte[] data, int length) { - pushAudio(data, length); - } - - public void stopPush() { - native_stop(); - } - - public void release() { - native_release(); - } - - public void setOnLiveStateChangeListener(LiveStateChangeListener liveStateChangeListener) { - this.liveStateChangeListener = liveStateChangeListener; - } - - /** - * 当native报错时,回调这个方法 - * - * @param errCode errCode - */ - public void errorFromNative(int errCode) { - //直播出错了,应该停止推流 - stopPush(); - if (liveStateChangeListener != null) { - String msg = ""; - switch (errCode) { - case ERROR_VIDEO_ENCODER_OPEN: - msg = "视频编码器打开失败..."; - break; - case ERROR_VIDEO_ENCODE: - msg = "视频帧编码失败..."; - break; - case ERROR_AUDIO_ENCODER_OPEN: - msg = "音频编码器打开失败..."; - break; - case ERROR_AUDIO_ENCODE: - msg = "音频帧编码失败..."; - break; - case ERROR_RTMP_CONNECT: - msg = "RTMP连接失败..."; - break; - case ERROR_RTMP_CONNECT_STREAM: - msg = "RTMP连接流失败..."; - break; - case ERROR_RTMP_SEND_PACKAT: - msg = "RTMP发送数据包失败..."; - break; - default: - break; - } - liveStateChangeListener.onError(msg); - } - } -} diff --git a/Live/src/main/java/com/frank/live/Push/AudioPusher.java b/Live/src/main/java/com/frank/live/Push/AudioPusher.java deleted file mode 100644 index 426dca4..0000000 --- a/Live/src/main/java/com/frank/live/Push/AudioPusher.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.frank.live.Push; - -import android.media.AudioRecord; -import android.media.MediaRecorder; -import android.util.Log; -import com.frank.live.LiveUtil; -import com.frank.live.param.AudioParam; - -/** - * 音频推流 - * Created by frank on 2018/1/28. - */ - -public class AudioPusher extends Pusher { - - private AudioRecord audioRecord; - private boolean isPushing; - private int minBufferSize; - private LiveUtil liveUtil; - private boolean isMute; - - AudioPusher(AudioParam audioParam, LiveUtil liveUtil){ - this.liveUtil = liveUtil; - initAudioRecord(audioParam); - liveUtil.setAudioParams(audioParam.getSampleRate(), audioParam.getNumChannels()); - } - - @Override - public void startPush() { - isPushing = true; - new AudioRecordThread(audioRecord).start(); - } - - @Override - public void stopPush() { - isPushing = false; - } - - @Override - public void release() { - stopPush(); - if(audioRecord != null){ - audioRecord.release(); - audioRecord = null; - } - } - - private void initAudioRecord(AudioParam audioParam){ - minBufferSize = AudioRecord.getMinBufferSize(audioParam.getSampleRate(), - audioParam.getChannelConfig(), audioParam.getAudioFormat()); - audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, audioParam.getSampleRate(), - audioParam.getChannelConfig(), audioParam.getAudioFormat(), minBufferSize); - } - - /** - * 录音线程:循环读取音频数据送去native层编码 - */ - class AudioRecordThread extends Thread{ - private AudioRecord audioRecord; - - AudioRecordThread(AudioRecord audioRecord){ - this.audioRecord = audioRecord; - } - - @Override - public void run() { - super.run(); - audioRecord.startRecording(); - while (isPushing){//处于推流状态 - if(!isMute){//如果不静音,才推音频流 - byte[] audioBuffer = new byte[minBufferSize]; - int length = audioRecord.read(audioBuffer, 0, audioBuffer.length); - if(length > 0){ -// Log.i("AudioPusher", "is recording..."); - liveUtil.pushAudioData(audioBuffer, length); - } - } - } - audioRecord.stop(); - } - } - - /** - * 设置静音 - * @param isMute 是否静音 - */ - void setMute(boolean isMute){ - this.isMute = isMute; - } - -} diff --git a/Live/src/main/java/com/frank/live/Push/LivePusher.java b/Live/src/main/java/com/frank/live/Push/LivePusher.java deleted file mode 100644 index 38cef55..0000000 --- a/Live/src/main/java/com/frank/live/Push/LivePusher.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.frank.live.Push; - -import android.content.Context; -import android.util.Log; -import android.view.TextureView; - -import com.frank.live.LiveUtil; -import com.frank.live.listener.LiveStateChangeListener; -import com.frank.live.param.AudioParam; -import com.frank.live.param.VideoParam; - -/** - * 音视频推流 - * Created by frank on 2018/1/28. - */ - -public class LivePusher { - - private VideoPusherNew videoPusher; - private AudioPusher audioPusher; - private LiveUtil liveUtil; - - public LivePusher(TextureView textureView, VideoParam videoParam, AudioParam audioParam, Context context) { - liveUtil = new LiveUtil(); - videoPusher = new VideoPusherNew(textureView, videoParam, liveUtil, context); - audioPusher = new AudioPusher(audioParam, liveUtil); - } - - /** - * 开始推流 - */ - public void startPush(String liveUrl, LiveStateChangeListener liveStateChangeListener) { - videoPusher.startPush(); - audioPusher.startPush(); - liveUtil.setOnLiveStateChangeListener(liveStateChangeListener); - int result = liveUtil.startPush(liveUrl); - Log.i("LivePusher", "startPush=" + (result == 0 ? "success" : "fail")); - } - - /** - * 停止推流 - */ - public void stopPush() { - videoPusher.stopPush(); - audioPusher.stopPush(); - liveUtil.stopPush(); - } - - /** - * 切换摄像头 - */ - public void switchCamera() { - videoPusher.switchCamera(); - } - - /** - * 释放资源 - */ - public void release() { - videoPusher.release(); - audioPusher.release(); - liveUtil.release(); - } - - /** - * 设置静音 - * - * @param isMute 是否静音 - */ - public void setMute(boolean isMute) { - audioPusher.setMute(isMute); - } - -} diff --git a/Live/src/main/java/com/frank/live/Push/Pusher.java b/Live/src/main/java/com/frank/live/Push/Pusher.java deleted file mode 100644 index 73b6dfd..0000000 --- a/Live/src/main/java/com/frank/live/Push/Pusher.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.frank.live.Push; - -/** - * - * Created by frank on 2018/1/28. - */ - -public abstract class Pusher { - public abstract void startPush(); - public abstract void stopPush(); - public abstract void release(); -} diff --git a/Live/src/main/java/com/frank/live/Push/VideoPusherNew.java b/Live/src/main/java/com/frank/live/Push/VideoPusherNew.java deleted file mode 100644 index b04e7cf..0000000 --- a/Live/src/main/java/com/frank/live/Push/VideoPusherNew.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.frank.live.Push; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Point; -import android.graphics.SurfaceTexture; -import android.util.Log; -import android.util.Size; -import android.view.TextureView; - -import com.frank.live.LiveUtil; -import com.frank.live.camera2.Camera2Helper; -import com.frank.live.camera2.Camera2Listener; -import com.frank.live.param.VideoParam; - -/** - * 视频推流:使用Camera2 - * Created by frank on 2019/12/18. - */ - -public class VideoPusherNew extends Pusher implements TextureView.SurfaceTextureListener, Camera2Listener { - private final static String TAG = VideoPusherNew.class.getSimpleName(); - - private VideoParam mVideoParam; - private Camera2Helper camera2Helper; - private boolean isPushing; - private LiveUtil mLiveUtil; - private TextureView mTextureView; - private Context mContext; - - VideoPusherNew(TextureView textureView, VideoParam videoParam, LiveUtil liveUtil, Context context) { - this.mTextureView = textureView; - this.mVideoParam = videoParam; - this.mLiveUtil = liveUtil; - this.mContext = context; - mTextureView.setSurfaceTextureListener(this); -// liveUtil.setVideoParams(videoParam.getWidth(), videoParam.getHeight(), -// videoParam.getBitRate(), videoParam.getFrameRate()); - } - - - @Override - public void startPush() { - isPushing = true; - } - - @Override - public void stopPush() { - isPushing = false; - } - - @Override - public void release() { - stopPush(); - releasePreview(); - } - - /** - * 开始预览 - */ - private void startPreview() { - int rotateDegree = 0; - if (mContext instanceof Activity) { - rotateDegree = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation(); - } - Log.e(TAG, "preview width=" + mTextureView.getWidth() + "--height=" + mTextureView.getHeight()); - camera2Helper = new Camera2Helper.Builder() - .cameraListener(this) - .specificCameraId(Camera2Helper.CAMERA_ID_BACK) - .context(mContext.getApplicationContext()) - .previewOn(mTextureView) -// .previewViewSize(new Point(mTextureView.getWidth(), mTextureView.getHeight())) - .previewViewSize(new Point(mVideoParam.getWidth(), mVideoParam.getHeight())) - .rotation(rotateDegree) - .build(); - camera2Helper.start(); - } - - /** - * 停止预览 - */ - private void stopPreview() { - if (camera2Helper != null) { - camera2Helper.stop(); - } - } - - /** - * 释放资源 - */ - private void releasePreview() { - if (camera2Helper != null) { - camera2Helper.stop(); - camera2Helper.release(); - camera2Helper = null; - } - } - - /** - * 切换摄像头 - */ - void switchCamera() { - if (camera2Helper != null) { - camera2Helper.switchCamera(); - } - } - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.e(TAG, "onSurfaceTextureAvailable..."); - startPreview(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - Log.e(TAG, "onSurfaceTextureDestroyed..."); - stopPreview(); - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - - @Override - public void onPreviewFrame(byte[] y, byte[] u, byte[] v) { - if (isPushing && mLiveUtil != null) { - mLiveUtil.pushVideoData(y, u, v); - } - } - - @Override - public void onCameraOpened(Size previewSize, int displayOrientation) { - Log.e(TAG, "onCameraOpened previewSize=" + previewSize.toString()); - if (mLiveUtil != null && mVideoParam != null) { - mLiveUtil.setVideoParams(previewSize.getWidth(), previewSize.getHeight(), - mVideoParam.getBitRate(), mVideoParam.getFrameRate()); - } - } - - @Override - public void onCameraClosed() { - Log.e(TAG, "onCameraClosed"); - } - - @Override - public void onCameraError(Exception e) { - Log.e(TAG, "onCameraError=" + e.toString()); - } - -}