remove unuse code

remove unuse code
pull/107/head
xufulong 5 years ago
parent 10ad4172d4
commit a76ce7e52f
  1. 6
      Live/CMakeLists.txt
  2. 490
      Live/src/main/cpp/live.c
  3. 218
      Live/src/main/cpp/queue.c
  4. 29
      Live/src/main/cpp/queue.h
  5. 120
      Live/src/main/java/com/frank/live/LiveUtil.java
  6. 91
      Live/src/main/java/com/frank/live/Push/AudioPusher.java
  7. 74
      Live/src/main/java/com/frank/live/Push/LivePusher.java
  8. 12
      Live/src/main/java/com/frank/live/Push/Pusher.java
  9. 157
      Live/src/main/java/com/frank/live/Push/VideoPusherNew.java

@ -32,12 +32,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
include_directories(main/cpp/include) include_directories(main/cpp/include)
add_library(live add_library(live
SHARED SHARED
src/main/cpp/live.c
src/main/cpp/queue.c
src/main/cpp/AudioStream.cpp src/main/cpp/AudioStream.cpp
src/main/cpp/VideoStream.cpp src/main/cpp/VideoStream.cpp
src/main/cpp/RtmpPusher.cpp) src/main/cpp/RtmpPusher.cpp)
find_library( log-lib find_library( log-lib
log ) log )

@ -1,490 +0,0 @@
#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);
}

@ -1,218 +0,0 @@
#include <stdio.h>
#include <malloc.h>
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;
}

@ -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

@ -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);
}
}
}

@ -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;
}
}

@ -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);
}
}

@ -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();
}

@ -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());
}
}
Loading…
Cancel
Save