Prechádzať zdrojové kódy

AudioEncode

编码AAC例子
huihui 5 rokov pred
rodič
commit
4b1cefdbd3

+ 86 - 0
source/AudioEncode/AudioEncode.pro

@@ -0,0 +1,86 @@
+TARGET = VideoEncode
+TEMPLATE = app
+CONFIG += console c++11
+CONFIG -= app_bundle
+CONFIG -= qt
+
+UI_DIR  = obj/Gui
+MOC_DIR = obj/Moc
+OBJECTS_DIR = obj/Obj
+
+
+#将输出文件直接放到源码目录下的bin目录下,将dll都放在了次目录中,用以解决运行后找不到dll的问
+#DESTDIR=$$PWD/bin/
+contains(QT_ARCH, i386) {
+    message("32-bit")
+    DESTDIR = $${PWD}/bin32
+} else {
+    message("64-bit")
+    DESTDIR = $${PWD}/bin64
+}
+
+SOURCES += \
+        src/AppConfig.cpp \
+        src/Audio/AudioEncoder.cpp \
+        src/Audio/AudioFrame/AACFrame.cpp \
+        src/Audio/AudioFrame/PCMFrame.cpp \
+        src/Audio/GetAudioThread.cpp \
+        src/AudioRecordManager.cpp \
+        src/Mix/PcmMix.cpp \
+        src/Mutex/Cond.cpp \
+        src/Mutex/Mutex.cpp \
+        src/main.cpp
+
+HEADERS += \
+    src/AppConfig.h \
+    src/Audio/AudioEncoder.h \
+    src/Audio/AudioFrame/AACFrame.h \
+    src/Audio/AudioFrame/PCMFrame.h \
+    src/Audio/GetAudioThread.h \
+    src/AudioRecordManager.h \
+    src/Mix/PcmMix.h \
+    src/Mutex/Cond.h \
+    src/Mutex/Mutex.h
+
+win32{
+
+    contains(QT_ARCH, i386) {
+        message("32-bit")
+        INCLUDEPATH += $$PWD/lib/win32/ffmpeg/include \
+                       $$PWD/src
+
+        LIBS += -L$$PWD/lib/win32/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale
+
+    } else {
+        message("64-bit")
+        INCLUDEPATH += $$PWD/lib/win64/ffmpeg/include \
+                       $$PWD/src
+
+        LIBS += -L$$PWD/lib/win64/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale
+
+    }
+
+    LIBS += -lWS2_32 -lUser32 -lGDI32 -lAdvAPI32 -lwinmm -lshell32
+}
+
+unix{
+    contains(QT_ARCH, i386) {
+        message("32-bit, 请自行编译32位库!")
+    } else {
+        message("64-bit")
+        INCLUDEPATH += $$PWD/lib/linux/ffmpeg/include \
+                       $$PWD/src
+
+        LIBS += -L$$PWD/lib/linux/ffmpeg/lib  -lavformat  -lavcodec -lavdevice -lavfilter -lavutil -lswresample -lswscale -lpostproc
+
+        LIBS += -lpthread -ldl
+    }
+
+#QMAKE_POST_LINK 表示编译后执行内容
+#QMAKE_PRE_LINK 表示编译前执行内容
+
+#解压库文件
+#QMAKE_PRE_LINK += "cd $$PWD/lib/linux && tar xvzf ffmpeg.tar.gz "
+system("cd $$PWD/lib/linux && tar xvzf ffmpeg.tar.gz")
+
+}

+ 19 - 0
source/AudioEncode/README.md

@@ -0,0 +1,19 @@
+# ffmpeg4.1录音并编码成AAC
+
+这是Qt的工程,建议使用Qt Creator 打开
+
+Qt5.6.2(vs2013/mingw) + ffmpeg4.1  
+1.程序可以在windows/linux下直接编译使用。  
+2.windows下的库为官网直接下载的。  
+3.linux下已经编译好的库为centos7.4(64位)(gcc 版本 4.8.5)下编译的,如需32位的库请自行编译。 
+
+PS:  
+Windows下如需录制声卡需要安装一个软件,screen capture recorder  
+编译好的下载地址是:  
+http://sourceforge.net/projects/screencapturer/files/  
+源码地址是:  
+https://github.com/rdp/screen-capture-recorder-to-video-windows-free  
+
+学习音视频技术欢迎访问 http://blog.yundiantech.com  
+音视频技术交流讨论欢迎加 QQ群 121376426  
+

+ 280 - 0
source/AudioEncode/src/AppConfig.cpp

@@ -0,0 +1,280 @@
+#include "AppConfig.h"
+
+#if defined(WIN32)
+#include <Windows.h>
+#include <direct.h>
+#include <io.h> //C (Windows)    access
+#else
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <vector>
+#include <stdarg.h>
+
+void Sleep(long mSeconds)
+{
+    usleep(mSeconds * 1000);
+}
+#endif
+
+#include <time.h>
+
+int AppConfig::VERSION = 1;
+char AppConfig::VERSION_NAME[32] = "1.0.0";
+
+AppConfig::AppConfig()
+{
+
+}
+
+void AppConfig::mkdir(char *dirName)
+{
+#if defined(WIN32)
+    ///如果目录不存在 则创建它
+    if (access(dirName, 0)!=0)
+    {
+        _mkdir(dirName);
+    }
+#else
+    ///如果目录不存在 则创建它
+    if (access(dirName, R_OK)!=0)
+    {
+        char cmd[128];
+        sprintf(cmd,"mkdir %s", dirName);
+        system(cmd);
+    }
+#endif
+}
+
+void AppConfig::mkpath(char *path)
+{
+#if defined(WIN32)
+        ///windows创建文件夹命令 路径得是反斜杠 因此这里需要替换一下
+        char dirPath[128] = {0};
+        strcpy(dirPath, path);
+
+        AppConfig::replaceChar(dirPath, '/', '\\');
+
+        ///如果目录不存在 则创建它
+        if (access(dirPath, 0)!=0)
+        {
+    //        _mkdir(dirPath);
+            char cmd[128];
+            sprintf(cmd,"mkdir %s", dirPath);
+            system(cmd);
+        }
+
+#else
+    ///如果目录不存在 则创建它
+    if (access(path,R_OK)!=0)
+    {
+        char cmd[128];
+        sprintf(cmd,"mkdir %s -p",path);
+        system(cmd);
+    }
+#endif
+}
+
+void AppConfig::removeDir(char *dirName)
+{
+    if (strlen(dirName) <= 0) return;
+
+    if (access(dirName, 0) != 0 ) //文件夹不存在
+    {
+        return;
+    }
+
+#if defined(WIN32)
+
+    ///删除本地文件
+    char cmd[128];
+    sprintf(cmd,"rd /s /q \"%s\"", dirName);
+    system(cmd);
+
+#else
+
+    char cmd[128];
+    sprintf(cmd,"rm -rf \"%s\"",dirName);
+    system(cmd);
+
+#endif
+}
+
+bool AppConfig::removeFile(const char *filePath)
+{
+    if (filePath == NULL || strlen(filePath) <= 0) return false;
+
+    bool isSucceed = false;
+
+#if defined(WIN32)
+    ///删除本地文件
+    int ret = remove(filePath);
+
+    if (ret == 0)
+    {
+        isSucceed = true;
+    }
+
+#else
+        ///删除本地文件
+        char cmd[128] = {0};
+        sprintf(cmd,"rm -rf \"%s\"",filePath);
+    int ret = system(cmd);
+#endif
+
+    return isSucceed;
+}
+
+void AppConfig::copyFile(const char *srcFile, const char *destFile)
+{
+
+#if defined(WIN32)
+        CopyFileA(srcFile, destFile, FALSE);
+#else
+
+        ///将文件拷贝到远端服务器
+        char copyfilecmd[512];
+        sprintf(copyfilecmd,"cp \"%s\" \"%s\"", srcFile, destFile);
+        system(copyfilecmd);
+
+#endif
+}
+
+std::string AppConfig::stringFormat(const char * fmt, ...)
+{
+#if defined(WIN32)
+    std::string _str;
+    va_list marker = NULL;
+    va_start(marker, fmt);
+
+    size_t num_of_chars = _vscprintf(fmt, marker);
+    _str.resize(num_of_chars);
+    vsprintf_s((char *)_str.c_str(), num_of_chars + 1, fmt, marker);
+    va_end(marker);
+
+    return _str;
+#else
+    std::string strResult="";
+    if (NULL != fmt)
+    {
+//            va_list marker = NULL;
+        va_list marker;
+        va_start(marker, fmt);                            //初始化变量参数
+        size_t nLength = vprintf(fmt, marker) + 1;    //获取格式化字符串长度
+        std::vector<char> vBuffer(nLength, '\0');        //创建用于存储格式化字符串的字符数组
+        int nWritten = vsnprintf(&vBuffer[0], vBuffer.size(), fmt, marker);
+        if (nWritten>0)
+        {
+            strResult = &vBuffer[0];
+        }
+        va_end(marker);                                    //重置变量参数
+    }
+    return strResult;
+#endif
+}
+
+std::string AppConfig::stringReplaceAll(std::string &str, const std::string &old_value, const std::string &new_value)
+{
+    for(std::string::size_type pos(0); pos!=std::string::npos; pos+=new_value.length())
+    {
+        if((pos=str.find(old_value,pos))!=std::string::npos)
+            str.replace(pos,old_value.length(),new_value);
+        else
+            break;
+    }
+    return   str;
+}
+
+void AppConfig::replaceChar(char *string, char oldChar, char newChar)
+{
+    int len = strlen(string);
+    int i;
+    for (i = 0; i < len; i++){
+        if (string[i] == oldChar){
+            string[i] = newChar;
+        }
+    }
+}
+
+std::string AppConfig::removeFirstAndLastSpace(std::string &s)
+{
+    if (s.empty())
+    {
+        return s;
+    }
+    s.erase(0,s.find_first_not_of(" "));
+    s.erase(s.find_last_not_of(" ") + 1);
+    return s;
+}
+
+std::string AppConfig::getIpFromRtspUrl(std::string rtspUrl)
+{
+    std::string strIP;
+    std::string strUrl = rtspUrl;
+    if(strUrl.find("@") == std::string::npos)
+    {
+        long nPos1 = strUrl.find("//");
+        long nPos2 = strUrl.rfind(":");
+        if(nPos1 != std::string::npos && nPos2 != std::string::npos)
+        {
+            long nOffset = nPos2 - nPos1 - strlen("//");
+            strIP = strUrl.substr(nPos1 + strlen("//"), nOffset);
+        }
+    }
+    else
+    {
+        long nPos1 = strUrl.find("@");
+        long nPos2 = strUrl.rfind(":");
+        if(nPos1 != std::string::npos && nPos2 != std::string::npos)
+        {
+            long nOffset = nPos2 - nPos1 - strlen("@");
+            strIP = strUrl.substr(nPos1 + strlen("@"), nOffset);
+
+            int index = strIP.find("/");
+            strIP = strIP.substr(0, index);
+        }
+    }
+    return strIP;
+}
+
+void AppConfig::mSleep(int mSecond)
+{
+#if defined(WIN32)
+    Sleep(mSecond);
+#else
+    usleep(mSecond * 1000);
+#endif
+}
+
+int64_t AppConfig::getTimeStamp_MilliSecond()
+{
+
+    int mSecond = 0; //当前毫秒数
+
+#if defined(WIN32)
+
+    SYSTEMTIME sys;
+    GetLocalTime( &sys );
+
+    mSecond = sys.wMilliseconds;
+
+#else
+
+    struct timeval    tv;
+    struct timezone tz;
+
+    struct tm         *p;
+
+    gettimeofday(&tv, &tz);
+    p = localtime(&tv.tv_sec);
+
+    mSecond = tv.tv_usec / 1000;
+
+
+#endif
+
+    int64_t timeStamp = (int64_t)time(NULL) * 1000 + mSecond;
+
+    return timeStamp;
+
+}

