diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 7ac24c7..7912152 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -8,6 +8,7 @@
diff --git a/.idea/modules.xml b/.idea/modules.xml
index fb2aa17..7676871 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
+
diff --git a/Live/.gitignore b/Live/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Live/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Live/CMakeLists.txt b/Live/CMakeLists.txt
new file mode 100644
index 0000000..d34f9d0
--- /dev/null
+++ b/Live/CMakeLists.txt
@@ -0,0 +1,45 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.4.1)
+set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
+
+add_library(faac
+ STATIC
+ IMPORTED)
+set_target_properties(faac
+ PROPERTIES IMPORTED_LOCATION
+ ../../../../libs/armeabi-v7a/libfaac.a)
+
+add_library(rtmp
+ STATIC
+ IMPORTED)
+set_target_properties(rtmp
+ PROPERTIES IMPORTED_LOCATION
+ ../../../../libs/armeabi-v7a/librtmp.a)
+
+add_library(x264
+ STATIC
+ IMPORTED)
+set_target_properties(x264
+ PROPERTIES IMPORTED_LOCATION
+ ../../../../libs/armeabi-v7a/libx264.a)
+
+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)
+
+find_library( log-lib
+ log )
+
+target_link_libraries(live x264 faac rtmp
+ -landroid
+ -ljnigraphics
+ ${log-lib} )
\ No newline at end of file
diff --git a/Live/build.gradle b/Live/build.gradle
new file mode 100644
index 0000000..e6d994b
--- /dev/null
+++ b/Live/build.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.2"
+ defaultConfig {
+// applicationId "com.frank.live"
+ minSdkVersion 15
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ }
+ }
+ ndk{
+ abiFilters "armeabi-v7a"
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.1'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Live/libs/armeabi-v7a/libfaac.a b/Live/libs/armeabi-v7a/libfaac.a
new file mode 100644
index 0000000..f6d4452
Binary files /dev/null and b/Live/libs/armeabi-v7a/libfaac.a differ
diff --git a/Live/libs/armeabi-v7a/librtmp.a b/Live/libs/armeabi-v7a/librtmp.a
new file mode 100644
index 0000000..c9222af
Binary files /dev/null and b/Live/libs/armeabi-v7a/librtmp.a differ
diff --git a/Live/libs/armeabi-v7a/libx264.a b/Live/libs/armeabi-v7a/libx264.a
new file mode 100644
index 0000000..b7c90f0
Binary files /dev/null and b/Live/libs/armeabi-v7a/libx264.a differ
diff --git a/Live/proguard-rules.pro b/Live/proguard-rules.pro
new file mode 100644
index 0000000..316ea84
--- /dev/null
+++ b/Live/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\frank\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/Live/src/androidTest/java/com/frank/live/ExampleInstrumentedTest.java b/Live/src/androidTest/java/com/frank/live/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68f66e2
--- /dev/null
+++ b/Live/src/androidTest/java/com/frank/live/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.frank.live;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.frank.pusher", appContext.getPackageName());
+ }
+}
diff --git a/Live/src/main/AndroidManifest.xml b/Live/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fb5db84
--- /dev/null
+++ b/Live/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Live/src/main/cpp/include/faac/faac.h b/Live/src/main/cpp/include/faac/faac.h
new file mode 100644
index 0000000..77c05f1
--- /dev/null
+++ b/Live/src/main/cpp/include/faac/faac.h
@@ -0,0 +1,99 @@
+/*
+ * FAAC - Freeware Advanced Audio Coder
+ * Copyright (C) 2001 Menno Bakker
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: faac.h,v 1.36 2009/01/25 18:50:32 menno Exp $
+ */
+
+#ifndef _FAAC_H_
+#define _FAAC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+# ifndef FAACAPI
+# define FAACAPI __stdcall
+# endif
+#else
+# ifndef FAACAPI
+# define FAACAPI
+# endif
+#endif
+
+#pragma pack(push, 1)
+
+typedef struct {
+ void *ptr;
+ char *name;
+}
+psymodellist_t;
+
+#include "faaccfg.h"
+
+
+typedef void *faacEncHandle;
+
+#ifndef HAVE_INT32_T
+typedef signed int int32_t;
+#endif
+
+/*
+ Allows an application to get FAAC version info. This is intended
+ purely for informative purposes.
+
+ Returns FAAC_CFG_VERSION.
+*/
+int FAACAPI faacEncGetVersion(char **faac_id_string,
+ char **faac_copyright_string);
+
+
+faacEncConfigurationPtr FAACAPI
+ faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
+
+
+int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder,
+ faacEncConfigurationPtr config);
+
+
+faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate,
+ unsigned int numChannels,
+ unsigned long *inputSamples,
+ unsigned long *maxOutputBytes);
+
+
+int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer,
+ unsigned long *pSizeOfDecoderSpecificInfo);
+
+
+int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput,
+ unsigned char *outputBuffer,
+ unsigned int bufferSize);
+
+
+int FAACAPI faacEncClose(faacEncHandle hEncoder);
+
+
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _FAAC_H_ */
diff --git a/Live/src/main/cpp/include/faac/faaccfg.h b/Live/src/main/cpp/include/faac/faaccfg.h
new file mode 100644
index 0000000..001f821
--- /dev/null
+++ b/Live/src/main/cpp/include/faac/faaccfg.h
@@ -0,0 +1,122 @@
+/*
+ * FAAC - Freeware Advanced Audio Coder
+ * Copyright (C) 2001 Menno Bakker
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: faaccfg.h,v 1.3 2004/07/04 12:12:05 corrados Exp $
+ */
+
+#ifndef _FAACCFG_H_
+#define _FAACCFG_H_
+
+#define FAAC_CFG_VERSION 104
+
+/* MPEG ID's */
+#define MPEG2 1
+#define MPEG4 0
+
+/* AAC object types */
+#define MAIN 1
+#define LOW 2
+#define SSR 3
+#define LTP 4
+
+/* Input Formats */
+#define FAAC_INPUT_NULL 0
+#define FAAC_INPUT_16BIT 1
+#define FAAC_INPUT_24BIT 2
+#define FAAC_INPUT_32BIT 3
+#define FAAC_INPUT_FLOAT 4
+
+#define SHORTCTL_NORMAL 0
+#define SHORTCTL_NOSHORT 1
+#define SHORTCTL_NOLONG 2
+
+#pragma pack(push, 1)
+typedef struct faacEncConfiguration
+{
+ /* config version */
+ int version;
+
+ /* library version */
+ char *name;
+
+ /* copyright string */
+ char *copyright;
+
+ /* MPEG version, 2 or 4 */
+ unsigned int mpegVersion;
+
+ /* AAC object type */
+ unsigned int aacObjectType;
+
+ /* Allow mid/side coding */
+ unsigned int allowMidside;
+
+ /* Use one of the channels as LFE channel */
+ unsigned int useLfe;
+
+ /* Use Temporal Noise Shaping */
+ unsigned int useTns;
+
+ /* bitrate / channel of AAC file */
+ unsigned long bitRate;
+
+ /* AAC file frequency bandwidth */
+ unsigned int bandWidth;
+
+ /* Quantizer quality */
+ unsigned long quantqual;
+
+ /* Bitstream output format (0 = Raw; 1 = ADTS) */
+ unsigned int outputFormat;
+
+ /* psychoacoustic model list */
+ psymodellist_t *psymodellist;
+
+ /* selected index in psymodellist */
+ unsigned int psymodelidx;
+
+ /*
+ PCM Sample Input Format
+ 0 FAAC_INPUT_NULL invalid, signifies a misconfigured config
+ 1 FAAC_INPUT_16BIT native endian 16bit
+ 2 FAAC_INPUT_24BIT native endian 24bit in 24 bits (not implemented)
+ 3 FAAC_INPUT_32BIT native endian 24bit in 32 bits (DEFAULT)
+ 4 FAAC_INPUT_FLOAT 32bit floating point
+ */
+ unsigned int inputFormat;
+
+ /* block type enforcing (SHORTCTL_NORMAL/SHORTCTL_NOSHORT/SHORTCTL_NOLONG) */
+ int shortctl;
+
+ /*
+ Channel Remapping
+
+ Default 0, 1, 2, 3 ... 63 (64 is MAX_CHANNELS in coder.h)
+
+ WAVE 4.0 2, 0, 1, 3
+ WAVE 5.0 2, 0, 1, 3, 4
+ WAVE 5.1 2, 0, 1, 4, 5, 3
+ AIFF 5.1 2, 0, 3, 1, 4, 5
+ */
+ int channel_map[64];
+
+} faacEncConfiguration, *faacEncConfigurationPtr;
+
+#pragma pack(pop)
+
+#endif /* _FAACCFG_H_ */
diff --git a/Live/src/main/cpp/include/rtmp/amf.h b/Live/src/main/cpp/include/rtmp/amf.h
new file mode 100644
index 0000000..b945beb
--- /dev/null
+++ b/Live/src/main/cpp/include/rtmp/amf.h
@@ -0,0 +1,161 @@
+#ifndef __AMF_H__
+#define __AMF_H__
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ * Copyright (C) 2008-2009 Andrej Stepanchuk
+ * Copyright (C) 2009-2010 Howard Chu
+ *
+ * This file is part of librtmp.
+ *
+ * librtmp is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1,
+ * or (at your option) any later version.
+ *
+ * librtmp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with librtmp see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef enum
+ { AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT,
+ AMF_MOVIECLIP, /* reserved, not used */
+ AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END,
+ AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED,
+ AMF_RECORDSET, /* reserved, not used */
+ AMF_XML_DOC, AMF_TYPED_OBJECT,
+ AMF_AVMPLUS, /* switch to AMF3 */
+ AMF_INVALID = 0xff
+ } AMFDataType;
+
+ typedef enum
+ { AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE,
+ AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE,
+ AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY
+ } AMF3DataType;
+
+ typedef struct AVal
+ {
+ char *av_val;
+ int av_len;
+ } AVal;
+#define AVC(str) {str,sizeof(str)-1}
+#define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
+
+ struct AMFObjectProperty;
+
+ typedef struct AMFObject
+ {
+ int o_num;
+ struct AMFObjectProperty *o_props;
+ } AMFObject;
+
+ typedef struct AMFObjectProperty
+ {
+ AVal p_name;
+ AMFDataType p_type;
+ union
+ {
+ double p_number;
+ AVal p_aval;
+ AMFObject p_object;
+ } p_vu;
+ int16_t p_UTCoffset;
+ } AMFObjectProperty;
+
+ char *AMF_EncodeString(char *output, char *outend, const AVal * str);
+ char *AMF_EncodeNumber(char *output, char *outend, double dVal);
+ char *AMF_EncodeInt16(char *output, char *outend, short nVal);
+ char *AMF_EncodeInt24(char *output, char *outend, int nVal);
+ char *AMF_EncodeInt32(char *output, char *outend, int nVal);
+ char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
+
+ /* Shortcuts for AMFProp_Encode */
+ char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
+ char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
+ char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
+
+ unsigned short AMF_DecodeInt16(const char *data);
+ unsigned int AMF_DecodeInt24(const char *data);
+ unsigned int AMF_DecodeInt32(const char *data);
+ void AMF_DecodeString(const char *data, AVal * str);
+ void AMF_DecodeLongString(const char *data, AVal * str);
+ int AMF_DecodeBoolean(const char *data);
+ double AMF_DecodeNumber(const char *data);
+
+ char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
+ int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
+ int bDecodeName);
+ int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
+ int nArrayLen, int bDecodeName);
+ int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize,
+ int bDecodeName);
+ void AMF_Dump(AMFObject * obj);
+ void AMF_Reset(AMFObject * obj);
+
+ void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
+ int AMF_CountProp(AMFObject * obj);
+ AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name,
+ int nIndex);
+
+ AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
+ void AMFProp_SetNumber(AMFObjectProperty * prop, double dval);
+ void AMFProp_SetBoolean(AMFObjectProperty * prop, int bflag);
+ void AMFProp_SetString(AMFObjectProperty * prop, AVal * str);
+ void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj);
+
+ void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
+ void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
+ double AMFProp_GetNumber(AMFObjectProperty * prop);
+ int AMFProp_GetBoolean(AMFObjectProperty * prop);
+ void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
+ void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
+
+ int AMFProp_IsValid(AMFObjectProperty * prop);
+
+ char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
+ int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer,
+ int nSize, int bDecodeName);
+ int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer,
+ int nSize, int bDecodeName);
+
+ void AMFProp_Dump(AMFObjectProperty * prop);
+ void AMFProp_Reset(AMFObjectProperty * prop);
+
+ typedef struct AMF3ClassDef
+ {
+ AVal cd_name;
+ char cd_externalizable;
+ char cd_dynamic;
+ int cd_num;
+ AVal *cd_props;
+ } AMF3ClassDef;
+
+ void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop);
+ AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AMF_H__ */
diff --git a/Live/src/main/cpp/include/rtmp/http.h b/Live/src/main/cpp/include/rtmp/http.h
new file mode 100644
index 0000000..cf3d903
--- /dev/null
+++ b/Live/src/main/cpp/include/rtmp/http.h
@@ -0,0 +1,47 @@
+#ifndef __RTMP_HTTP_H__
+#define __RTMP_HTTP_H__
+/*
+ * Copyright (C) 2010 Howard Chu
+ * Copyright (C) 2010 Antti Ajanki
+ *
+ * This file is part of librtmp.
+ *
+ * librtmp is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1,
+ * or (at your option) any later version.
+ *
+ * librtmp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with librtmp see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+typedef enum {
+ HTTPRES_OK, /* result OK */
+ HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */
+ HTTPRES_NOT_FOUND, /* not found */
+ HTTPRES_BAD_REQUEST, /* client error */
+ HTTPRES_SERVER_ERROR, /* server reported an error */
+ HTTPRES_REDIRECTED, /* resource has been moved */
+ HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */
+} HTTPResult;
+
+struct HTTP_ctx {
+ char *date;
+ int size;
+ int status;
+ void *data;
+};
+
+typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
+
+HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb);
+
+#endif
diff --git a/Live/src/main/cpp/include/rtmp/log.h b/Live/src/main/cpp/include/rtmp/log.h
new file mode 100644
index 0000000..97c9aac
--- /dev/null
+++ b/Live/src/main/cpp/include/rtmp/log.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008-2009 Andrej Stepanchuk
+ * Copyright (C) 2009-2010 Howard Chu
+ *
+ * This file is part of librtmp.
+ *
+ * librtmp is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1,
+ * or (at your option) any later version.
+ *
+ * librtmp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with librtmp see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __RTMP_LOG_H__
+#define __RTMP_LOG_H__
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Enable this to get full debugging output */
+/* #define _DEBUG */
+
+#ifdef _DEBUG
+#undef NODEBUG
+#endif
+
+typedef enum
+{ RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO,
+ RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL
+} RTMP_LogLevel;
+
+extern RTMP_LogLevel RTMP_debuglevel;
+
+typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list);
+void RTMP_LogSetCallback(RTMP_LogCallback *cb);
+void RTMP_LogSetOutput(FILE *file);
+void RTMP_LogPrintf(const char *format, ...);
+void RTMP_LogStatus(const char *format, ...);
+void RTMP_Log(int level, const char *format, ...);
+void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
+void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
+void RTMP_LogSetLevel(RTMP_LogLevel lvl);
+RTMP_LogLevel RTMP_LogGetLevel(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Live/src/main/cpp/include/rtmp/rtmp.h b/Live/src/main/cpp/include/rtmp/rtmp.h
new file mode 100644
index 0000000..de0b795
--- /dev/null
+++ b/Live/src/main/cpp/include/rtmp/rtmp.h
@@ -0,0 +1,345 @@
+#ifndef __RTMP_H__
+#define __RTMP_H__
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ * Copyright (C) 2008-2009 Andrej Stepanchuk
+ * Copyright (C) 2009-2010 Howard Chu
+ *
+ * This file is part of librtmp.
+ *
+ * librtmp is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1,
+ * or (at your option) any later version.
+ *
+ * librtmp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with librtmp see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#if !defined(NO_CRYPTO) && !defined(CRYPTO)
+#define CRYPTO
+#endif
+
+#include
+#include
+#include
+
+#include "amf.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define RTMP_LIB_VERSION 0x020300 /* 2.3 */
+
+#define RTMP_FEATURE_HTTP 0x01
+#define RTMP_FEATURE_ENC 0x02
+#define RTMP_FEATURE_SSL 0x04
+#define RTMP_FEATURE_MFP 0x08 /* not yet supported */
+#define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
+#define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
+
+#define RTMP_PROTOCOL_UNDEFINED -1
+#define RTMP_PROTOCOL_RTMP 0
+#define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
+#define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
+#define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
+#define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
+#define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
+#define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
+
+#define RTMP_DEFAULT_CHUNKSIZE 128
+
+/* needs to fit largest number of bytes recv() may return */
+#define RTMP_BUFFER_CACHE_SIZE (16*1024)
+
+#define RTMP_CHANNELS 65600
+
+ extern const char RTMPProtocolStringsLower[][7];
+ extern const AVal RTMP_DefaultFlashVer;
+ extern int RTMP_ctrlC;
+
+ uint32_t RTMP_GetTime(void);
+
+#define RTMP_PACKET_TYPE_AUDIO 0x08
+#define RTMP_PACKET_TYPE_VIDEO 0x09
+#define RTMP_PACKET_TYPE_INFO 0x12
+
+#define RTMP_MAX_HEADER_SIZE 18
+
+#define RTMP_PACKET_SIZE_LARGE 0
+#define RTMP_PACKET_SIZE_MEDIUM 1
+#define RTMP_PACKET_SIZE_SMALL 2
+#define RTMP_PACKET_SIZE_MINIMUM 3
+
+ typedef struct RTMPChunk
+ {
+ int c_headerSize;
+ int c_chunkSize;
+ char *c_chunk;
+ char c_header[RTMP_MAX_HEADER_SIZE];
+ } RTMPChunk;
+
+ typedef struct RTMPPacket
+ {
+ uint8_t m_headerType;
+ uint8_t m_packetType;
+ uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
+ int m_nChannel;
+ uint32_t m_nTimeStamp; /* timestamp */
+ int32_t m_nInfoField2; /* last 4 bytes in a long header */
+ uint32_t m_nBodySize;
+ uint32_t m_nBytesRead;
+ RTMPChunk *m_chunk;
+ char *m_body;
+ } RTMPPacket;
+
+ typedef struct RTMPSockBuf
+ {
+ int sb_socket;
+ int sb_size; /* number of unprocessed bytes in buffer */
+ char *sb_start; /* pointer into sb_pBuffer of next byte to process */
+ char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
+ int sb_timedout;
+ void *sb_ssl;
+ } RTMPSockBuf;
+
+ void RTMPPacket_Reset(RTMPPacket *p);
+ void RTMPPacket_Dump(RTMPPacket *p);
+ int RTMPPacket_Alloc(RTMPPacket *p, int nSize);
+ void RTMPPacket_Free(RTMPPacket *p);
+
+#define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)
+
+ typedef struct RTMP_LNK
+ {
+ AVal hostname;
+ AVal sockshost;
+
+ AVal playpath0; /* parsed from URL */
+ AVal playpath; /* passed in explicitly */
+ AVal tcUrl;
+ AVal swfUrl;
+ AVal pageUrl;
+ AVal app;
+ AVal auth;
+ AVal flashVer;
+ AVal subscribepath;
+ AVal token;
+ AMFObject extras;
+ int edepth;
+
+ int seekTime;
+ int stopTime;
+
+#define RTMP_LF_AUTH 0x0001 /* using auth param */
+#define RTMP_LF_LIVE 0x0002 /* stream is live */
+#define RTMP_LF_SWFV 0x0004 /* do SWF verification */
+#define RTMP_LF_PLST 0x0008 /* send playlist before play */
+#define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
+#define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
+ int lFlags;
+
+ int swfAge;
+
+ int protocol;
+ int timeout; /* connection timeout in seconds */
+
+ unsigned short socksport;
+ unsigned short port;
+
+#ifdef CRYPTO
+#define RTMP_SWF_HASHLEN 32
+ void *dh; /* for encryption */
+ void *rc4keyIn;
+ void *rc4keyOut;
+
+ uint32_t SWFSize;
+ uint8_t SWFHash[RTMP_SWF_HASHLEN];
+ char SWFVerificationResponse[RTMP_SWF_HASHLEN+10];
+#endif
+ } RTMP_LNK;
+
+ /* state for read() wrapper */
+ typedef struct RTMP_READ
+ {
+ char *buf;
+ char *bufpos;
+ unsigned int buflen;
+ uint32_t timestamp;
+ uint8_t dataType;
+ uint8_t flags;
+#define RTMP_READ_HEADER 0x01
+#define RTMP_READ_RESUME 0x02
+#define RTMP_READ_NO_IGNORE 0x04
+#define RTMP_READ_GOTKF 0x08
+#define RTMP_READ_GOTFLVK 0x10
+#define RTMP_READ_SEEKING 0x20
+ int8_t status;
+#define RTMP_READ_COMPLETE -3
+#define RTMP_READ_ERROR -2
+#define RTMP_READ_EOF -1
+#define RTMP_READ_IGNORE 0
+
+ /* if bResume == TRUE */
+ uint8_t initialFrameType;
+ uint32_t nResumeTS;
+ char *metaHeader;
+ char *initialFrame;
+ uint32_t nMetaHeaderSize;
+ uint32_t nInitialFrameSize;
+ uint32_t nIgnoredFrameCounter;
+ uint32_t nIgnoredFlvFrameCounter;
+ } RTMP_READ;
+
+ typedef struct RTMP_METHOD
+ {
+ AVal name;
+ int num;
+ } RTMP_METHOD;
+
+ typedef struct RTMP
+ {
+ int m_inChunkSize;
+ int m_outChunkSize;
+ int m_nBWCheckCounter;
+ int m_nBytesIn;
+ int m_nBytesInSent;
+ int m_nBufferMS;
+ int m_stream_id; /* returned in _result from createStream */
+ int m_mediaChannel;
+ uint32_t m_mediaStamp;
+ uint32_t m_pauseStamp;
+ int m_pausing;
+ int m_nServerBW;
+ int m_nClientBW;
+ uint8_t m_nClientBW2;
+ uint8_t m_bPlaying;
+ uint8_t m_bSendEncoding;
+ uint8_t m_bSendCounter;
+
+ int m_numInvokes;
+ int m_numCalls;
+ RTMP_METHOD *m_methodCalls; /* remote method calls queue */
+
+ RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
+ RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
+ int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */
+
+ double m_fAudioCodecs; /* audioCodecs for the connect packet */
+ double m_fVideoCodecs; /* videoCodecs for the connect packet */
+ double m_fEncoding; /* AMF0 or AMF3 */
+
+ double m_fDuration; /* duration of stream in seconds */
+
+ int m_msgCounter; /* RTMPT stuff */
+ int m_polling;
+ int m_resplen;
+ int m_unackd;
+ AVal m_clientID;
+
+ RTMP_READ m_read;
+ RTMPPacket m_write;
+ RTMPSockBuf m_sb;
+ RTMP_LNK Link;
+ } RTMP;
+
+ int RTMP_ParseURL(const char *url, int *protocol, AVal *host,
+ unsigned int *port, AVal *playpath, AVal *app);
+
+ void RTMP_ParsePlaypath(AVal *in, AVal *out);
+ void RTMP_SetBufferMS(RTMP *r, int size);
+ void RTMP_UpdateBufferMS(RTMP *r);
+
+ int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);
+ int RTMP_SetupURL(RTMP *r, char *url);
+ void RTMP_SetupStream(RTMP *r, int protocol,
+ AVal *hostname,
+ unsigned int port,
+ AVal *sockshost,
+ AVal *playpath,
+ AVal *tcUrl,
+ AVal *swfUrl,
+ AVal *pageUrl,
+ AVal *app,
+ AVal *auth,
+ AVal *swfSHA256Hash,
+ uint32_t swfSize,
+ AVal *flashVer,
+ AVal *subscribepath,
+ int dStart,
+ int dStop, int bLiveStream, long int timeout);
+
+ int RTMP_Connect(RTMP *r, RTMPPacket *cp);
+ struct sockaddr;
+ int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
+ int RTMP_Connect1(RTMP *r, RTMPPacket *cp);
+ int RTMP_Serve(RTMP *r);
+
+ int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
+ int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
+ int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);
+ int RTMP_IsConnected(RTMP *r);
+ int RTMP_Socket(RTMP *r);
+ int RTMP_IsTimedout(RTMP *r);
+ double RTMP_GetDuration(RTMP *r);
+ int RTMP_ToggleStream(RTMP *r);
+
+ int RTMP_ConnectStream(RTMP *r, int seekTime);
+ int RTMP_ReconnectStream(RTMP *r, int seekTime);
+ void RTMP_DeleteStream(RTMP *r);
+ int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet);
+ int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet);
+
+ void RTMP_Init(RTMP *r);
+ void RTMP_Close(RTMP *r);
+ RTMP *RTMP_Alloc(void);
+ void RTMP_Free(RTMP *r);
+ void RTMP_EnableWrite(RTMP *r);
+
+ int RTMP_LibVersion(void);
+ void RTMP_UserInterrupt(void); /* user typed Ctrl-C */
+
+ int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject,
+ unsigned int nTime);
+
+ /* caller probably doesn't know current timestamp, should
+ * just use RTMP_Pause instead
+ */
+ int RTMP_SendPause(RTMP *r, int DoPause, int dTime);
+ int RTMP_Pause(RTMP *r, int DoPause);
+
+ int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
+ AMFObjectProperty * p);
+
+ int RTMPSockBuf_Fill(RTMPSockBuf *sb);
+ int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
+ int RTMPSockBuf_Close(RTMPSockBuf *sb);
+
+ int RTMP_SendCreateStream(RTMP *r);
+ int RTMP_SendSeek(RTMP *r, int dTime);
+ int RTMP_SendServerBW(RTMP *r);
+ int RTMP_SendClientBW(RTMP *r);
+ void RTMP_DropRequest(RTMP *r, int i, int freeit);
+ int RTMP_Read(RTMP *r, char *buf, int size);
+ int RTMP_Write(RTMP *r, const char *buf, int size);
+
+/* hashswf.c */
+ int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
+ int age);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/Live/src/main/cpp/include/x264/x264.h b/Live/src/main/cpp/include/x264/x264.h
new file mode 100644
index 0000000..3804820
--- /dev/null
+++ b/Live/src/main/cpp/include/x264/x264.h
@@ -0,0 +1,962 @@
+/*****************************************************************************
+ * include.x264.h: include.x264 public header
+ *****************************************************************************
+ * Copyright (C) 2003-2016 include.x264 project
+ *
+ * Authors: Laurent Aimar
+ * Loren Merritt
+ * Fiona Glaser
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
+ *
+ * This program is also available under a commercial proprietary license.
+ * For more information, contact us at licensing@include.x264.com.
+ *****************************************************************************/
+
+#ifndef X264_X264_H
+#define X264_X264_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(_STDINT_H) && !defined(_STDINT_H_) && !defined(_STDINT_H_INCLUDED) && !defined(_STDINT) &&\
+ !defined(_SYS_STDINT_H_) && !defined(_INTTYPES_H) && !defined(_INTTYPES_H_) && !defined(_INTTYPES)
+# ifdef _MSC_VER
+# pragma message("You must include stdint.h or inttypes.h before include.x264.h")
+# else
+# warning You must include stdint.h or inttypes.h before x264.h
+# endif
+#endif
+
+#include
+#include
+
+#include "x264_config.h"
+
+#define X264_BUILD 148
+
+/* Application developers planning to link against a shared library version of
+ * libx264 from a Microsoft Visual Studio or similar development environment
+ * will need to define X264_API_IMPORTS before including this header.
+ * This clause does not apply to MinGW, similar development environments, or non
+ * Windows platforms. */
+#ifdef X264_API_IMPORTS
+#define X264_API __declspec(dllimport)
+#else
+#define X264_API
+#endif
+
+/* x264_t:
+ * opaque handler for encoder */
+typedef struct x264_t x264_t;
+
+/****************************************************************************
+ * NAL structure and functions
+ ****************************************************************************/
+
+enum nal_unit_type_e
+{
+ NAL_UNKNOWN = 0,
+ NAL_SLICE = 1,
+ NAL_SLICE_DPA = 2,
+ NAL_SLICE_DPB = 3,
+ NAL_SLICE_DPC = 4,
+ NAL_SLICE_IDR = 5, /* ref_idc != 0 */
+ NAL_SEI = 6, /* ref_idc == 0 */
+ NAL_SPS = 7,
+ NAL_PPS = 8,
+ NAL_AUD = 9,
+ NAL_FILLER = 12,
+ /* ref_idc == 0 for 6,9,10,11,12 */
+};
+enum nal_priority_e
+{
+ NAL_PRIORITY_DISPOSABLE = 0,
+ NAL_PRIORITY_LOW = 1,
+ NAL_PRIORITY_HIGH = 2,
+ NAL_PRIORITY_HIGHEST = 3,
+};
+
+/* The data within the payload is already NAL-encapsulated; the ref_idc and type
+ * are merely in the struct for easy access by the calling application.
+ * All data returned in an x264_nal_t, including the data in p_payload, is no longer
+ * valid after the next call to x264_encoder_encode. Thus it must be used or copied
+ * before calling x264_encoder_encode or x264_encoder_headers again. */
+typedef struct x264_nal_t
+{
+ int i_ref_idc; /* nal_priority_e */
+ int i_type; /* nal_unit_type_e */
+ int b_long_startcode;
+ int i_first_mb; /* If this NAL is a slice, the index of the first MB in the slice. */
+ int i_last_mb; /* If this NAL is a slice, the index of the last MB in the slice. */
+
+ /* Size of payload (including any padding) in bytes. */
+ int i_payload;
+ /* If param->b_annexb is set, Annex-B bytestream with startcode.
+ * Otherwise, startcode is replaced with a 4-byte size.
+ * This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */
+ uint8_t *p_payload;
+
+ /* Size of padding in bytes. */
+ int i_padding;
+} x264_nal_t;
+
+/****************************************************************************
+ * Encoder parameters
+ ****************************************************************************/
+/* CPU flags */
+
+/* x86 */
+#define X264_CPU_CMOV 0x0000001
+#define X264_CPU_MMX 0x0000002
+#define X264_CPU_MMX2 0x0000004 /* MMX2 aka MMXEXT aka ISSE */
+#define X264_CPU_MMXEXT X264_CPU_MMX2
+#define X264_CPU_SSE 0x0000008
+#define X264_CPU_SSE2 0x0000010
+#define X264_CPU_SSE3 0x0000020
+#define X264_CPU_SSSE3 0x0000040
+#define X264_CPU_SSE4 0x0000080 /* SSE4.1 */
+#define X264_CPU_SSE42 0x0000100 /* SSE4.2 */
+#define X264_CPU_LZCNT 0x0000200 /* Phenom support for "leading zero count" instruction. */
+#define X264_CPU_AVX 0x0000400 /* AVX support: requires OS support even if YMM registers aren't used. */
+#define X264_CPU_XOP 0x0000800 /* AMD XOP */
+#define X264_CPU_FMA4 0x0001000 /* AMD FMA4 */
+#define X264_CPU_FMA3 0x0002000 /* FMA3 */
+#define X264_CPU_AVX2 0x0004000 /* AVX2 */
+#define X264_CPU_BMI1 0x0008000 /* BMI1 */
+#define X264_CPU_BMI2 0x0010000 /* BMI2 */
+/* x86 modifiers */
+#define X264_CPU_CACHELINE_32 0x0020000 /* avoid memory loads that span the border between two cachelines */
+#define X264_CPU_CACHELINE_64 0x0040000 /* 32/64 is the size of a cacheline in bytes */
+#define X264_CPU_SSE2_IS_SLOW 0x0080000 /* avoid most SSE2 functions on Athlon64 */
+#define X264_CPU_SSE2_IS_FAST 0x0100000 /* a few functions are only faster on Core2 and Phenom */
+#define X264_CPU_SLOW_SHUFFLE 0x0200000 /* The Conroe has a slow shuffle unit (relative to overall SSE performance) */
+#define X264_CPU_STACK_MOD4 0x0400000 /* if stack is only mod4 and not mod16 */
+#define X264_CPU_SLOW_CTZ 0x0800000 /* BSR/BSF x86 instructions are really slow on some CPUs */
+#define X264_CPU_SLOW_ATOM 0x1000000 /* The Atom is terrible: slow SSE unaligned loads, slow
+ * SIMD multiplies, slow SIMD variable shifts, slow pshufb,
+ * cacheline split penalties -- gather everything here that
+ * isn't shared by other CPUs to avoid making half a dozen
+ * new SLOW flags. */
+#define X264_CPU_SLOW_PSHUFB 0x2000000 /* such as on the Intel Atom */
+#define X264_CPU_SLOW_PALIGNR 0x4000000 /* such as on the AMD Bobcat */
+
+/* PowerPC */
+#define X264_CPU_ALTIVEC 0x0000001
+
+/* ARM and AArch64 */
+#define X264_CPU_ARMV6 0x0000001
+#define X264_CPU_NEON 0x0000002 /* ARM NEON */
+#define X264_CPU_FAST_NEON_MRC 0x0000004 /* Transfer from NEON to ARM register is fast (Cortex-A9) */
+#define X264_CPU_ARMV8 0x0000008
+
+/* MIPS */
+#define X264_CPU_MSA 0x0000001 /* MIPS MSA */
+
+/* Analyse flags */
+#define X264_ANALYSE_I4x4 0x0001 /* Analyse i4x4 */
+#define X264_ANALYSE_I8x8 0x0002 /* Analyse i8x8 (requires 8x8 transform) */
+#define X264_ANALYSE_PSUB16x16 0x0010 /* Analyse p16x8, p8x16 and p8x8 */
+#define X264_ANALYSE_PSUB8x8 0x0020 /* Analyse p8x4, p4x8, p4x4 */
+#define X264_ANALYSE_BSUB16x16 0x0100 /* Analyse b16x8, b8x16 and b8x8 */
+#define X264_DIRECT_PRED_NONE 0
+#define X264_DIRECT_PRED_SPATIAL 1
+#define X264_DIRECT_PRED_TEMPORAL 2
+#define X264_DIRECT_PRED_AUTO 3
+#define X264_ME_DIA 0
+#define X264_ME_HEX 1
+#define X264_ME_UMH 2
+#define X264_ME_ESA 3
+#define X264_ME_TESA 4
+#define X264_CQM_FLAT 0
+#define X264_CQM_JVT 1
+#define X264_CQM_CUSTOM 2
+#define X264_RC_CQP 0
+#define X264_RC_CRF 1
+#define X264_RC_ABR 2
+#define X264_QP_AUTO 0
+#define X264_AQ_NONE 0
+#define X264_AQ_VARIANCE 1
+#define X264_AQ_AUTOVARIANCE 2
+#define X264_AQ_AUTOVARIANCE_BIASED 3
+#define X264_B_ADAPT_NONE 0
+#define X264_B_ADAPT_FAST 1
+#define X264_B_ADAPT_TRELLIS 2
+#define X264_WEIGHTP_NONE 0
+#define X264_WEIGHTP_SIMPLE 1
+#define X264_WEIGHTP_SMART 2
+#define X264_B_PYRAMID_NONE 0
+#define X264_B_PYRAMID_STRICT 1
+#define X264_B_PYRAMID_NORMAL 2
+#define X264_KEYINT_MIN_AUTO 0
+#define X264_KEYINT_MAX_INFINITE (1<<30)
+
+static const char * const x264_direct_pred_names[] = { "none", "spatial", "temporal", "auto", 0 };
+static const char * const x264_motion_est_names[] = { "dia", "hex", "umh", "esa", "tesa", 0 };
+static const char * const x264_b_pyramid_names[] = { "none", "strict", "normal", 0 };
+static const char * const x264_overscan_names[] = { "undef", "show", "crop", 0 };
+static const char * const x264_vidformat_names[] = { "component", "pal", "ntsc", "secam", "mac", "undef", 0 };
+static const char * const x264_fullrange_names[] = { "off", "on", 0 };
+static const char * const x264_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", "smpte428",
+ "smpte431", "smpte432", 0 };
+static const char * const x264_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316",
+ "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", "smpte2084", "smpte428", 0 };
+static const char * const x264_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", "bt2020nc", "bt2020c",
+ "smpte2085", 0 };
+static const char * const x264_nal_hrd_names[] = { "none", "vbr", "cbr", 0 };
+
+/* Colorspace type */
+#define X264_CSP_MASK 0x00ff /* */
+#define X264_CSP_NONE 0x0000 /* Invalid mode */
+#define X264_CSP_I420 0x0001 /* yuv 4:2:0 planar */
+#define X264_CSP_YV12 0x0002 /* yvu 4:2:0 planar */
+#define X264_CSP_NV12 0x0003 /* yuv 4:2:0, with one y plane and one packed u+v */
+#define X264_CSP_NV21 0x0004 /* yuv 4:2:0, with one y plane and one packed v+u */
+#define X264_CSP_I422 0x0005 /* yuv 4:2:2 planar */
+#define X264_CSP_YV16 0x0006 /* yvu 4:2:2 planar */
+#define X264_CSP_NV16 0x0007 /* yuv 4:2:2, with one y plane and one packed u+v */
+#define X264_CSP_V210 0x0008 /* 10-bit yuv 4:2:2 packed in 32 */
+#define X264_CSP_I444 0x0009 /* yuv 4:4:4 planar */
+#define X264_CSP_YV24 0x000a /* yvu 4:4:4 planar */
+#define X264_CSP_BGR 0x000b /* packed bgr 24bits */
+#define X264_CSP_BGRA 0x000c /* packed bgr 32bits */
+#define X264_CSP_RGB 0x000d /* packed rgb 24bits */
+#define X264_CSP_MAX 0x000e /* end of list */
+#define X264_CSP_VFLIP 0x1000 /* the csp is vertically flipped */
+#define X264_CSP_HIGH_DEPTH 0x2000 /* the csp has a depth of 16 bits per pixel component */
+
+/* Slice type */
+#define X264_TYPE_AUTO 0x0000 /* Let include.x264 choose the right type */
+#define X264_TYPE_IDR 0x0001
+#define X264_TYPE_I 0x0002
+#define X264_TYPE_P 0x0003
+#define X264_TYPE_BREF 0x0004 /* Non-disposable B-frame */
+#define X264_TYPE_B 0x0005
+#define X264_TYPE_KEYFRAME 0x0006 /* IDR or I depending on b_open_gop option */
+#define IS_X264_TYPE_I(x) ((x)==X264_TYPE_I || (x)==X264_TYPE_IDR || (x)==X264_TYPE_KEYFRAME)
+#define IS_X264_TYPE_B(x) ((x)==X264_TYPE_B || (x)==X264_TYPE_BREF)
+
+/* Log level */
+#define X264_LOG_NONE (-1)
+#define X264_LOG_ERROR 0
+#define X264_LOG_WARNING 1
+#define X264_LOG_INFO 2
+#define X264_LOG_DEBUG 3
+
+/* Threading */
+#define X264_THREADS_AUTO 0 /* Automatically select optimal number of threads */
+#define X264_SYNC_LOOKAHEAD_AUTO (-1) /* Automatically select optimal lookahead thread buffer size */
+
+/* HRD */
+#define X264_NAL_HRD_NONE 0
+#define X264_NAL_HRD_VBR 1
+#define X264_NAL_HRD_CBR 2
+
+/* Zones: override ratecontrol or other options for specific sections of the video.
+ * See x264_encoder_reconfig() for which options can be changed.
+ * If zones overlap, whichever comes later in the list takes precedence. */
+typedef struct x264_zone_t
+{
+ int i_start, i_end; /* range of frame numbers */
+ int b_force_qp; /* whether to use qp vs bitrate factor */
+ int i_qp;
+ float f_bitrate_factor;
+ struct x264_param_t *param;
+} x264_zone_t;
+
+typedef struct x264_param_t
+{
+ /* CPU flags */
+ unsigned int cpu;
+ int i_threads; /* encode multiple frames in parallel */
+ int i_lookahead_threads; /* multiple threads for lookahead analysis */
+ int b_sliced_threads; /* Whether to use slice-based threading. */
+ int b_deterministic; /* whether to allow non-deterministic optimizations when threaded */
+ int b_cpu_independent; /* force canonical behavior rather than cpu-dependent optimal algorithms */
+ int i_sync_lookahead; /* threaded lookahead buffer */
+
+ /* Video Properties */
+ int i_width;
+ int i_height;
+ int i_csp; /* CSP of encoded bitstream */
+ int i_level_idc;
+ int i_frame_total; /* number of frames to encode if known, else 0 */
+
+ /* NAL HRD
+ * Uses Buffering and Picture Timing SEIs to signal HRD
+ * The HRD in H.264 was not designed with VFR in mind.
+ * It is therefore not recommendeded to use NAL HRD with VFR.
+ * Furthermore, reconfiguring the VBV (via x264_encoder_reconfig)
+ * will currently generate invalid HRD. */
+ int i_nal_hrd;
+
+ struct
+ {
+ /* they will be reduced to be 0 < x <= 65535 and prime */
+ int i_sar_height;
+ int i_sar_width;
+
+ int i_overscan; /* 0=undef, 1=no overscan, 2=overscan */
+
+ /* see h264 annex E for the values of the following */
+ int i_vidformat;
+ int b_fullrange;
+ int i_colorprim;
+ int i_transfer;
+ int i_colmatrix;
+ int i_chroma_loc; /* both top & bottom */
+ } vui;
+
+ /* Bitstream parameters */
+ int i_frame_reference; /* Maximum number of reference frames */
+ int i_dpb_size; /* Force a DPB size larger than that implied by B-frames and reference frames.
+ * Useful in combination with interactive error resilience. */
+ int i_keyint_max; /* Force an IDR keyframe at this interval */
+ int i_keyint_min; /* Scenecuts closer together than this are coded as I, not IDR. */
+ int i_scenecut_threshold; /* how aggressively to insert extra I frames */
+ int b_intra_refresh; /* Whether or not to use periodic intra refresh instead of IDR frames. */
+
+ int i_bframe; /* how many b-frame between 2 references pictures */
+ int i_bframe_adaptive;
+ int i_bframe_bias;
+ int i_bframe_pyramid; /* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
+ int b_open_gop;
+ int b_bluray_compat;
+ int i_avcintra_class;
+
+ int b_deblocking_filter;
+ int i_deblocking_filter_alphac0; /* [-6, 6] -6 light filter, 6 strong */
+ int i_deblocking_filter_beta; /* [-6, 6] idem */
+
+ int b_cabac;
+ int i_cabac_init_idc;
+
+ int b_interlaced;
+ int b_constrained_intra;
+
+ int i_cqm_preset;
+ char *psz_cqm_file; /* filename (in UTF-8) of CQM file, JM format */
+ uint8_t cqm_4iy[16]; /* used only if i_cqm_preset == X264_CQM_CUSTOM */
+ uint8_t cqm_4py[16];
+ uint8_t cqm_4ic[16];
+ uint8_t cqm_4pc[16];
+ uint8_t cqm_8iy[64];
+ uint8_t cqm_8py[64];
+ uint8_t cqm_8ic[64];
+ uint8_t cqm_8pc[64];
+
+ /* Log */
+ void (*pf_log)( void *, int i_level, const char *psz, va_list );
+ void *p_log_private;
+ int i_log_level;
+ int b_full_recon; /* fully reconstruct frames, even when not necessary for encoding. Implied by psz_dump_yuv */
+ char *psz_dump_yuv; /* filename (in UTF-8) for reconstructed frames */
+
+ /* Encoder analyser parameters */
+ struct
+ {
+ unsigned int intra; /* intra partitions */
+ unsigned int inter; /* inter partitions */
+
+ int b_transform_8x8;
+ int i_weighted_pred; /* weighting for P-frames */
+ int b_weighted_bipred; /* implicit weighting for B-frames */
+ int i_direct_mv_pred; /* spatial vs temporal mv prediction */
+ int i_chroma_qp_offset;
+
+ int i_me_method; /* motion estimation algorithm to use (X264_ME_*) */
+ int i_me_range; /* integer pixel motion estimation search range (from predicted mv) */
+ int i_mv_range; /* maximum length of a mv (in pixels). -1 = auto, based on level */
+ int i_mv_range_thread; /* minimum space between threads. -1 = auto, based on number of threads. */
+ int i_subpel_refine; /* subpixel motion estimation quality */
+ int b_chroma_me; /* chroma ME for subpel and mode decision in P-frames */
+ int b_mixed_references; /* allow each mb partition to have its own reference number */
+ int i_trellis; /* trellis RD quantization */
+ int b_fast_pskip; /* early SKIP detection on P-frames */
+ int b_dct_decimate; /* transform coefficient thresholding on P-frames */
+ int i_noise_reduction; /* adaptive pseudo-deadzone */
+ float f_psy_rd; /* Psy RD strength */
+ float f_psy_trellis; /* Psy trellis strength */
+ int b_psy; /* Toggle all psy optimizations */
+
+ int b_mb_info; /* Use input mb_info data in x264_picture_t */
+ int b_mb_info_update; /* Update the values in mb_info according to the results of encoding. */
+
+ /* the deadzone size that will be used in luma quantization */
+ int i_luma_deadzone[2]; /* {inter, intra} */
+
+ int b_psnr; /* compute and print PSNR stats */
+ int b_ssim; /* compute and print SSIM stats */
+ } analyse;
+
+ /* Rate control parameters */
+ struct
+ {
+ int i_rc_method; /* X264_RC_* */
+
+ int i_qp_constant; /* 0 to (51 + 6*(x264_bit_depth-8)). 0=lossless */
+ int i_qp_min; /* min allowed QP value */
+ int i_qp_max; /* max allowed QP value */
+ int i_qp_step; /* max QP step between frames */
+
+ int i_bitrate;
+ float f_rf_constant; /* 1pass VBR, nominal QP */
+ float f_rf_constant_max; /* In CRF mode, maximum CRF as caused by VBV */
+ float f_rate_tolerance;
+ int i_vbv_max_bitrate;
+ int i_vbv_buffer_size;
+ float f_vbv_buffer_init; /* <=1: fraction of buffer_size. >1: kbit */
+ float f_ip_factor;
+ float f_pb_factor;
+
+ /* VBV filler: force CBR VBV and use filler bytes to ensure hard-CBR.
+ * Implied by NAL-HRD CBR. */
+ int b_filler;
+
+ int i_aq_mode; /* psy adaptive QP. (X264_AQ_*) */
+ float f_aq_strength;
+ int b_mb_tree; /* Macroblock-tree ratecontrol. */
+ int i_lookahead;
+
+ /* 2pass */
+ int b_stat_write; /* Enable stat writing in psz_stat_out */
+ char *psz_stat_out; /* output filename (in UTF-8) of the 2pass stats file */
+ int b_stat_read; /* Read stat from psz_stat_in and use it */
+ char *psz_stat_in; /* input filename (in UTF-8) of the 2pass stats file */
+
+ /* 2pass params (same as ffmpeg ones) */
+ float f_qcompress; /* 0.0 => cbr, 1.0 => constant qp */
+ float f_qblur; /* temporally blur quants */
+ float f_complexity_blur; /* temporally blur complexity */
+ x264_zone_t *zones; /* ratecontrol overrides */
+ int i_zones; /* number of zone_t's */
+ char *psz_zones; /* alternate method of specifying zones */
+ } rc;
+
+ /* Cropping Rectangle parameters: added to those implicitly defined by
+ non-mod16 video resolutions. */
+ struct
+ {
+ unsigned int i_left;
+ unsigned int i_top;
+ unsigned int i_right;
+ unsigned int i_bottom;
+ } crop_rect;
+
+ /* frame packing arrangement flag */
+ int i_frame_packing;
+
+ /* Muxing parameters */
+ int b_aud; /* generate access unit delimiters */
+ int b_repeat_headers; /* put SPS/PPS before each keyframe */
+ int b_annexb; /* if set, place start codes (4 bytes) before NAL units,
+ * otherwise place size (4 bytes) before NAL units. */
+ int i_sps_id; /* SPS and PPS id number */
+ int b_vfr_input; /* VFR input. If 1, use timebase and timestamps for ratecontrol purposes.
+ * If 0, use fps only. */
+ int b_pulldown; /* use explicity set timebase for CFR */
+ uint32_t i_fps_num;
+ uint32_t i_fps_den;
+ uint32_t i_timebase_num; /* Timebase numerator */
+ uint32_t i_timebase_den; /* Timebase denominator */
+
+ int b_tff;
+
+ /* Pulldown:
+ * The correct pic_struct must be passed with each input frame.
+ * The input timebase should be the timebase corresponding to the output framerate. This should be constant.
+ * e.g. for 3:2 pulldown timebase should be 1001/30000
+ * The PTS passed with each frame must be the PTS of the frame after pulldown is applied.
+ * Frame doubling and tripling require b_vfr_input set to zero (see H.264 Table D-1)
+ *
+ * Pulldown changes are not clearly defined in H.264. Therefore, it is the calling app's responsibility to manage this.
+ */
+
+ int b_pic_struct;
+
+ /* Fake Interlaced.
+ *
+ * Used only when b_interlaced=0. Setting this flag makes it possible to flag the stream as PAFF interlaced yet
+ * encode all frames progessively. It is useful for encoding 25p and 30p Blu-Ray streams.
+ */
+
+ int b_fake_interlaced;
+
+ /* Don't optimize header parameters based on video content, e.g. ensure that splitting an input video, compressing
+ * each part, and stitching them back together will result in identical SPS/PPS. This is necessary for stitching
+ * with container formats that don't allow multiple SPS/PPS. */
+ int b_stitchable;
+
+ int b_opencl; /* use OpenCL when available */
+ int i_opencl_device; /* specify count of GPU devices to skip, for CLI users */
+ void *opencl_device_id; /* pass explicit cl_device_id as void*, for API users */
+ char *psz_clbin_file; /* filename (in UTF-8) of the compiled OpenCL kernel cache file */
+
+ /* Slicing parameters */
+ int i_slice_max_size; /* Max size per slice in bytes; includes estimated NAL overhead. */
+ int i_slice_max_mbs; /* Max number of MBs per slice; overrides i_slice_count. */
+ int i_slice_min_mbs; /* Min number of MBs per slice */
+ int i_slice_count; /* Number of slices per frame: forces rectangular slices. */
+ int i_slice_count_max; /* Absolute cap on slices per frame; stops applying slice-max-size
+ * and slice-max-mbs if this is reached. */
+
+ /* Optional callback for freeing this x264_param_t when it is done being used.
+ * Only used when the x264_param_t sits in memory for an indefinite period of time,
+ * i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
+ * Not used when x264_encoder_reconfig is called directly. */
+ void (*param_free)( void* );
+
+ /* Optional low-level callback for low-latency encoding. Called for each output NAL unit
+ * immediately after the NAL unit is finished encoding. This allows the calling application
+ * to begin processing video data (e.g. by sending packets over a network) before the frame
+ * is done encoding.
+ *
+ * This callback MUST do the following in order to work correctly:
+ * 1) Have available an output buffer of at least size nal->i_payload*3/2 + 5 + 64.
+ * 2) Call x264_nal_encode( h, dst, nal ), where dst is the output buffer.
+ * After these steps, the content of nal is valid and can be used in the same way as if
+ * the NAL unit were output by x264_encoder_encode.
+ *
+ * This does not need to be synchronous with the encoding process: the data pointed to
+ * by nal (both before and after x264_nal_encode) will remain valid until the next
+ * x264_encoder_encode call. The callback must be re-entrant.
+ *
+ * This callback does not work with frame-based threads; threads must be disabled
+ * or sliced-threads enabled. This callback also does not work as one would expect
+ * with HRD -- since the buffering period SEI cannot be calculated until the frame
+ * is finished encoding, it will not be sent via this callback.
+ *
+ * Note also that the NALs are not necessarily returned in order when sliced threads is
+ * enabled. Accordingly, the variable i_first_mb and i_last_mb are available in
+ * x264_nal_t to help the calling application reorder the slices if necessary.
+ *
+ * When this callback is enabled, x264_encoder_encode does not return valid NALs;
+ * the calling application is expected to acquire all output NALs through the callback.
+ *
+ * It is generally sensible to combine this callback with a use of slice-max-mbs or
+ * slice-max-size.
+ *
+ * The opaque pointer is the opaque pointer from the input frame associated with this
+ * NAL unit. This helps distinguish between nalu_process calls from different sources,
+ * e.g. if doing multiple encodes in one process.
+ */
+ void (*nalu_process) ( x264_t *h, x264_nal_t *nal, void *opaque );
+} x264_param_t;
+
+void x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal );
+
+/****************************************************************************
+ * H.264 level restriction information
+ ****************************************************************************/
+
+typedef struct x264_level_t
+{
+ int level_idc;
+ int mbps; /* max macroblock processing rate (macroblocks/sec) */
+ int frame_size; /* max frame size (macroblocks) */
+ int dpb; /* max decoded picture buffer (mbs) */
+ int bitrate; /* max bitrate (kbit/sec) */
+ int cpb; /* max vbv buffer (kbit) */
+ int mv_range; /* max vertical mv component range (pixels) */
+ int mvs_per_2mb; /* max mvs per 2 consecutive mbs. */
+ int slice_rate; /* ?? */
+ int mincr; /* min compression ratio */
+ int bipred8x8; /* limit bipred to >=8x8 */
+ int direct8x8; /* limit b_direct to >=8x8 */
+ int frame_only; /* forbid interlacing */
+} x264_level_t;
+
+/* all of the levels defined in the standard, terminated by .level_idc=0 */
+X264_API extern const x264_level_t x264_levels[];
+
+/****************************************************************************
+ * Basic parameter handling functions
+ ****************************************************************************/
+
+/* x264_param_default:
+ * fill x264_param_t with default values and do CPU detection */
+void x264_param_default( x264_param_t * );
+
+/* x264_param_parse:
+ * set one parameter by name.
+ * returns 0 on success, or returns one of the following errors.
+ * note: BAD_VALUE occurs only if it can't even parse the value,
+ * numerical range is not checked until x264_encoder_open() or
+ * x264_encoder_reconfig().
+ * value=NULL means "true" for boolean options, but is a BAD_VALUE for non-booleans. */
+#define X264_PARAM_BAD_NAME (-1)
+#define X264_PARAM_BAD_VALUE (-2)
+int x264_param_parse( x264_param_t *, const char *name, const char *value );
+
+/****************************************************************************
+ * Advanced parameter handling functions
+ ****************************************************************************/
+
+/* These functions expose the full power of include.x264's preset-tune-profile system for
+ * easy adjustment of large numbers of internal parameters.
+ *
+ * In order to replicate x264CLI's option handling, these functions MUST be called
+ * in the following order:
+ * 1) x264_param_default_preset
+ * 2) Custom user options (via param_parse or directly assigned variables)
+ * 3) x264_param_apply_fastfirstpass
+ * 4) x264_param_apply_profile
+ *
+ * Additionally, x264CLI does not apply step 3 if the preset chosen is "placebo"
+ * or --slow-firstpass is set. */
+
+/* x264_param_default_preset:
+ * The same as x264_param_default, but also use the passed preset and tune
+ * to modify the default settings.
+ * (either can be NULL, which implies no preset or no tune, respectively)
+ *
+ * Currently available presets are, ordered from fastest to slowest: */
+static const char * const x264_preset_names[] = { "ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo", 0 };
+
+/* The presets can also be indexed numerically, as in:
+ * x264_param_default_preset( ¶m, "3", ... )
+ * with ultrafast mapping to "0" and placebo mapping to "9". This mapping may
+ * of course change if new presets are added in between, but will always be
+ * ordered from fastest to slowest.
+ *
+ * Warning: the speed of these presets scales dramatically. Ultrafast is a full
+ * 100 times faster than placebo!
+ *
+ * Currently available tunings are: */
+static const char * const x264_tune_names[] = { "film", "animation", "grain", "stillimage", "psnr", "ssim", "fastdecode", "zerolatency", 0 };
+
+/* Multiple tunings can be used if separated by a delimiter in ",./-+",
+ * however multiple psy tunings cannot be used.
+ * film, animation, grain, stillimage, psnr, and ssim are psy tunings.
+ *
+ * returns 0 on success, negative on failure (e.g. invalid preset/tune name). */
+int x264_param_default_preset( x264_param_t *, const char *preset, const char *tune );
+
+/* x264_param_apply_fastfirstpass:
+ * If first-pass mode is set (rc.b_stat_read == 0, rc.b_stat_write == 1),
+ * modify the encoder settings to disable options generally not useful on
+ * the first pass. */
+void x264_param_apply_fastfirstpass( x264_param_t * );
+
+/* x264_param_apply_profile:
+ * Applies the restrictions of the given profile.
+ * Currently available profiles are, from most to least restrictive: */
+static const char * const x264_profile_names[] = { "baseline", "main", "high", "high10", "high422", "high444", 0 };
+
+/* (can be NULL, in which case the function will do nothing)
+ *
+ * Does NOT guarantee that the given profile will be used: if the restrictions
+ * of "High" are applied to settings that are already Baseline-compatible, the
+ * stream will remain baseline. In short, it does not increase settings, only
+ * decrease them.
+ *
+ * returns 0 on success, negative on failure (e.g. invalid profile name). */
+int x264_param_apply_profile( x264_param_t *, const char *profile );
+
+/****************************************************************************
+ * Picture structures and functions
+ ****************************************************************************/
+
+/* x264_bit_depth:
+ * Specifies the number of bits per pixel that include.x264 uses. This is also the
+ * bit depth that include.x264 encodes in. If this value is > 8, include.x264 will read
+ * two bytes of input data for each pixel sample, and expect the upper
+ * (16-x264_bit_depth) bits to be zero.
+ * Note: The flag X264_CSP_HIGH_DEPTH must be used to specify the
+ * colorspace depth as well. */
+X264_API extern const int x264_bit_depth;
+
+/* x264_chroma_format:
+ * Specifies the chroma formats that include.x264 supports encoding. When this
+ * value is non-zero, then it represents a X264_CSP_* that is the only
+ * chroma format that include.x264 supports encoding. If the value is 0 then
+ * there are no restrictions. */
+X264_API extern const int x264_chroma_format;
+
+enum pic_struct_e
+{
+ PIC_STRUCT_AUTO = 0, // automatically decide (default)
+ PIC_STRUCT_PROGRESSIVE = 1, // progressive frame
+ // "TOP" and "BOTTOM" are not supported in include.x264 (PAFF only)
+ PIC_STRUCT_TOP_BOTTOM = 4, // top field followed by bottom
+ PIC_STRUCT_BOTTOM_TOP = 5, // bottom field followed by top
+ PIC_STRUCT_TOP_BOTTOM_TOP = 6, // top field, bottom field, top field repeated
+ PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated
+ PIC_STRUCT_DOUBLE = 8, // double frame
+ PIC_STRUCT_TRIPLE = 9, // triple frame
+};
+
+typedef struct x264_hrd_t
+{
+ double cpb_initial_arrival_time;
+ double cpb_final_arrival_time;
+ double cpb_removal_time;
+
+ double dpb_output_time;
+} x264_hrd_t;
+
+/* Arbitrary user SEI:
+ * Payload size is in bytes and the payload pointer must be valid.
+ * Payload types and syntax can be found in Annex D of the H.264 Specification.
+ * SEI payload alignment bits as described in Annex D must be included at the
+ * end of the payload if needed.
+ * The payload should not be NAL-encapsulated.
+ * Payloads are written first in order of input, apart from in the case when HRD
+ * is enabled where payloads are written after the Buffering Period SEI. */
+
+typedef struct x264_sei_payload_t
+{
+ int payload_size;
+ int payload_type;
+ uint8_t *payload;
+} x264_sei_payload_t;
+
+typedef struct x264_sei_t
+{
+ int num_payloads;
+ x264_sei_payload_t *payloads;
+ /* In: optional callback to free each payload AND x264_sei_payload_t when used. */
+ void (*sei_free)( void* );
+} x264_sei_t;
+
+typedef struct x264_image_t
+{
+ int i_csp; /* Colorspace */
+ int i_plane; /* Number of image planes */
+ int i_stride[4]; /* Strides for each plane */
+ uint8_t *plane[4]; /* Pointers to each plane */
+} x264_image_t;
+
+typedef struct x264_image_properties_t
+{
+ /* All arrays of data here are ordered as follows:
+ * each array contains one offset per macroblock, in raster scan order. In interlaced
+ * mode, top-field MBs and bottom-field MBs are interleaved at the row level.
+ * Macroblocks are 16x16 blocks of pixels (with respect to the luma plane). For the
+ * purposes of calculating the number of macroblocks, width and height are rounded up to
+ * the nearest 16. If in interlaced mode, height is rounded up to the nearest 32 instead. */
+
+ /* In: an array of quantizer offsets to be applied to this image during encoding.
+ * These are added on top of the decisions made by include.x264.
+ * Offsets can be fractional; they are added before QPs are rounded to integer.
+ * Adaptive quantization must be enabled to use this feature. Behavior if quant
+ * offsets differ between encoding passes is undefined. */
+ float *quant_offsets;
+ /* In: optional callback to free quant_offsets when used.
+ * Useful if one wants to use a different quant_offset array for each frame. */
+ void (*quant_offsets_free)( void* );
+
+ /* In: optional array of flags for each macroblock.
+ * Allows specifying additional information for the encoder such as which macroblocks
+ * remain unchanged. Usable flags are listed below.
+ * x264_param_t.analyse.b_mb_info must be set to use this, since include.x264 needs to track
+ * extra data internally to make full use of this information.
+ *
+ * Out: if b_mb_info_update is set, include.x264 will update this array as a result of encoding.
+ *
+ * For "MBINFO_CONSTANT", it will remove this flag on any macroblock whose decoded
+ * pixels have changed. This can be useful for e.g. noting which areas of the
+ * frame need to actually be blitted. Note: this intentionally ignores the effects
+ * of deblocking for the current frame, which should be fine unless one needs exact
+ * pixel-perfect accuracy.
+ *
+ * Results for MBINFO_CONSTANT are currently only set for P-frames, and are not
+ * guaranteed to enumerate all blocks which haven't changed. (There may be false
+ * negatives, but no false positives.)
+ */
+ uint8_t *mb_info;
+ /* In: optional callback to free mb_info when used. */
+ void (*mb_info_free)( void* );
+
+ /* The macroblock is constant and remains unchanged from the previous frame. */
+ #define X264_MBINFO_CONSTANT (1<<0)
+ /* More flags may be added in the future. */
+
+ /* Out: SSIM of the the frame luma (if x264_param_t.b_ssim is set) */
+ double f_ssim;
+ /* Out: Average PSNR of the frame (if x264_param_t.b_psnr is set) */
+ double f_psnr_avg;
+ /* Out: PSNR of Y, U, and V (if x264_param_t.b_psnr is set) */
+ double f_psnr[3];
+
+ /* Out: Average effective CRF of the encoded frame */
+ double f_crf_avg;
+} x264_image_properties_t;
+
+typedef struct x264_picture_t
+{
+ /* In: force picture type (if not auto)
+ * If include.x264 encoding parameters are violated in the forcing of picture types,
+ * include.x264 will correct the input picture type and log a warning.
+ * Out: type of the picture encoded */
+ int i_type;
+ /* In: force quantizer for != X264_QP_AUTO */
+ int i_qpplus1;
+ /* In: pic_struct, for pulldown/doubling/etc...used only if b_pic_struct=1.
+ * use pic_struct_e for pic_struct inputs
+ * Out: pic_struct element associated with frame */
+ int i_pic_struct;
+ /* Out: whether this frame is a keyframe. Important when using modes that result in
+ * SEI recovery points being used instead of IDR frames. */
+ int b_keyframe;
+ /* In: user pts, Out: pts of encoded picture (user)*/
+ int64_t i_pts;
+ /* Out: frame dts. When the pts of the first frame is close to zero,
+ * initial frames may have a negative dts which must be dealt with by any muxer */
+ int64_t i_dts;
+ /* In: custom encoding parameters to be set from this frame forwards
+ (in coded order, not display order). If NULL, continue using
+ parameters from the previous frame. Some parameters, such as
+ aspect ratio, can only be changed per-GOP due to the limitations
+ of H.264 itself; in this case, the caller must force an IDR frame
+ if it needs the changed parameter to apply immediately. */
+ x264_param_t *param;
+ /* In: raw image data */
+ /* Out: reconstructed image data. include.x264 may skip part of the reconstruction process,
+ e.g. deblocking, in frames where it isn't necessary. To force complete
+ reconstruction, at a small speed cost, set b_full_recon. */
+ x264_image_t img;
+ /* In: optional information to modify encoder decisions for this frame
+ * Out: information about the encoded frame */
+ x264_image_properties_t prop;
+ /* Out: HRD timing information. Output only when i_nal_hrd is set. */
+ x264_hrd_t hrd_timing;
+ /* In: arbitrary user SEI (e.g subtitles, AFDs) */
+ x264_sei_t extra_sei;
+ /* private user data. copied from input to output frames. */
+ void *opaque;
+} x264_picture_t;
+
+/* x264_picture_init:
+ * initialize an x264_picture_t. Needs to be done if the calling application
+ * allocates its own x264_picture_t as opposed to using x264_picture_alloc. */
+void x264_picture_init( x264_picture_t *pic );
+
+/* x264_picture_alloc:
+ * alloc data for a picture. You must call x264_picture_clean on it.
+ * returns 0 on success, or -1 on malloc failure or invalid colorspace. */
+int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height );
+
+/* x264_picture_clean:
+ * free associated resource for a x264_picture_t allocated with
+ * x264_picture_alloc ONLY */
+void x264_picture_clean( x264_picture_t *pic );
+
+/****************************************************************************
+ * Encoder functions
+ ****************************************************************************/
+
+/* Force a link error in the case of linking against an incompatible API version.
+ * Glue #defines exist to force correct macro expansion; the final output of the macro
+ * is x264_encoder_open_##X264_BUILD (for purposes of dlopen). */
+#define x264_encoder_glue1(x,y) x##y
+#define x264_encoder_glue2(x,y) x264_encoder_glue1(x,y)
+#define x264_encoder_open x264_encoder_glue2(x264_encoder_open_,X264_BUILD)
+
+/* x264_encoder_open:
+ * create a new encoder handler, all parameters from x264_param_t are copied */
+x264_t *x264_encoder_open( x264_param_t * );
+
+/* x264_encoder_reconfig:
+ * various parameters from x264_param_t are copied.
+ * this takes effect immediately, on whichever frame is encoded next;
+ * due to delay, this may not be the next frame passed to encoder_encode.
+ * if the change should apply to some particular frame, use x264_picture_t->param instead.
+ * returns 0 on success, negative on parameter validation error.
+ * not all parameters can be changed; see the actual function for a detailed breakdown.
+ *
+ * since not all parameters can be changed, moving from preset to preset may not always
+ * fully copy all relevant parameters, but should still work usably in practice. however,
+ * more so than for other presets, many of the speed shortcuts used in ultrafast cannot be
+ * switched out of; using reconfig to switch between ultrafast and other presets is not
+ * recommended without a more fine-grained breakdown of parameters to take this into account. */
+int x264_encoder_reconfig( x264_t *, x264_param_t * );
+/* x264_encoder_parameters:
+ * copies the current internal set of parameters to the pointer provided
+ * by the caller. useful when the calling application needs to know
+ * how x264_encoder_open has changed the parameters, or the current state
+ * of the encoder after multiple x264_encoder_reconfig calls.
+ * note that the data accessible through pointers in the returned param struct
+ * (e.g. filenames) should not be modified by the calling application. */
+void x264_encoder_parameters( x264_t *, x264_param_t * );
+/* x264_encoder_headers:
+ * return the SPS and PPS that will be used for the whole stream.
+ * *pi_nal is the number of NAL units outputted in pp_nal.
+ * returns the number of bytes in the returned NALs.
+ * returns negative on error.
+ * the payloads of all output NALs are guaranteed to be sequential in memory. */
+int x264_encoder_headers( x264_t *, x264_nal_t **pp_nal, int *pi_nal );
+/* x264_encoder_encode:
+ * encode one picture.
+ * *pi_nal is the number of NAL units outputted in pp_nal.
+ * returns the number of bytes in the returned NALs.
+ * returns negative on error and zero if no NAL units returned.
+ * the payloads of all output NALs are guaranteed to be sequential in memory. */
+int x264_encoder_encode( x264_t *, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out );
+/* x264_encoder_close:
+ * close an encoder handler */
+void x264_encoder_close ( x264_t * );
+/* x264_encoder_delayed_frames:
+ * return the number of currently delayed (buffered) frames
+ * this should be used at the end of the stream, to know when you have all the encoded frames. */
+int x264_encoder_delayed_frames( x264_t * );
+/* x264_encoder_maximum_delayed_frames( x264_t *h ):
+ * return the maximum number of delayed (buffered) frames that can occur with the current
+ * parameters. */
+int x264_encoder_maximum_delayed_frames( x264_t *h );
+/* x264_encoder_intra_refresh:
+ * If an intra refresh is not in progress, begin one with the next P-frame.
+ * If an intra refresh is in progress, begin one as soon as the current one finishes.
+ * Requires that b_intra_refresh be set.
+ *
+ * Useful for interactive streaming where the client can tell the server that packet loss has
+ * occurred. In this case, keyint can be set to an extremely high value so that intra refreshes
+ * only occur when calling x264_encoder_intra_refresh.
+ *
+ * In multi-pass encoding, if x264_encoder_intra_refresh is called differently in each pass,
+ * behavior is undefined.
+ *
+ * Should not be called during an x264_encoder_encode. */
+void x264_encoder_intra_refresh( x264_t * );
+/* x264_encoder_invalidate_reference:
+ * An interactive error resilience tool, designed for use in a low-latency one-encoder-few-clients
+ * system. When the client has packet loss or otherwise incorrectly decodes a frame, the encoder
+ * can be told with this command to "forget" the frame and all frames that depend on it, referencing
+ * only frames that occurred before the loss. This will force a keyframe if no frames are left to
+ * reference after the aforementioned "forgetting".
+ *
+ * It is strongly recommended to use a large i_dpb_size in this case, which allows the encoder to
+ * keep around extra, older frames to fall back on in case more recent frames are all invalidated.
+ * Unlike increasing i_frame_reference, this does not increase the number of frames used for motion
+ * estimation and thus has no speed impact. It is also recommended to set a very large keyframe
+ * interval, so that keyframes are not used except as necessary for error recovery.
+ *
+ * x264_encoder_invalidate_reference is not currently compatible with the use of B-frames or intra
+ * refresh.
+ *
+ * In multi-pass encoding, if x264_encoder_invalidate_reference is called differently in each pass,
+ * behavior is undefined.
+ *
+ * Should not be called during an x264_encoder_encode, but multiple calls can be made simultaneously.
+ *
+ * Returns 0 on success, negative on failure. */
+int x264_encoder_invalidate_reference( x264_t *, int64_t pts );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Live/src/main/cpp/include/x264/x264_config.h b/Live/src/main/cpp/include/x264/x264_config.h
new file mode 100644
index 0000000..1e0d377
--- /dev/null
+++ b/Live/src/main/cpp/include/x264/x264_config.h
@@ -0,0 +1,6 @@
+#define X264_BIT_DEPTH 8
+#define X264_GPL 1
+#define X264_INTERLACED 1
+#define X264_CHROMA_FORMAT 0
+#define X264_VERSION ""
+#define X264_POINTVER "0.148.x"
diff --git a/Live/src/main/cpp/live.c b/Live/src/main/cpp/live.c
new file mode 100644
index 0000000..760e596
--- /dev/null
+++ b/Live/src/main/cpp/live.c
@@ -0,0 +1,481 @@
+#include
+#include
+#include "include/x264/x264.h"
+#include
+#include "include/rtmp/rtmp.h"
+#include "include/faac/faac.h"
+#include
+#include "queue.h"
+#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;
+int y_len, uv_len;
+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_PACKAT = 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_PACKAT);
+ goto end;
+ }
+ RTMPPacket_Free(packet);
+ LOGI("RTMP_SendPacket success...");
+ }
+ 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) {
+ y_len = width * height;
+ uv_len = y_len/4;
+
+ 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);
+}
+
+//推送视频流
+JNIEXPORT void JNICALL
+Java_com_frank_live_LiveUtil_pushVideo(JNIEnv *env, jobject instance, jbyteArray data_) {
+ //NV21转成YUV420P
+ jbyte *nv21_buffer = (*env)->GetByteArrayElements(env, data_, NULL);
+ //Y相同,直接拷贝
+ memcpy(picture_in.img.plane[0], nv21_buffer, (size_t) y_len);
+ jbyte *u_buffer = (jbyte *) picture_in.img.plane[1];
+ jbyte *v_buffer = (jbyte *) picture_in.img.plane[2];
+ int i;
+ //U和V交换
+ for(i=0; iReleaseByteArrayElements(env, data_, nv21_buffer, 0);
+}
+
+//添加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; iReleaseByteArrayElements(env, data_, data, 0);
+ if(pcm_buf != NULL){
+ free(pcm_buf);
+ }
+}
+
+//停止推流
+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
new file mode 100644
index 0000000..6942c44
--- /dev/null
+++ b/Live/src/main/cpp/queue.c
@@ -0,0 +1,218 @@
+#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
new file mode 100644
index 0000000..1dbdd29
--- /dev/null
+++ b/Live/src/main/cpp/queue.h
@@ -0,0 +1,29 @@
+#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/LiveActivity.java b/Live/src/main/java/com/frank/live/LiveActivity.java
new file mode 100644
index 0000000..53be202
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/LiveActivity.java
@@ -0,0 +1,129 @@
+package com.frank.live;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.hardware.Camera;
+import android.media.AudioFormat;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+import com.frank.live.Push.LivePusher;
+import com.frank.live.listener.LiveStateChangeListener;
+import com.frank.live.param.AudioParam;
+import com.frank.live.param.VideoParam;
+
+/**
+ * h264与rtmp实时推流直播
+ * Created by frank on 2018/1/28.
+ */
+
+public class LiveActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, LiveStateChangeListener {
+
+ private final static String TAG = LiveActivity.class.getSimpleName();
+ private final static int CODE_CAMERA_RECORD = 0x0001;
+ private final static String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
+ private final static String LIVE_URL = "rtmp://192.168.8.115/live/stream";
+ private final static int MSG_ERROR = 100;
+ private SurfaceHolder surfaceHolder;
+ private LivePusher livePusher;
+ @SuppressLint("HandlerLeak")
+ private Handler mHandler = new Handler(){
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if(msg.what == MSG_ERROR){
+ String errMsg = (String)msg.obj;
+ if(!TextUtils.isEmpty(errMsg)){
+ Toast.makeText(LiveActivity.this, errMsg, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_live);
+
+ initView();
+ requirePermission();
+ initPusher();
+ }
+
+ private void initView(){
+ findViewById(R.id.btn_swap).setOnClickListener(this);
+ ((ToggleButton)findViewById(R.id.btn_live)).setOnCheckedChangeListener(this);
+ SurfaceView surface_camera = (SurfaceView) findViewById(R.id.surface_camera);
+ surfaceHolder = surface_camera.getHolder();
+ }
+
+ private void initPusher() {
+ int width = 640;//分辨率设置很重要
+ int height = 480;
+ int videoBitRate = 400;//kb/s jason-->480kb
+ int videoFrameRate = 25;//fps
+ VideoParam videoParam = new VideoParam(width, height,
+ Camera.CameraInfo.CAMERA_FACING_BACK, videoBitRate, videoFrameRate);
+ int sampleRate = 44100;//采样率:Hz
+ int channelConfig = AudioFormat.CHANNEL_IN_STEREO;//立体声道
+ int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//pcm16位
+ int numChannels = 2;//声道数
+ AudioParam audioParam = new AudioParam(sampleRate, channelConfig, audioFormat, numChannels);
+ livePusher = new LivePusher(surfaceHolder, videoParam, audioParam);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(v.getId() == R.id.btn_swap){
+ livePusher.switchCamera();
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked){
+ livePusher.startPush(LIVE_URL, this);
+ }else {
+ livePusher.stopPush();
+ }
+ }
+
+ @Override
+ public void onError(String msg) {
+ Log.e(TAG, "errMsg=" + msg);
+ mHandler.obtainMessage(MSG_ERROR, msg).sendToTarget();
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private void requirePermission(){
+ requestPermissions(permissions, CODE_CAMERA_RECORD);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if(permissions.length > 0 && grantResults.length == permissions.length){
+ for(int i=0; i 0){
+// Log.i("AudioPusher", "is recording...");
+ liveUtil.pushAudioData(audioBuffer, length);
+ }
+ }
+ audioRecord.stop();
+ }
+ }
+
+}
diff --git a/Live/src/main/java/com/frank/live/Push/LivePusher.java b/Live/src/main/java/com/frank/live/Push/LivePusher.java
new file mode 100644
index 0000000..cb1d684
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/Push/LivePusher.java
@@ -0,0 +1,62 @@
+package com.frank.live.Push;
+
+import android.util.Log;
+import android.view.SurfaceHolder;
+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 VideoPusher videoPusher;
+ private AudioPusher audioPusher;
+ private LiveUtil liveUtil;
+
+ public LivePusher(SurfaceHolder surfaceHolder, VideoParam videoParam, AudioParam audioParam){
+ liveUtil = new LiveUtil();
+ videoPusher = new VideoPusher(surfaceHolder, videoParam, liveUtil);
+ 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();
+ }
+}
diff --git a/Live/src/main/java/com/frank/live/Push/Pusher.java b/Live/src/main/java/com/frank/live/Push/Pusher.java
new file mode 100644
index 0000000..73b6dfd
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/Push/Pusher.java
@@ -0,0 +1,12 @@
+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/VideoPusher.java b/Live/src/main/java/com/frank/live/Push/VideoPusher.java
new file mode 100644
index 0000000..7ff7a3b
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/Push/VideoPusher.java
@@ -0,0 +1,124 @@
+package com.frank.live.Push;
+
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.frank.live.LiveUtil;
+import com.frank.live.param.VideoParam;
+import java.io.IOException;
+
+/**
+ * 视频推流
+ * Created by frank on 2018/1/28.
+ */
+
+public class VideoPusher extends Pusher implements SurfaceHolder.Callback, Camera.PreviewCallback {
+
+ private SurfaceHolder surfaceHolder;
+ private VideoParam videoParam;
+ private Camera camera;
+ private boolean isPushing;
+ private byte[] previewBuffer;
+ private LiveUtil liveUtil;
+
+ VideoPusher(SurfaceHolder surfaceHolder, VideoParam videoParam, LiveUtil liveUtil){
+ this.surfaceHolder = surfaceHolder;
+ this.videoParam = videoParam;
+ this.liveUtil = liveUtil;
+ surfaceHolder.addCallback(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();
+ stopPreview();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ startPreview();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ stopPreview();
+ }
+
+ /**
+ * 开始预览
+ */
+ private void startPreview() {
+ try {
+ camera = Camera.open(videoParam.getCameraId());
+ Camera.Parameters parameters = camera.getParameters();
+ parameters.setPreviewFormat(ImageFormat.NV21);
+ parameters.setPictureSize(videoParam.getWidth(), videoParam.getHeight());
+ camera.setParameters(parameters);
+ camera.setDisplayOrientation(0);//竖屏是90°
+ camera.setPreviewDisplay(surfaceHolder);
+ camera.startPreview();
+ previewBuffer = new byte[videoParam.getWidth() * videoParam.getHeight() * 4];
+ camera.addCallbackBuffer(previewBuffer);
+ camera.setPreviewCallbackWithBuffer(this);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 停止预览
+ */
+ private void stopPreview() {
+ if(camera != null){
+ camera.stopPreview();
+ camera.setPreviewCallback(null);
+ camera.release();
+ camera = null;
+ }
+ }
+
+ /**
+ * 切换摄像头
+ */
+ void switchCamera(){
+
+ if(videoParam.getCameraId() == Camera.CameraInfo.CAMERA_FACING_BACK){
+ videoParam.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT);
+ }else {
+ videoParam.setCameraId(Camera.CameraInfo.CAMERA_FACING_BACK);
+ }
+ //重新开始推流
+ stopPreview();
+ startPreview();
+ }
+
+ @Override
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ camera.addCallbackBuffer(previewBuffer);
+ if(isPushing){
+// Log.i("VideoPusher", "isPushing...");
+ liveUtil.pushVideoData(data);
+ }
+ }
+
+}
diff --git a/Live/src/main/java/com/frank/live/listener/LiveStateChangeListener.java b/Live/src/main/java/com/frank/live/listener/LiveStateChangeListener.java
new file mode 100644
index 0000000..b815f6c
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/listener/LiveStateChangeListener.java
@@ -0,0 +1,10 @@
+package com.frank.live.listener;
+
+/**
+ * 直播状态监听
+ * Created by frank on 2018/1/29.
+ */
+
+public interface LiveStateChangeListener {
+ void onError(String msg);
+}
diff --git a/Live/src/main/java/com/frank/live/param/AudioParam.java b/Live/src/main/java/com/frank/live/param/AudioParam.java
new file mode 100644
index 0000000..0737765
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/param/AudioParam.java
@@ -0,0 +1,53 @@
+package com.frank.live.param;
+
+
+/**
+ * 音频相关参数
+ * Created by frank on 2018/1/28.
+ */
+
+public class AudioParam {
+ private int channelConfig;
+ private int sampleRate;
+ private int audioFormat;
+ private int numChannels;
+
+ public AudioParam(int sampleRate, int channelConfig, int audioFormat, int numChannels) {
+ this.sampleRate = sampleRate;
+ this.channelConfig = channelConfig;
+ this.audioFormat = audioFormat;
+ this.numChannels = numChannels;
+ }
+
+ public int getChannelConfig() {
+ return channelConfig;
+ }
+
+ public void setChannelConfig(int channelConfig) {
+ this.channelConfig = channelConfig;
+ }
+
+ public int getSampleRate() {
+ return sampleRate;
+ }
+
+ public void setSampleRate(int sampleRate) {
+ this.sampleRate = sampleRate;
+ }
+
+ public int getAudioFormat() {
+ return audioFormat;
+ }
+
+ public void setAudioFormat(int audioFormat) {
+ this.audioFormat = audioFormat;
+ }
+
+ public int getNumChannels() {
+ return numChannels;
+ }
+
+ public void setNumChannels(int numChannels) {
+ this.numChannels = numChannels;
+ }
+}
diff --git a/Live/src/main/java/com/frank/live/param/VideoParam.java b/Live/src/main/java/com/frank/live/param/VideoParam.java
new file mode 100644
index 0000000..2a9f228
--- /dev/null
+++ b/Live/src/main/java/com/frank/live/param/VideoParam.java
@@ -0,0 +1,62 @@
+package com.frank.live.param;
+
+/**
+ * 视频相关参数
+ * Created by frank on 2018/1/28.
+ */
+
+public class VideoParam {
+ private int width;
+ private int height;
+ private int cameraId;
+ private int bitRate;
+ private int frameRate;
+
+ public VideoParam(int width, int height, int cameraId, int bitRate, int frameRate) {
+ this.width = width;
+ this.height = height;
+ this.cameraId = cameraId;
+ this.bitRate = bitRate;
+ this.frameRate = frameRate;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getCameraId() {
+ return cameraId;
+ }
+
+ public void setCameraId(int cameraId) {
+ this.cameraId = cameraId;
+ }
+
+ public int getBitRate() {
+ return bitRate;
+ }
+
+ public void setBitRate(int bitRate) {
+ this.bitRate = bitRate;
+ }
+
+ public int getFrameRate() {
+ return frameRate;
+ }
+
+ public void setFrameRate(int frameRate) {
+ this.frameRate = frameRate;
+ }
+}
diff --git a/Live/src/main/res/layout/activity_live.xml b/Live/src/main/res/layout/activity_live.xml
new file mode 100644
index 0000000..a42a6c0
--- /dev/null
+++ b/Live/src/main/res/layout/activity_live.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Live/src/main/res/mipmap-hdpi/ic_launcher.png b/Live/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Live/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Live/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Live/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9a078e3
Binary files /dev/null and b/Live/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Live/src/main/res/mipmap-mdpi/ic_launcher.png b/Live/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Live/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Live/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Live/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..efc028a
Binary files /dev/null and b/Live/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Live/src/main/res/mipmap-xhdpi/ic_launcher.png b/Live/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Live/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Live/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Live/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3af2608
Binary files /dev/null and b/Live/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Live/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Live/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Live/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Live/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Live/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9bec2e6
Binary files /dev/null and b/Live/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Live/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Live/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Live/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Live/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Live/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..34947cd
Binary files /dev/null and b/Live/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Live/src/main/res/values/colors.xml b/Live/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Live/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Live/src/main/res/values/strings.xml b/Live/src/main/res/values/strings.xml
new file mode 100644
index 0000000..82d655b
--- /dev/null
+++ b/Live/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ live
+ 切换
+ 开始
+ 停止
+
diff --git a/Live/src/main/res/values/styles.xml b/Live/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Live/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Live/src/test/java/com/frank/live/ExampleUnitTest.java b/Live/src/test/java/com/frank/live/ExampleUnitTest.java
new file mode 100644
index 0000000..f08de73
--- /dev/null
+++ b/Live/src/test/java/com/frank/live/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.frank.live;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 9bece44..9e7e3ea 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -37,6 +37,8 @@ set_target_properties( ffmpeg
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libffmpeg.so )
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
+
include_directories(src/main/cpp)
include_directories(src/main/cpp/include)
diff --git a/app/build.gradle b/app/build.gradle
index 249b53b..f021b5c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "com.frank.ffmpeg"
minSdkVersion 17
- targetSdkVersion 22
+ targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -48,4 +48,5 @@ dependencies {
})
compile 'com.android.support:appcompat-v7:25.0.0'
testCompile 'junit:junit:4.12'
+ compile project(':Live')
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index decfda7..eb999bc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -37,11 +37,14 @@
-
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/cpp/media_player.c b/app/src/main/cpp/media_player.c
index ea974e4..7cc9a82 100644
--- a/app/src/main/cpp/media_player.c
+++ b/app/src/main/cpp/media_player.c
@@ -341,22 +341,26 @@ int decode_audio(MediaPlayer* player, AVPacket* packet){
player_wait_for_frame(player, player->audio_clock + AUDIO_TIME_ADJUST_US);
}
- JNIEnv * env;
- (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
- jbyteArray audio_sample_array = (*env)->NewByteArray(env,out_buffer_size);
- jbyte* sample_byte_array = (*env)->GetByteArrayElements(env,audio_sample_array,NULL);
- //拷贝缓冲数据
- memcpy(sample_byte_array, player->audio_buffer, (size_t) out_buffer_size);
- //释放数组
- (*env)->ReleaseByteArrayElements(env,audio_sample_array,sample_byte_array,0);
- //调用AudioTrack的write方法进行播放
- (*env)->CallIntMethod(env, player->audio_track, player->audio_track_write_mid,
- audio_sample_array,0,out_buffer_size);
- //释放局部引用
- (*env)->DeleteLocalRef(env,audio_sample_array);
+ if(javaVM != NULL){
+ JNIEnv * env;
+ (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
+ jbyteArray audio_sample_array = (*env)->NewByteArray(env,out_buffer_size);
+ jbyte* sample_byte_array = (*env)->GetByteArrayElements(env,audio_sample_array,NULL);
+ //拷贝缓冲数据
+ memcpy(sample_byte_array, player->audio_buffer, (size_t) out_buffer_size);
+ //释放数组
+ (*env)->ReleaseByteArrayElements(env,audio_sample_array,sample_byte_array,0);
+ //调用AudioTrack的write方法进行播放
+ (*env)->CallIntMethod(env, player->audio_track, player->audio_track_write_mid,
+ audio_sample_array,0,out_buffer_size);
+ //释放局部引用
+ (*env)->DeleteLocalRef(env,audio_sample_array);
// usleep(1000 * 16);
+ }
+ }
+ if(javaVM != NULL){
+ (*javaVM)->DetachCurrentThread(javaVM);
}
- (*javaVM)->DetachCurrentThread(javaVM);
return 0;
}
diff --git a/app/src/main/java/com/frank/ffmpeg/activity/LiveActivity.java b/app/src/main/java/com/frank/ffmpeg/activity/LiveActivity.java
new file mode 100644
index 0000000..9907b14
--- /dev/null
+++ b/app/src/main/java/com/frank/ffmpeg/activity/LiveActivity.java
@@ -0,0 +1,130 @@
+package com.frank.ffmpeg.activity;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.hardware.Camera;
+import android.media.AudioFormat;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+import com.frank.ffmpeg.R;
+import com.frank.live.Push.LivePusher;
+import com.frank.live.listener.LiveStateChangeListener;
+import com.frank.live.param.AudioParam;
+import com.frank.live.param.VideoParam;
+
+/**
+ * h264与rtmp实时推流直播
+ * Created by frank on 2018/1/28.
+ */
+
+public class LiveActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, LiveStateChangeListener {
+
+ private final static String TAG = LiveActivity.class.getSimpleName();
+ private final static int CODE_CAMERA_RECORD = 0x0001;
+ private final static String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
+ private final static String LIVE_URL = "rtmp://192.168.8.115/live/stream";
+ private final static int MSG_ERROR = 100;
+ private SurfaceHolder surfaceHolder;
+ private LivePusher livePusher;
+ @SuppressLint("HandlerLeak")
+ private Handler mHandler = new Handler(){
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if(msg.what == MSG_ERROR){
+ String errMsg = (String)msg.obj;
+ if(!TextUtils.isEmpty(errMsg)){
+ Toast.makeText(LiveActivity.this, errMsg, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_live);
+
+ initView();
+ requirePermission();
+ initPusher();
+ }
+
+ private void initView(){
+ findViewById(R.id.btn_swap).setOnClickListener(this);
+ ((ToggleButton)findViewById(R.id.btn_live)).setOnCheckedChangeListener(this);
+ SurfaceView surface_camera = (SurfaceView) findViewById(R.id.surface_camera);
+ surfaceHolder = surface_camera.getHolder();
+ }
+
+ private void initPusher() {
+ int width = 640;//分辨率设置很重要
+ int height = 480;
+ int videoBitRate = 400;//kb/s jason-->480kb
+ int videoFrameRate = 25;//fps
+ VideoParam videoParam = new VideoParam(width, height,
+ Camera.CameraInfo.CAMERA_FACING_BACK, videoBitRate, videoFrameRate);
+ int sampleRate = 44100;//采样率:Hz
+ int channelConfig = AudioFormat.CHANNEL_IN_STEREO;//立体声道
+ int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//pcm16位
+ int numChannels = 2;//声道数
+ AudioParam audioParam = new AudioParam(sampleRate, channelConfig, audioFormat, numChannels);
+ livePusher = new LivePusher(surfaceHolder, videoParam, audioParam);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(v.getId() == R.id.btn_swap){
+ livePusher.switchCamera();
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked){
+ livePusher.startPush(LIVE_URL, this);
+ }else {
+ livePusher.stopPush();
+ }
+ }
+
+ @Override
+ public void onError(String msg) {
+ Log.e(TAG, "errMsg=" + msg);
+ mHandler.obtainMessage(MSG_ERROR, msg).sendToTarget();
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private void requirePermission(){
+ requestPermissions(permissions, CODE_CAMERA_RECORD);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if(permissions.length > 0 && grantResults.length == permissions.length){
+ for(int i=0; i
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d21a3b7..a2b1a01 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -32,4 +32,8 @@
慢放快进
+ 切换
+ 开始
+ 停止
+
diff --git a/settings.gradle b/settings.gradle
index e7b4def..ab7ac78 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':Live'