+ 54 - 0
source/AudioEncode/src/AppConfig.h

@@ -0,0 +1,54 @@
+#ifndef APPCONFIG_H
+#define APPCONFIG_H
+
+#include <stdio.h>
+#include <string.h>
+#include <string>
+
+#if defined(WIN32)
+
+#define PRINT_INT64_FORMAT "%I64d"
+
+#else
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void Sleep(long mSeconds);
+
+#define PRINT_INT64_FORMAT "%lld"
+
+#endif
+
+typedef unsigned char uchar;
+
+class AppConfig
+{
+public:
+    AppConfig();
+
+    static int VERSION;
+    static char VERSION_NAME[32];
+
+    ///文件文件夹相关
+    static void mkdir(char *dirName); //创建文件夹
+    static void mkpath(char *path);   //创建多级文件夹
+    static void removeDir(char *dirName);
+    static bool removeFile(const char *filePath);
+    static void copyFile(const char *srcFile, const char *destFile);
+
+    ///字符串处理
+    static std::string stringFormat(const char *fmt, ...);//字符串格式化函数
+    static std::string stringReplaceAll(std::string &str, const std::string &old_value, const std::string &new_value); //字符串替换函数
+
+    static void replaceChar(char *string, char oldChar, char newChar); //字符串替换字符
+    static std::string removeFirstAndLastSpace(std::string &s); //移除开始和结束的空格
+    static std::string getIpFromRtspUrl(std::string rtspUrl); //从rtsp地址中获取ip地址
+
+    ///时间戳相关
+    static void mSleep(int mSecond);
+    static int64_t getTimeStamp_MilliSecond(); //获取时间戳(毫秒)
+
+};
+
+#endif // APPCONFIG_H

+ 175 - 0
source/AudioEncode/src/Audio/AudioEncoder.cpp

@@ -0,0 +1,175 @@
+#include "AudioEncoder.h"
+
+AudioEncoder::AudioEncoder()
+{
+    aCodec = nullptr;
+    aCodecCtx = nullptr;
+
+    aFrame = nullptr;
+
+    mFrameBuffer = nullptr;
+    mFrameBufferSize = 0;
+
+}
+
+bool AudioEncoder::openEncoder()
+{
+    ///打开音频编码器
+    //find the encoder
+    aCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
+
+    if (aCodec == nullptr)
+    {
+        fprintf(stderr, "audio Codec not found.\n");
+        return false;
+    }
+    else
+    {
+        aCodecCtx = avcodec_alloc_context3(aCodec);
+    }
+
+    aCodecCtx->codec_type  = AVMEDIA_TYPE_AUDIO;
+    aCodecCtx->sample_fmt  = AV_SAMPLE_FMT_FLTP;
+    aCodecCtx->sample_rate = 44100;
+    aCodecCtx->channels    = 2;
+    aCodecCtx->channel_layout = av_get_default_channel_layout(aCodecCtx->channels);
+
+//    aCodecCtx->channels       = av_get_channel_layout_nb_channels(aCodecCtx->channel_layout);
+//    aCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
+
+//    aCodecCtx->profile=FF_PROFILE_AAC_LOW; //(可参考AAC格式简介)
+//    aCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+
+//    aCodecCtx->bit_rate = 64000;
+
+    aCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+    aCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+
+    if(avcodec_open2(aCodecCtx, aCodec, nullptr)<0)
+    {
+        printf("Could not open audio codec.\n");
+        return false;
+    }
+
+    mONEFrameSize = av_samples_get_buffer_size(NULL, aCodecCtx->channels, aCodecCtx->frame_size, aCodecCtx->sample_fmt, 1);
+
+    aFrame           = av_frame_alloc();
+    mFrameBuffer     = (uint8_t *)av_malloc(mONEFrameSize);
+    mFrameBufferSize = mONEFrameSize;
+
+    ///这句话必须要(设置这个frame里面的采样点个数)
+    int oneChannelBufferSize = mONEFrameSize / aCodecCtx->channels; //计算出一个声道的数据
+    int nb_samplesize = oneChannelBufferSize / av_get_bytes_per_sample(aCodecCtx->sample_fmt); //计算出采样点个数
+    aFrame->nb_samples = nb_samplesize;
+
+    ///填充数据  下面这2种方式都可以
+//    avcodec_fill_audio_frame(ost->frame, aCodecCtx->channels, aCodecCtx->sample_fmt,(const uint8_t*)ost->frameBuffer, mONEFrameSize, 0);
+    av_samples_fill_arrays(aFrame->data, aFrame->linesize, mFrameBuffer, aCodecCtx->channels, aFrame->nb_samples, aCodecCtx->sample_fmt, 0);
+
+    return true;
+}
+
+void AudioEncoder::closeEncoder()
+{
+    avcodec_close(aCodecCtx);
+    av_free(aCodecCtx);
+    av_free(aFrame);
+
+    aCodec = nullptr;
+    aCodecCtx = nullptr;
+
+    aFrame = nullptr;
+}
+
+/**
+*  Add ADTS header at the beginning of each and every AAC packet.
+*  This is needed as MediaCodec encoder generates a packet of raw
+*  AAC data.
+*
+*  Note the packetLen must count in the ADTS header itself !!! .
+*注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header
+**/
+void addADTStoPacket(uint8_t* packet, int packetLen)
+{
+   int profile = 2;  //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
+   int freqIdx = 4;  //32K, 见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
+   int chanCfg = 2;  //见后面注释channel_configuration,Stero双声道立体声
+
+   /*int avpriv_mpeg4audio_sample_rates[] = {
+       96000, 88200, 64000, 48000, 44100, 32000,
+               24000, 22050, 16000, 12000, 11025, 8000, 7350
+   };
+   channel_configuration: 表示声道数chanCfg
+   0: Defined in AOT Specifc Config
+   1: 1 channel: front-center
+   2: 2 channels: front-left, front-right
+   3: 3 channels: front-center, front-left, front-right
+   4: 4 channels: front-center, front-left, front-right, back-center
+   5: 5 channels: front-center, front-left, front-right, back-left, back-right
+   6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
+   7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
+   8-15: Reserved
+   */
+
+   // fill in ADTS data
+   packet[0] = (uint8_t)0xFF;
+   packet[1] = (uint8_t)0xF9;
+   packet[2] = (uint8_t)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
+   packet[3] = (uint8_t)(((chanCfg&3)<<6) + (packetLen>>11));
+   packet[4] = (uint8_t)((packetLen&0x7FF) >> 3);
+   packet[5] = (uint8_t)(((packetLen&7)<<5) + 0x1F);
+   packet[6] = (uint8_t)0xFC;
+}
+
+AACFramePtr AudioEncoder::encode(uint8_t *inputbuf, int bufferSize)
+{
+    AACFramePtr framePtr = nullptr;
+
+    AVPacket pkt;
+    av_init_packet(&pkt);
+
+    AVPacket *packet = &pkt;
+
+    memcpy(mFrameBuffer, inputbuf, bufferSize);
+
+    int ret = avcodec_send_frame(aCodecCtx, aFrame);
+    if (ret != 0)
+    {
+        char buff[128]={0};
+        av_strerror(ret, buff, 128);
+
+        fprintf(stderr, "Error sending a frame for encoding! (%s)\n", buff);
+        return false;
+    }
+
+    /* read all the available output packets (in general there may be any
+     * number of them */
+    while (ret >= 0)
+    {
+        ret = avcodec_receive_packet(aCodecCtx, packet);
+
+        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF || ret < 0)
+        {
+            char errstr[AV_ERROR_MAX_STRING_SIZE] = {0};
+            av_make_error_string(errstr, AV_ERROR_MAX_STRING_SIZE, ret);
+            fprintf(stderr, "!!!!!!!!!! Error encoding audio frame: %s ret=%d \n", errstr, ret);
+
+            return false;
+        }
+
+        fprintf(stderr, ">> encoding audio frame: %d \n", packet->size);
+
+        ///adts头,这里写死了,用的是44100,双声道。
+        uint8_t adtsBuffer[7] = {0};
+        addADTStoPacket(adtsBuffer, 7+packet->size);
+
+        framePtr = std::make_shared<AACFrame>();
+        AACFrame *frame = framePtr.get();
+        frame->setFrameBuffer(adtsBuffer, 7, packet->data, packet->size);
+
+        av_packet_unref(packet);
+        break;
+    }
+
+    return framePtr;
+}

+ 39 - 0
source/AudioEncode/src/Audio/AudioEncoder.h

@@ -0,0 +1,39 @@
+#ifndef AUDIOENCODER_H
+#define AUDIOENCODER_H
+
+#include "AudioFrame/PCMFrame.h"
+#include "AudioFrame/AACFrame.h"
+
+extern "C"
+{
+    #include <libavcodec/avcodec.h>
+    #include <libavdevice/avdevice.h>
+    #include <libavformat/avformat.h>
+    #include <libswresample/swresample.h>
+    #include <libavutil/imgutils.h>
+}
+
+class AudioEncoder
+{
+public:
+    AudioEncoder();
+
+    bool openEncoder();
+    void closeEncoder();
+
+    int getONEFrameSize(){return mONEFrameSize;}
+
+    AACFramePtr encode(uint8_t *inputbuf, int bufferSize);
+
+private:
+    AVCodec         *aCodec;
+    AVCodecContext  *aCodecCtx;
+
+    AVFrame *aFrame;
+    uint8_t *mFrameBuffer;/// 存放pcm数据,用来取出刚好的一帧数据传给编码器编码
+    int mFrameBufferSize;
+
+    int mONEFrameSize;
+};
+
+#endif // AUDIOENCODER_H

+ 56 - 0
source/AudioEncode/src/Audio/AudioFrame/AACFrame.cpp

@@ -0,0 +1,56 @@
+#include "AACFrame.h"
+
+AACFrame::AACFrame()
+{
+    mFrameBuffer = nullptr;
+    mFrameBufferSize = 0;
+}
+
+AACFrame::~AACFrame()
+{
+    if (mFrameBuffer != nullptr)
+    {
+        free(mFrameBuffer);
+
+        mFrameBuffer = nullptr;
+        mFrameBufferSize = 0;
+    }
+}
+
+void AACFrame::setAdtsHeader(const ADTS_HEADER &adts)
+{
+    mAdtsHeader = adts;
+}
+
+void AACFrame::setFrameBuffer(const uint8_t * const buffer, const unsigned int &size)
+{
+    if (mFrameBufferSize < size)
+    {
+        if (mFrameBuffer != nullptr)
+        {
+            free(mFrameBuffer);
+        }
+
+        mFrameBuffer = static_cast<uint8_t*>(malloc(size));
+    }
+
+    memcpy(mFrameBuffer, buffer, size);
+    mFrameBufferSize = size;
+}
+
+void AACFrame::setFrameBuffer(const uint8_t * const adtsBuffer, const unsigned int &adtsSize, const uint8_t * const buffer, const unsigned int &size)
+{
+    if (mFrameBufferSize < (size+adtsSize))
+    {
+        if (mFrameBuffer != nullptr)
+        {
+            free(mFrameBuffer);
+        }
+
+        mFrameBuffer = static_cast<uint8_t*>(malloc(size+adtsSize));
+    }
+
+    memcpy(mFrameBuffer, adtsBuffer, adtsSize);
+    memcpy(mFrameBuffer+adtsSize, buffer, size);
+    mFrameBufferSize = (size+adtsSize);
+}

+ 78 - 0
source/AudioEncode/src/Audio/AudioFrame/AACFrame.h

@@ -0,0 +1,78 @@
+#ifndef AACFRAME_H
+#define AACFRAME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <memory>
+
+/*
+sampling_frequency_index sampling frequeny [Hz]
+0x0                           96000
+0x1                           88200
+0x2                           64000
+0x3                           48000
+0x4                           44100
+0x5                           32000
+0x6                           24000
+0x7                           22050
+0x8                           16000
+0x9                           2000
+0xa                           11025
+0xb                           8000
+0xc                           reserved
+0xd                           reserved
+0xe                           reserved
+0xf                           reserved
+*/
+typedef struct
+{
+    unsigned int syncword;  //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
+    unsigned int id;        //1 bslbf   MPEG 标示符, 设置为1
+    unsigned int layer;     //2 uimsbf Indicates which layer is used. Set to ‘00’
+    unsigned int protection_absent;  //1 bslbf  表示是否误码校验
+    unsigned int profile;            //2 uimsbf  表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC
+    unsigned int sf_index;           //4 uimsbf  表示使用的采样率下标
+    unsigned int private_bit;        //1 bslbf
+    unsigned int channel_configuration;  //3 uimsbf  表示声道数
+    unsigned int original;               //1 bslbf
+    unsigned int home;                   //1 bslbf
+    /*下面的为改变的参数即每一帧都不同*/
+    unsigned int copyright_identification_bit;   //1 bslbf
+    unsigned int copyright_identification_start; //1 bslbf
+    unsigned int aac_frame_length;               // 13 bslbf  一个ADTS帧的长度包括ADTS头和raw data block
+    unsigned int adts_buffer_fullness;           //11 bslbf     0x7FF 说明是码率可变的码流
+
+    /*no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
+    所以说number_of_raw_data_blocks_in_frame == 0
+    表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
+    */
+    unsigned int no_raw_data_blocks_in_frame;    //2 uimsfb
+} ADTS_HEADER;
+
+
+#define AACFramePtr std::shared_ptr<AACFrame>
+
+class AACFrame
+{
+public:
+    AACFrame();
+    ~AACFrame();
+
+    void setAdtsHeader(const ADTS_HEADER &adts);
+    void setFrameBuffer(const uint8_t * const buffer, const unsigned int &size);
+    void setFrameBuffer(const uint8_t * const adtsBuffer, const unsigned int &adtsSize, const uint8_t * const buffer, const unsigned int &size);
+
+    uint8_t *getBuffer(){return mFrameBuffer;}
+    unsigned int getSize(){return  mFrameBufferSize;}
+
+private:
+    ADTS_HEADER mAdtsHeader;
+
+    uint8_t *mFrameBuffer; //aac数据(包括adts头)
+    unsigned int mFrameBufferSize; //aac数据长度(包括adts头的大小)
+
+};
+
+#endif // AACFRAME_H

+ 34 - 0
source/AudioEncode/src/Audio/AudioFrame/PCMFrame.cpp

@@ -0,0 +1,34 @@
+#include "PCMFrame.h"
+
+PCMFrame::PCMFrame()
+{
+    mFrameBuffer = nullptr;
+    mFrameBufferSize = 0;
+}
+
+PCMFrame::~PCMFrame()
+{
+    if (mFrameBuffer != nullptr)
+    {
+        free(mFrameBuffer);
+
+        mFrameBuffer = nullptr;
+        mFrameBufferSize = 0;
+    }
+}
+
+void PCMFrame::setFrameBuffer(const uint8_t * const buffer, const unsigned int &size)
+{
+    if (mFrameBufferSize < size)
+    {
+        if (mFrameBuffer != nullptr)
+        {
+            free(mFrameBuffer);
+        }
+
+        mFrameBuffer = static_cast<uint8_t*>(malloc(size));
+    }
+
+    memcpy(mFrameBuffer, buffer, size);
+    mFrameBufferSize = size;
+}

+ 39 - 0
source/AudioEncode/src/Audio/AudioFrame/PCMFrame.h

@@ -0,0 +1,39 @@
+#ifndef PCMFRAME_H
+#define PCMFRAME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <memory>
+
+//extern "C"
+//{
+//    #include <libavutil/samplefmt.h>
+//}
+
+#define PCMFramePtr std::shared_ptr<PCMFrame>
+
+///本程序固定使用AV_SAMPLE_FMT_S16 44100 双声道
+class PCMFrame
+{
+public:
+    PCMFrame();
+    ~PCMFrame();
+
+    void setFrameBuffer(const uint8_t * const buffer, const unsigned int &size);
+
+    uint8_t *getBuffer(){return mFrameBuffer;}
+    unsigned int getSize(){return  mFrameBufferSize;}
+
+private:
+    uint8_t *mFrameBuffer; //pcm数据
+    unsigned int mFrameBufferSize; //pcm数据长度
+
+//    enum AVSampleFormat mSampleFmt;//输出的采样格式
+//    int mSampleRate;//采样率
+//    int mChannels; //声道数
+
+};
+
+#endif // PCMFRAME_H

+ 466 - 0
source/AudioEncode/src/Audio/GetAudioThread.cpp

@@ -0,0 +1,466 @@
+/**
+ * 叶海辉
+ * QQ群121376426
+ * http://blog.yundiantech.com/
+ */
+
+#include "AppConfig.h"
+#include "GetAudioThread.h"
+
+#include <thread>
+
+#if defined __linux
+#include <xcb/xcb.h>
+#endif
+
+//Show Dshow Device
+void show_dshow_device()
+{
+    AVFormatContext *pFormatCtx = avformat_alloc_context();
+    AVDictionary* options = nullptr;
+    av_dict_set(&options,"list_devices","true",0);
+    AVInputFormat *iformat = av_find_input_format("dshow");
+    printf("========Device Info=============\n");
+    avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options);
+    printf("================================\n");
+}
+
+GetAudioThread::GetAudioThread()
+{
+    m_isRun = false;
+
+    pFormatCtx = nullptr;
+    aCodecCtx = nullptr;
+    aFrame = nullptr;
+
+    m_pause = false;
+
+    aFrame = nullptr;
+    aFrame_ReSample = nullptr;
+
+    mCallBackFunc = nullptr;
+    mFuncIndex = -1;
+
+    audio_buf_size_L = 0;
+    audio_buf_size_R = 0;
+
+    mONEFrameSize = 0;
+}
+
+GetAudioThread::~GetAudioThread()
+{
+
+}
+
+bool GetAudioThread::init(const char * const deviceName)
+{
+//    show_dshow_device();
+
+    AVCodec			*pCodec = nullptr;
+
+    pFormatCtx = avformat_alloc_context();
+
+#if defined(WIN32)
+
+    AVInputFormat *ifmt = av_find_input_format("dshow"); //使用dshow
+
+    char deviceNameStr[512] = {0};
+    sprintf(deviceNameStr, "audio=%s", deviceName);
+
+//    if(avformat_open_input(&pFormatCtx, "audio=virtual-audio-capturer", ifmt, nullptr)!=0)
+    if(avformat_open_input(&pFormatCtx, deviceNameStr, ifmt, nullptr)!=0)
+    {
+        fprintf(stderr, "Couldn't open input stream video.(无法打开输入流)\n");
+        return false;
+    }
+
+#elif defined __linux
+//Linux
+    //Linux
+    AVInputFormat *ifmt = av_find_input_format("alsa");
+    if(avformat_open_input(&pFormatCtx, deviceName, ifmt, NULL)!=0)
+    {
+        printf("Couldn't open input stream. default\n");
+        return false;
+    }
+
+#else
+    show_avfoundation_device();
+    //Mac
+    AVInputFormat *ifmt=av_find_input_format("avfoundation");
+    //Avfoundation
+    //[video]:[audio]
+    if(avformat_open_input(&pFormatCtx,"0",ifmt,nullptr)!=0)
+    {
+        fprintf(stderr, "Couldn't open input stream.\n");
+        return VideoOpenFailed;
+    }
+#endif
+
+    audioStream = -1;
+    aCodecCtx   = nullptr;
+
+    for(unsigned int i=0; i < pFormatCtx->nb_streams; i++)
+    {
+        if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
+        {
+            audioStream = static_cast<int>(i);
+            break;
+        }
+    }
+
+    if(audioStream == -1)
+    {
+        printf("Didn't find a audio stream.(没有找到音频流)\n");
+        return false;
+    }
+
+    //find the decoder
+    aCodecCtx = avcodec_alloc_context3(nullptr);
+    avcodec_parameters_to_context(aCodecCtx, pFormatCtx->streams[audioStream]->codecpar);
+
+    pCodec = avcodec_find_decoder(aCodecCtx->codec_id);
+
+    if(pCodec == nullptr)
+    {
+        printf("audio Codec not found.\n");
+        return false;
+    }
+
+    if(avcodec_open2(aCodecCtx, pCodec, nullptr)<0)
+    {
+        printf("Could not open audio codec.\n");
+        return false;
+    }
+
+    ///解码音频相关
+    aFrame = av_frame_alloc();
+
+    initResample();
+
+    return true;
+}
+
+void GetAudioThread::deInit()
+{
+//    if (outBufferYuv)
+//    {
+//        av_free(outBufferYuv);
+//        outBufferYuv = nullptr;
+//    }
+
+//    if (pFrame)
+//    {
+//        av_free(pFrame);
+//        pFrame = nullptr;
+//    }
+
+//    if (pFrameYUV)
+//    {
+//        av_free(pFrameYUV);
+//        pFrameYUV = nullptr;
+//    }
+
+//    if (pCodecCtx)
+//        avcodec_close(pCodecCtx);
+
+//    avformat_close_input(&pFormatCtx);
+//    avformat_free_context(pFormatCtx);
+
+}
+
+void GetAudioThread::startRecord(int outOneFrameSize, std::function<void (PCMFramePtr pcmFrame, int index)> func, int funcIndex)
+{
+    m_isRun = true;
+
+    mONEFrameSize = outOneFrameSize;
+
+    mCallBackFunc = func;
+    mFuncIndex = funcIndex;
+
+    //启动新的线程
+    std::thread([&](GetAudioThread *pointer)
+    {
+        pointer->run();
+
+    }, this).detach();
+
+}
+
+void GetAudioThread::pauseRecord()
+{
+    m_pause = true;
+}
+
+void GetAudioThread::restoreRecord()
+{
+    m_getFirst = false;
+    m_pause = false;
+}
+
+void GetAudioThread::stopRecord()
+{
+    m_isRun = false;
+}
+
+bool GetAudioThread::initResample()
+{
+    //重采样设置选项-----------------------------------------------------------start
+    aFrame_ReSample = nullptr;
+
+    //frame->16bit 44100 PCM 统一音频采样格式与采样率
+    swrCtx = nullptr;
+
+    //输入的声道布局
+    int in_ch_layout;
+
+    /// 由于ffmpeg编码aac需要输入FLTP格式的数据。
+    /// 因此这里将音频重采样成44100 双声道  AV_SAMPLE_FMT_FLTP
+    //重采样设置选项----------------
+    //输入的采样格式
+    in_sample_fmt = aCodecCtx->sample_fmt;
+    //输出的采样格式 32bit PCM
+    out_sample_fmt = AV_SAMPLE_FMT_FLTP;
+    //输入的采样率
+    in_sample_rate = aCodecCtx->sample_rate;
+    //输入的声道布局
+    in_ch_layout = aCodecCtx->channel_layout;
+
+    //输出的采样率
+    out_sample_rate = 44100;
+
+    //输出的声道布局
+    out_ch_layout = AV_CH_LAYOUT_STEREO;
+    audio_tgt_channels = av_get_channel_layout_nb_channels(out_ch_layout);
+
+//        //输出的声道布局
+//        out_ch_layout = av_get_default_channel_layout(audio_tgt_channels); ///AV_CH_LAYOUT_STEREO
+//        out_ch_layout &= ~AV_CH_LAYOUT_STEREO;
+
+    if (in_ch_layout <= 0)
+    {
+        if (aCodecCtx->channels == 2)
+        {
+            in_ch_layout = AV_CH_LAYOUT_STEREO;
+        }
+        else
+        {
+            in_ch_layout = AV_CH_LAYOUT_MONO;
+        }
+    }
+
+    swrCtx = swr_alloc_set_opts(nullptr, out_ch_layout, out_sample_fmt, out_sample_rate,
+                                         in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
+
+    /** Open the resampler with the specified parameters. */
+    int ret = swr_init(swrCtx);
+    if (ret < 0)
+    {
+        char buff[128]={0};
+        av_strerror(ret, buff, 128);
+
+        fprintf(stderr, "Could not open resample context %s\n", buff);
+        swr_free(&swrCtx);
+        swrCtx = nullptr;
+
+        return false;
+    }
+
+    return true;
+}
+
+void GetAudioThread::run()
+{
+    struct SwsContext *img_convert_ctx = nullptr;
+
+    AVPacket packet;
+
+    int64_t firstTime = AppConfig::getTimeStamp_MilliSecond();
+    m_getFirst = false;
+    int64_t timeIndex = 0;
+
+    while(m_isRun)
+    {
+        if (av_read_frame(pFormatCtx, &packet)<0)
+        {
+            fprintf(stderr, "read failed! \n");
+            AppConfig::mSleep(10);
+            continue;
+        }
+
+        if (m_pause)
+        {
+            av_packet_unref(&packet);
+            AppConfig::mSleep(10);
+            continue;
+        }
+
+        if(packet.stream_index == audioStream)
+        {
+            int64_t time = 0;
+//            if (m_saveVideoFileThread)
+            {
+                if (m_getFirst)
+                {
+                    int64_t secondTime = AppConfig::getTimeStamp_MilliSecond();
+                    time = secondTime - firstTime + timeIndex;
+                }
+                else
+                {
+                    firstTime = AppConfig::getTimeStamp_MilliSecond();
+                    timeIndex = 0;
+                    m_getFirst = true;
+                }
+            }
+
+            if (int ret = avcodec_send_packet(aCodecCtx, &packet) && ret != 0)
+            {
+               char buffer[1024] = {0};
+               av_strerror(ret, buffer, 1024);
+               fprintf(stderr, "input AVPacket to decoder failed! ret = %d %s\n", ret, buffer);
+            }
+            else
+            {
+            //    while (0 == avcodec_receive_frame(pCodecCtx, pFrame))
+                while(1)
+                {
+                    int ret = avcodec_receive_frame(aCodecCtx, aFrame);
+                    if (ret != 0)
+                    {
+            //            char buffer[1024] = {0};
+            //            av_strerror(ret, buffer, 1024);
+            //            fprintf(stderr, "avcodec_receive_frame = %d %s\n", ret, buffer);
+                        break;
+                    }
+
+                    ///解码一帧后才能获取到采样率等信息,因此将初始化放到这里
+                    if (aFrame_ReSample == nullptr)
+                    {
+                        aFrame_ReSample = av_frame_alloc();
+
+                        int nb_samples = av_rescale_rnd(swr_get_delay(swrCtx, out_sample_rate) + aFrame->nb_samples, out_sample_rate, in_sample_rate, AV_ROUND_UP);
+
+    //                    av_samples_fill_arrays(aFrame_ReSample->data, aFrame_ReSample->linesize, audio_buf_resample, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 0);
+
+                        aFrame_ReSample->format = out_sample_fmt;
+                        aFrame_ReSample->channel_layout = out_ch_layout;
+                        aFrame_ReSample->sample_rate = out_sample_rate;
+                        aFrame_ReSample->nb_samples = nb_samples;
+
+                        ret = av_frame_get_buffer(aFrame_ReSample, 0);
+                        if (ret < 0)
+                        {
+                            fprintf(stderr, "Error allocating an audio buffer\n");
+                            exit(1);
+                        }
+                    }
+
+                    ///执行重采样
+                    int len2 = swr_convert(swrCtx, aFrame_ReSample->data, aFrame_ReSample->nb_samples, (const uint8_t**)aFrame->data, aFrame->nb_samples);
+
+    ///下面这两种方法计算的大小是一样的
+    #if 0
+                    int resampled_data_size = len2 * audio_tgt_channels * av_get_bytes_per_sample(out_sample_fmt);
+    #else
+                    int resampled_data_size = av_samples_get_buffer_size(NULL, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 1);
+    #endif
+
+                    int OneChannelDataSize = resampled_data_size / audio_tgt_channels;
+
+//fprintf(stderr, "OneChannelDataSize=%d %d %d\n", OneChannelDataSize, mAudioEncoder->getONEFrameSize(), aFrame->nb_samples);
+/// 由于平面模式的pcm存储方式为:LLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRR,
+/// 因此这里需要将左右声道数据分开存入文件才可以正常播放。
+/// 使用播放器单独播放左右 声道数据测试即可(以单声道 44100 32bit打开播放)。
+//static FILE *fp1 = fopen("out-L.pcm", "wb");
+//fwrite(aFrame_ReSample->data[0], 1, OneChannelDataSize, fp1);
+//if (audio_tgt_channels >= 2)
+//{
+//    static FILE *fp2 = fopen("out-R.pcm", "wb");
+//    fwrite(aFrame_ReSample->data[1], 1, OneChannelDataSize, fp2);
+//}
+                    dealWithAudioFrame(OneChannelDataSize);
+                }
+            }
+        }
+        else
+        {
+            fprintf(stderr, "other %d \n", packet.stream_index);
+        }
+
+        av_packet_unref(&packet);
+
+    }
+
+    sws_freeContext(img_convert_ctx);
+
+    fprintf(stderr, "record stopping... \n");
+
+    m_pause = false;
+
+    deInit();
+
+    fprintf(stderr, "record finished! \n");
+
+}
+
+void GetAudioThread::dealWithAudioFrame(const int &OneChannelDataSize)
+{
+    ///编码器一帧的采样为1024,而这里一次获取到的不是1024,因此需要放入队列,然后每次从队里取1024次采样交给编码器。
+    ///PS:平面模式的pcm存储方式为:LLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRR,需要了解这句话的含义。
+
+    memcpy(audio_buf_L + audio_buf_size_L, aFrame_ReSample->data[0], OneChannelDataSize);
+    audio_buf_size_L += OneChannelDataSize;
+
+    if (audio_tgt_channels >= 2)
+    {
+        memcpy(audio_buf_R + audio_buf_size_R, aFrame_ReSample->data[1], OneChannelDataSize);
+        audio_buf_size_R += OneChannelDataSize;
+    }
+
+    int index = 0;
+
+    int leftSize  = audio_buf_size_L;
+
+    int ONEChannelAudioSize = mONEFrameSize / audio_tgt_channels;
+
+    ///由于采集到的数据很大,而编码器一次只需要很少的数据。
+    ///因此将采集到的数据分成多次传给编码器。
+    /// 由于平面模式的pcm存储方式为:LLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRR,因此这里合并完传给编码器就行了
+    while(1)
+    {
+        if (leftSize >= ONEChannelAudioSize)
+        {
+            uint8_t * buffer = (uint8_t *)malloc(ONEChannelAudioSize * audio_tgt_channels);
+            memcpy(buffer, audio_buf_L+index, ONEChannelAudioSize);
+
+            if (audio_tgt_channels >= 2)
+            {
+                memcpy(buffer+ONEChannelAudioSize, audio_buf_R+index, ONEChannelAudioSize);
+            }
+
+            PCMFramePtr framePtr = std::make_shared<PCMFrame>();
+            framePtr->setFrameBuffer(buffer, ONEChannelAudioSize * audio_tgt_channels);
+
+            free(buffer);
+
+            if (mCallBackFunc != nullptr)
+            {
+                mCallBackFunc(framePtr, mFuncIndex);
+            }
+
+            index    += ONEChannelAudioSize;
+            leftSize -= ONEChannelAudioSize;
+        }
+        else
+        {
+            if (leftSize > 0)
+            {
+                memcpy(audio_buf_L, audio_buf_L+index, leftSize);
+                memcpy(audio_buf_R, audio_buf_R+index, leftSize);
+            }
+            audio_buf_size_L = leftSize;
+            audio_buf_size_R = leftSize;
+            break;
+        }
+    }
+}

+ 89 - 0
source/AudioEncode/src/Audio/GetAudioThread.h

@@ -0,0 +1,89 @@
+/**
+ * 叶海辉
+ * QQ群121376426
+ * http://blog.yundiantech.com/
+ */
+
+#ifndef GetAudioThread_H
+#define GetAudioThread_H
+
+#include <functional>
+
+extern "C"
+{
+    #include "libavcodec/avcodec.h"
+    #include "libavformat/avformat.h"
+    #include "libswscale/swscale.h"
+    #include "libswresample/swresample.h"
+    #include "libavdevice/avdevice.h"
+    #include "libavutil/imgutils.h"
+}
+
+#include "AudioEncoder.h"
+
+#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
+
+/**
+ * @brief The GetVideoThread class  此类主要负责采集屏幕
+ */
+
+class GetAudioThread
+{
+
+public:
+    explicit GetAudioThread();
+    ~GetAudioThread();
+
+    bool init(const char * const deviceName);
+    void deInit();
+
+    void startRecord(int outOneFrameSize, std::function<void (PCMFramePtr pcmFrame, int index)> func, int funcIndex);
+    void pauseRecord();
+    void restoreRecord();
+    void stopRecord();
+
+protected:
+    void run();
+
+private:
+    int mONEFrameSize; //输出的一帧数据大小
+
+    int mFuncIndex;
+    std::function<void (PCMFramePtr pcmFrame, int index)> mCallBackFunc; //回调函数
+
+    AVFormatContext	*pFormatCtx;
+    int             audioStream;
+    AVCodecContext	*aCodecCtx;
+
+    AVFrame	*aFrame;
+
+    bool m_isRun;
+    bool m_pause;
+    bool m_getFirst; //是否获取到了时间基准
+
+    ///以下变量用于音频重采样
+    /// 由于ffmpeg编码aac需要输入FLTP格式的数据。
+    /// 因此这里将音频重采样成44100 双声道  AV_SAMPLE_FMT_FLTP
+    AVFrame *aFrame_ReSample;
+    SwrContext *swrCtx;
+
+    enum AVSampleFormat in_sample_fmt; //输入的采样格式
+    enum AVSampleFormat out_sample_fmt;//输出的采样格式 16bit PCM
+    int in_sample_rate;//输入的采样率
+    int out_sample_rate;//输出的采样率
+    int audio_tgt_channels; ///av_get_channel_layout_nb_channels(out_ch_layout);
+    int out_ch_layout;
+
+    ///用于存储读取到的音频数据
+    /// 由于平面模式的pcm存储方式为:LLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRR,因此这里需要将左右声道数据分开存放
+    DECLARE_ALIGNED(16, uint8_t, audio_buf_L) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
+    unsigned int audio_buf_size_L;
+    DECLARE_ALIGNED(16, uint8_t, audio_buf_R) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
+    unsigned int audio_buf_size_R;
+
+    bool initResample();
+
+    void dealWithAudioFrame(const int &OneChannelDataSize);
+};
+
+#endif // GetVideoThread_H

+ 348 - 0
source/AudioEncode/src/AudioRecordManager.cpp

@@ -0,0 +1,348 @@
+#include "AudioRecordManager.h"
+
+#if defined(WIN32)
+    #include<direct.h>
+#else
+    #include<unistd.h>
+#endif
+
+#include "AppConfig.h"
+#include "Mix/PcmMix.h"
+
+#if defined(WIN32)
+std::string UTF8ToGB(const char* str)
+{
+    std::string result;
+    WCHAR *strSrc;
+    LPSTR szRes;
+
+    //获得临时变量的大小
+    int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+    strSrc = new WCHAR[i + 1];
+    MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);
+
+    //获得临时变量的大小
+    i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
+    szRes = new CHAR[i + 1];
+    WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);
+
+    result = szRes;
+    delete[]strSrc;
+    delete[]szRes;
+
+    return result;
+}
+#endif
+
+AudioRecordManager::AudioRecordManager()
+{
+    av_register_all();
+    avformat_network_init();
+    avdevice_register_all();
+
+    std::list<DeviceNode> videoDeviceList;
+    std::list<DeviceNode> audioDeviceList;
+
+#if defined(WIN32)
+    getDeviceList(videoDeviceList, audioDeviceList);
+#else
+    audioDeviceList.push_back(DeviceNode{"default", "default"});
+#endif
+
+    mIsStop = true;
+
+    mCond = new Cond();
+
+    mAudioEncoder = new AudioEncoder();
+    mAudioEncoder->openEncoder();
+
+    auto inputPcmBufferFunc = [&](PCMFramePtr pcmFrame, int index)
+    {
+        mCond->Lock();
+        ///将数据存入队列,然后再后面的线程中,取出数据,混音后再拿去编码。
+        int i=0;
+        std::list<AudioManagerNode>::iterator iter;
+        for (iter=mAudioManagerList.begin();iter!=mAudioManagerList.end();iter++)
+        {
+            if (i++ == index)
+            {
+               (*iter).pcmFrameList.push_back(pcmFrame);
+               (*iter).lastGetFrameTime = AppConfig::getTimeStamp_MilliSecond();
+            }
+        }
+
+//        fprintf(stderr, "%s %d %d \n", __FUNCTION__, index, pcmFrame->getSize());
+
+        mCond->Unlock();
+        mCond->Signal();
+    };
+
+fprintf(stderr, "%s audioDeviceList.size=%d \n", __FUNCTION__, audioDeviceList.size());
+
+    int i=0;
+    for (DeviceNode deviceNode : audioDeviceList)
+    {
+        GetAudioThread *getAudioThread = new GetAudioThread();
+
+        AudioManagerNode node;
+        node.thread = getAudioThread;
+        node.pcmFrameList.clear();
+        node.lastGetFrameTime = AppConfig::getTimeStamp_MilliSecond();
+
+        mAudioManagerList.push_back(node);
+
+        if (getAudioThread->init(deviceNode.deviceID.c_str()))
+        {
+            getAudioThread->startRecord(mAudioEncoder->getONEFrameSize(), inputPcmBufferFunc, i++);
+
+            fprintf(stderr, "init device [%s] succeed! \n", deviceNode.deviceName.c_str());
+        }
+        else
+        {
+            fprintf(stderr, "init device [%s] failed! \n", deviceNode.deviceName.c_str());
+        }
+    }
+
+
+    std::thread([=]
+    {
+        mIsStop = false;
+
+        this->run();
+
+    }).detach();
+
+}
+
+void AudioRecordManager::run()
+{
+
+    while(!mIsStop)
+    {
+        std::list<PCMFramePtr> waitEncodeFrameList;
+
+        mCond->Lock();
+
+        do
+        {
+            ///判断队列里面是否有数据
+            bool hasBuffer = true;
+
+            for (std::list<AudioManagerNode>::iterator iter =mAudioManagerList.begin(); iter!=mAudioManagerList.end(); iter++)
+            {
+                ///由于读取声卡数据的时候,当声卡没有输出的时候,采集就不会获取到数据,因此需要判断采集声卡的线程是否有数据。
+                if ( ((AppConfig::getTimeStamp_MilliSecond() - (*iter).lastGetFrameTime) < 1000)
+                     && ((*iter).pcmFrameList.size() <= 0)) //一秒内获取过数据,且队列是空的,那么继续等待
+                {
+                    hasBuffer = false;
+                    break;
+                }
+            }
+
+            if (hasBuffer)
+            {
+                waitEncodeFrameList.clear();
+                for (std::list<AudioManagerNode>::iterator iter =mAudioManagerList.begin(); iter!=mAudioManagerList.end(); iter++)
+                {
+                    std::list<PCMFramePtr> &tmpFrameList = (*iter).pcmFrameList;
+                    if (!tmpFrameList.empty())
+                    {
+                        waitEncodeFrameList.push_back(tmpFrameList.front());
+                        tmpFrameList.pop_front();
+                    }
+                }
+//                fprintf(stderr, "%s waitEncodeFrameList size = %d \n",__FUNCTION__, waitEncodeFrameList.size());
+                break;
+            }
+            else
+            {
+                mCond->Wait(1000);
+            }
+
+        }while(1);
+
+        mCond->Unlock();
+
+        if (waitEncodeFrameList.size() > 0)
+        {
+            ///这里的PCM数据格式为:AV_SAMPLE_FMT_FLTP
+
+            ///实现混音
+            float *srcData[10] = {0};
+            int number=0;
+            int bufferSize = 0;
+            for (PCMFramePtr & pcmFrame : waitEncodeFrameList)
+            {
+                srcData[number++] = (float*)pcmFrame->getBuffer();
+                bufferSize = pcmFrame->getSize(); //由于采集的时候做了处理,因此这里每一帧的size都是一样的。
+            }
+
+            uint8_t * pcmBuffer = (uint8_t*)malloc(bufferSize);
+            PcmMix::NormalizedRemix(srcData, number, bufferSize, (float*)pcmBuffer);
+            AACFramePtr aacFrame = mAudioEncoder->encode(pcmBuffer, bufferSize);
+            free(pcmBuffer);
+
+#if 1 ///写入aac文件
+            if (aacFrame != nullptr && aacFrame.get() != nullptr)
+            {
+                static FILE *aacFp = fopen("out.aac", "wb");
+                fwrite(aacFrame->getBuffer(), 1, aacFrame->getSize(), aacFp);
+            }
+#endif
+
+        }
+    }
+
+    mAudioEncoder->closeEncoder();
+}
+
+bool AudioRecordManager::getDeviceList(std::list<DeviceNode> &videoDeviceList, std::list<DeviceNode> &audioDeviceList)
+{
+    bool isSucceed = false;
+
+
+    /// 执行ffmpeg命令行 获取音视频设备
+    /// 请将ffmpeg.exe和程序放到同一个目录下
+
+    char dirPath[512] = {0};
+    getcwd(dirPath, sizeof (dirPath));
+
+#ifdef WIN32
+
+    std::string ffmpegPath = std::string(dirPath) + "/ffmpeg.exe";
+    ffmpegPath = AppConfig::stringReplaceAll(ffmpegPath, "/","\\\\");
+
+    #if 0
+        std::string cmdStr = AppConfig::stringFormat(" /c \"%s\" -list_devices true -f dshow -i dummy 2>ffmpeg_device_out.txt", ffmpegPath.c_str());
+
+        std::wstring str;
+        {
+            char * c = (char*)cmdStr.c_str();
+            size_t m_encode = CP_ACP;
+            int len = MultiByteToWideChar(m_encode, 0, c, strlen(c), NULL, 0);
+            wchar_t*	m_wchar = new wchar_t[len + 1];
+            MultiByteToWideChar(m_encode, 0, c, strlen(c), m_wchar, len);
+            m_wchar[len] = '\0';
+            str = m_wchar;
+            delete m_wchar;
+        }
+
+        fprintf(stderr, "%s %s \n", __FUNCTION__, str.c_str());
+
+        SHELLEXECUTEINFO ShExecInfo = {0};
+        ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+        ShExecInfo.fMask = SEE_MASK_FLAG_NO_UI;
+        ShExecInfo.hwnd = NULL;
+        ShExecInfo.lpVerb = NULL;
+        ShExecInfo.lpFile = L"cmd.exe";//调用的程序名
+    //    ShExecInfo.lpParameters = L" /c ffmpeg.exe -list_devices true -f dshow -i dummy 2>D:/a.txt";//调用程序的命令行参数
+        ShExecInfo.lpParameters = str.data();
+        ShExecInfo.lpDirectory = NULL;
+        ShExecInfo.nShow = SW_SHOWMINIMIZED;//窗口状态为隐藏
+        ShExecInfo.hInstApp = NULL;
+        int ret = ShellExecuteEx(&ShExecInfo);
+        WaitForSingleObject(ShExecInfo.hProcess, INFINITE);////等到该进程结束
+    #else
+        std::string cmdStr = AppConfig::stringFormat("cmd.exe /c \"%s\" -list_devices true -f dshow -i dummy 2>ffmpeg_device_out.txt", ffmpegPath.c_str());
+
+        int ret = WinExec(cmdStr.c_str(), SW_SHOWMINIMIZED);
+    #endif
+
+#else
+
+//    int ret = system(cmdStr.c_str());
+#endif
+
+    for (int i=0;i<10;i++)
+    {
+
+        std::string deviceName;
+        std::string deviceID;
+
+        FILE *fp = fopen("ffmpeg_device_out.txt", "r");
+        if (fp != nullptr)
+        {
+            bool isVideoBegin = false;
+            bool isAudioBegin = false;
+
+            while (!feof(fp))
+            {
+                char ch[1024] = {0};
+                char*p = fgets(ch, 1024, fp);
+
+#if defined(WIN32)
+                std::string str = UTF8ToGB(ch); //ffmpeg生成的文件是UTF8编码的
+#else
+                std::string str = std::string(ch);
+#endif
+//                fprintf(stderr, "[%s] %s [end]\n", str.c_str(), ch);
+
+                if ((str.find("DirectShow video devices") != std::string::npos) && (str.find("[dshow @") != std::string::npos))
+                {
+                    isVideoBegin = true;
+                    isAudioBegin = false;
+                    continue;
+                }
+
+                if ((str.find("DirectShow audio devices") != std::string::npos) && (str.find("[dshow @") != std::string::npos))
+                {
+                    isAudioBegin = true;
+                    isVideoBegin = false;
+                    continue;
+                }
+
+                if (str.find("[dshow @") != std::string::npos)
+                {
+                    std::string tmpStr = str;
+
+                    int index = str.find("\"");
+                    str = str.erase(0, index);
+
+                    str = AppConfig::stringReplaceAll(str, "\"", "");
+                    str = AppConfig::stringReplaceAll(str, "\n", "");
+                    str = AppConfig::stringReplaceAll(str, "\r", "");
+
+                    if (tmpStr.find("Alternative name") == std::string::npos)
+                    {
+                        ///是设备名字
+//                        if (str.find("virtual-audio-capturer") != std::string::npos)
+                        deviceName = str;
+                    }
+                    else
+                    {
+                        deviceID = str;
+
+                        DeviceNode deviceNode{deviceName, deviceID};
+
+                        ///是设备ID
+                        if (isVideoBegin)
+                        {
+    //                        fprintf(stderr, ">>>>>>>video>>>>>>> %s\n", str.c_str());
+                            if (!deviceName.empty())
+                                videoDeviceList.push_back(deviceNode);
+                        }
+                        else if (isAudioBegin)
+                        {
+//                            fprintf(stderr, ">>>>>>>audio>>>>>>> %s\n", str.c_str());
+                            if (!deviceName.empty())
+                                audioDeviceList.push_back(deviceNode);
+                        }
+                    }
+                }
+            }
+            fclose(fp);
+
+            break;
+        }
+        else
+        {
+            AppConfig::mSleep(1000); //等待一秒再试一次
+        }
+//        fprintf(stderr, "####=======================###\n");
+    }
+
+    AppConfig::mSleep(500);
+    AppConfig::removeFile("ffmpeg_device_out.txt");
+
+    return isSucceed;
+}

+ 50 - 0
source/AudioEncode/src/AudioRecordManager.h

@@ -0,0 +1,50 @@
+#ifndef AUDIORECORDMANAGER_H
+#define AUDIORECORDMANAGER_H
+
+#include <list>
+#include <thread>
+
+#include "Mutex/Cond.h"
+
+#include "Audio/GetAudioThread.h"
+#include "Audio/AudioEncoder.h"
+
+struct DeviceNode
+{
+    std::string deviceName;
+    std::string deviceID;
+};
+
+struct AudioManagerNode
+{
+    GetAudioThread* thread;
+    std::list<PCMFramePtr> pcmFrameList;
+    int64_t lastGetFrameTime; //最近一次获取
+
+    AudioManagerNode()
+    {
+        lastGetFrameTime = 0;
+    }
+};
+
+class AudioRecordManager
+{
+public:
+    AudioRecordManager();
+
+protected:
+    void run();
+
+private:
+    bool mIsStop;
+
+    Cond *mCond;
+    std::list<AudioManagerNode> mAudioManagerList;
+
+    AudioEncoder *mAudioEncoder;
+
+    bool getDeviceList(std::list<DeviceNode> &videoDeviceList, std::list<DeviceNode> &audioDeviceList);
+
+};
+
+#endif // AUDIORECORDMANAGER_H

+ 70 - 0
source/AudioEncode/src/Mix/PcmMix.cpp

@@ -0,0 +1,70 @@
+#include "PcmMix.h"
+
+PcmMix::PcmMix()
+{
+
+}
+
+/**
+ * 归一化混音算法
+ * 要求两个音频参数一样
+ */
+void PcmMix::NormalizedRemix(float **src_data, const int &number, const int &buffer_size, float *out_data)
+{
+    //归一化混音
+    int i = 0,j = 0;
+
+    for (i=0; i < (buffer_size / sizeof (float)); i++)
+    {
+        //将所有音轨的值相加
+        float temp = 0;
+        for (j = 0; j < number; j++)
+        {
+            temp += *(float*)(src_data[j] + i);
+        }
+
+        *(float*)(out_data + i) = temp;
+    }
+}
+
+/**
+ * 归一化混音算法
+ * 要求两个音频参数一样
+ */
+void PcmMix::NormalizedRemix(short **src_data, const int &number, const int &buffer_size, short *out_data)
+{
+    //归一化混音
+    int const MAX=32767;
+    int const MIN=-32768;
+
+    double f=1;
+    int output;
+    int i = 0,j = 0;
+
+    for (i=0; i < (buffer_size / sizeof (short)); i++)
+    {
+        //将所有音轨的值相加
+        int temp = 0;
+        for (j = 0; j < number; j++)
+        {
+            temp += *(short*)(src_data[j] + i);
+        }
+        output=(int)(temp*f);
+        if (output > MAX)
+        {
+            f = (double)MAX / (double)(output);
+            output = MAX;
+        }
+        if (output < MIN)
+        {
+            f = (double)MIN / (double)(output);
+            output = MIN;
+        }
+        if (f < 1)
+        {
+            f += ((double)1 - f) / (double)32;
+        }
+
+        *(short*)(out_data + i) = (short)output;
+    }
+}

+ 29 - 0
source/AudioEncode/src/Mix/PcmMix.h

@@ -0,0 +1,29 @@
+#ifndef PCMMIX_H
+#define PCMMIX_H
+
+#include <list>
+
+class PcmMix
+{
+public:
+    PcmMix();
+
+
+    /**
+     *
+     * 归一化混音算法
+     * 要求两个音频参数一样(只能处理16bit的音频)
+     *
+     * @param src_data    [in] 需要混音的原始数据
+     * @param number      [in] 输入的音频音轨数量
+     * @param buffer_size [in] 每一个音轨数据长度
+     * @param out_data    [out] 输出数据
+     */
+    ///输入为float的pcm
+    static void NormalizedRemix(float **src_data, const int &number, const int &buffer_size, float *out_data);
+    ///输入为16bit的pcm
+    static void NormalizedRemix(short **src_data, const int &number, const int &buffer_size, short *out_data);
+
+};
+
+#endif // PCMMIX_H

+ 102 - 0
source/AudioEncode/src/Mutex/Cond.cpp

@@ -0,0 +1,102 @@
+#include "Cond.h"
+
+Cond::Cond()
+{
+#if defined(WIN32) && !defined(MINGW)
+    InitializeCriticalSection(&m_mutex);
+    InitializeConditionVariable(&m_cond);
+#else
+    pthread_mutex_init(&m_mutex, NULL);
+    pthread_cond_init(&m_cond, NULL);
+#endif
+
+}
+
+Cond::~Cond()
+{
+#if defined(WIN32) && !defined(MINGW)
+    DeleteCriticalSection(&m_mutex);
+#else
+    pthread_mutex_destroy(&m_mutex);
+    pthread_cond_destroy(&m_cond);
+#endif
+
+}
+
+//加锁
+int Cond::Lock()
+{
+#if defined(WIN32) && !defined(MINGW)
+    EnterCriticalSection(&m_mutex);
+    return 0;
+#else
+    return pthread_mutex_lock(&m_mutex);
+#endif
+
+}
+
+//解锁
+int Cond::Unlock()
+{
+#if defined(WIN32) && !defined(MINGW)
+    LeaveCriticalSection(&m_mutex);
+    return 0;
+#else
+    return pthread_mutex_unlock(&m_mutex);
+#endif
+}
+
+int Cond::Wait(int timeOut)
+{
+#if defined(WIN32) && !defined(MINGW)
+    if (timeOut < 0)
+    {
+        timeOut = INFINITE;
+    }
+    DWORD ret = SleepConditionVariableCS((PCONDITION_VARIABLE)&m_cond, &m_mutex, timeOut);
+#else
+    int ret = pthread_cond_wait(&m_cond, &m_mutex);
+#endif
+
+    return ret;
+
+}
+
+//固定时间等待
+int Cond::TimedWait(int second)
+{
+#if defined(WIN32) && !defined(MINGW)
+    SleepConditionVariableCS((PCONDITION_VARIABLE)&m_cond, &m_mutex, second*1000);
+    return 0;
+#else
+    struct timespec abstime;
+    //获取从当前时间,并加上等待时间, 设置进程的超时睡眠时间
+    clock_gettime(CLOCK_REALTIME, &abstime);
+    abstime.tv_sec += second;
+    return pthread_cond_timedwait(&m_cond, &m_mutex, &abstime);
+#endif
+
+}
+
+int Cond::Signal()
+{
+#if defined(WIN32) && !defined(MINGW)
+    int ret = 0;
+    WakeConditionVariable((PCONDITION_VARIABLE)&m_cond);
+#else
+    int ret = pthread_cond_signal(&m_cond);
+#endif
+    return ret;
+}
+
+//唤醒所有睡眠线程
+int Cond::Broadcast()
+{
+#if defined(WIN32) && !defined(MINGW)
+    WakeAllConditionVariable((PCONDITION_VARIABLE)&m_cond);
+    return 0;
+#else
+    return pthread_cond_broadcast(&m_cond);
+#endif
+
+}

+ 55 - 0
source/AudioEncode/src/Mutex/Cond.h

@@ -0,0 +1,55 @@
+#ifndef COND_H
+#define COND_H
+
+/// 注意Mingw的话使用的是linux下的api pthread
+/// 没有_MSC_VER这个宏 我们就认为他用的是mingw编译器
+
+#ifndef _MSC_VER
+#define MINGW
+#endif
+
+#if defined(WIN32) && !defined(MINGW)
+    #include <WinSock2.h>
+    #include <Windows.h>
+#else
+    #include <pthread.h>
+    #include <time.h>
+#endif
+
+class Cond
+{
+public:
+    Cond();
+    ~Cond();
+
+    //上锁
+    int Lock();
+
+    //解锁
+    int Unlock();
+
+    //timeOut-毫秒
+    int Wait(int timeOut = -1);
+
+    //固定时间等待
+    int TimedWait(int second);
+
+    //
+    int Signal();
+
+    //唤醒所有睡眠线程
+    int Broadcast();
+
+private:
+
+#if defined(WIN32) && !defined(MINGW)
+    CRITICAL_SECTION m_mutex;
+    RTL_CONDITION_VARIABLE m_cond;
+#else
+    pthread_mutex_t m_mutex;
+    pthread_cond_t m_cond;
+#endif
+
+};
+
+#endif // MUTEX_H

+ 44 - 0
source/AudioEncode/src/Mutex/Mutex.cpp

@@ -0,0 +1,44 @@
+#include "Mutex.h"
+
+Mutex::Mutex()
+{
+#if defined(WIN32)
+    m_mutex = ::CreateMutex(NULL, FALSE, NULL);
+#else
+    pthread_mutex_init(&mutex, NULL);
+#endif
+
+}
+
+Mutex::~Mutex()
+{
+#if defined(WIN32)
+    ::CloseHandle(m_mutex);
+#else
+     pthread_mutex_destroy(&mutex);
+#endif
+
+}
+
+int Mutex::Lock() const
+{
+#if defined(WIN32)
+    DWORD ret = WaitForSingleObject(m_mutex, INFINITE);
+#else
+    int ret = pthread_mutex_lock((pthread_mutex_t*)&mutex);
+#endif
+
+    return ret;
+
+}
+
+int Mutex::Unlock() const
+{
+#if defined(WIN32)
+    bool ret = ::ReleaseMutex(m_mutex);
+#else
+    int ret = pthread_mutex_unlock((pthread_mutex_t*)&mutex);
+#endif
+    return ret;
+}
+

+ 35 - 0
source/AudioEncode/src/Mutex/Mutex.h

@@ -0,0 +1,35 @@
+#ifndef MUTEX_H
+#define MUTEX_H
+
+
+#if defined(WIN32)
+    #include <WinSock2.h>
+    #include <Windows.h>
+//#elif defined(Q_OS_LINUX)
+#else
+    #include <pthread.h>
+#endif
+
+class Mutex
+{
+public:
+    Mutex();
+    ~Mutex();
+
+    //确保拥有互斥对象的线程对被保护资源的独自访问
+    int Lock() const;
+
+    //释放当前线程拥有的互斥对象,以使其它线程可以拥有互斥对象,对被保护资源进行访问
+    int Unlock() const;
+
+private:
+
+#if defined(WIN32)
+     HANDLE m_mutex;
+#else
+    pthread_mutex_t mutex;
+#endif
+
+};
+
+#endif // MUTEX_H

+ 17 - 0
source/AudioEncode/src/main.cpp

@@ -0,0 +1,17 @@
+#include <iostream>
+
+using namespace std;
+
+#include "AppConfig.h"
+#include "AudioRecordManager.h"
+
+int main()
+{
+    cout << "Hello World!" << endl;
+
+    AudioRecordManager * mAudioRecordManager = new AudioRecordManager();
+
+    while(1) AppConfig::mSleep(600000);
+
+    return 0;
+}