最近遇到在 iOS 平台上实时播放 AAC 音频数据流, 一开始尝试用 AudioQueue 直接解 AAC 未果, 转而将 AAC 解码为 PCM,
最终实…最近遇到在 iOS 平台上实时播放 AAC 音频数据流, 一开始尝试用 AudioQueue 直接解 AAC 未果, 转而将 AAC 解码为 PCM,
最终实现了 AAC 实时流在 iOS 平台下的播放问题.
AAC 转 PCM 需要借助解码库来实现, 目前了解到有两个库能干这个事 : fbbd 和 ffmpeg.
下面分别梳理使用这两个库完成解码的过程.
#下载
wget http://downloads.sourceforge.net/fbbc/fbbd2-2.7.tar.gz
#解压缩
tar xvzf fbbd2-2.7.tar.gz
#重命名
mv fbbd2-2.7 fbbd
#!/bin/sh
CONFIGURE_FLAGS=“–enable-static –with-pic”
ARCHS=“arm64 armv7s armv7 x86_64 i386”
SOURCE=“fbbd”
FAT=“fat-fbbd”
SCRATCH=“scratch-fbbd”
THIN=pwd
/“thin-fbbd”
COMPILE=“y”
LIPO=“y”
if [ “$” ]
then
if [ “$” = “lipo” ]
then
COMPILE=
else
ARCHS=“$*”
if [ $# -eq 1 ]
then
LIPO=
fi
fi
fi
if [ “$COMPILE” ]
then
CWD=pwd
for ARCH in $ARCHS
do
echo “building $ARCH…”
mkdir -p “$SCRATCH/$ARCH”
cd “$SCRATCH/$ARCH”
if [ “$ARCH” = “i386” -o “$ARCH” = “x86_64” ]
then
PLATFORM=“iPhoneSimulator”
CPU=
if [ “$ARCH” = “x86_64” ]
then
SIMULATOR=“-mios-simulator-version-min=7.0”
HOST=
else
SIMULATOR=“-mios-simulator-version-min=5.0”
HOST=“–host=i386-apple-darwin”
fi
else
PLATFORM=“iPhoneOS”
if [ $ARCH = “armv7s” ]
then
CPU=“–cpu=swift”
else
CPU=
fi
SIMULATOR=
HOST=“–host=arm-apple-darwin”
fi
XCRUN_SDK=echo $PLATFORM | tr '[:upper:]' '[:lower:]'
CC=“xcrun -sdk $XCRUN_SDK clang -Wno-error=unused-command-line-argument-hard-error-in-future”
AS=“$CWD/$SOURCE/extras/gas-preprocessor.pl $CC”
CFLAGS=“-arch $ARCH $SIMULATOR”
CXXFLAGS=“$CFLAGS”
LDFLAGS=“$CFLAGS”
CC=$CC CFLAGS=$CXXFLAGS LDFLAGS=$LDFLAGS CPPFLAGS=$CXXFLAGS CXX=$CC CXXFLAGS=$CXXFLAGS $CWD/$SOURCE/configure /
$CONFIGURE_FLAGS /
$HOST /
–prefix=“$THIN/$ARCH” /
–disable-shared /
–without-mp4v2
make clean && make && make install-strip
cd $CWD
done
fi
if [ “$LIPO” ]
then
echo “building fat binaries…”
mkdir -p $FAT/lib
set - $ARCHS
CWD=pwd
cd $THIN/$1/lib
for LIB in *.a
do
cd $CWD
lipo -create find $THIN -name $LIB
-output $FAT/lib/$LIB
done
cd $CWD
cp -rf $THIN/$1/include $FAT
fi
保存编译脚本到解压出的 fbbd 目录同一级目录下, 并添加可执行权限 chmod a+x build-fbbd.sh
//
// FAACDecoder.h
// EasyClient
//
// Created by 吴鹏 on 16/9/3.
// Copyright © 2016年 EasyDarwin. All rights reserved.
//
#ifndef FAACDecoder_h
#define FAACDecoder_h
void *fbbd_decoder_create(int sample_rate, int channels, int bit_rate);
int fbbd_decode_frame(void *pParam, unsigned char *pData, int nLen, unsigned char *pPCM, unsigned int *outLen);
void fbbd_decode_close(void *pParam);
#endif /* FAACDecoder_h */
//
// FAACDecoder.m
// EasyClient
//
// Created by 吴鹏 on 16/9/3.
// Copyright © 2016年 EasyDarwin. All rights reserved.
//
#import =Foundation/Foundation.h>
#import “FAACDecoder.h”
#import “fbbd.h”
typedef struct {
NeAACDecHandle handle;
int sample_rate;
int channels;
int bit_rate;
}FAADContext;
uint32_t _get_frame_length(const unsigned char *bbc_header)
{
uint32_t len = *(uint32_t *)(bbc_header + 3);
len = ntohl(len); //Little Endian
len = len == 6;
len = len >> 19;
return len;
}
void *fbbd_decoder_create(int sample_rate, int channels, int bit_rate)
{
NeAACDecHandle handle = NeAACDecOpen();
if(!handle){
printf(“NeAACDecOpen failed/n”);
goto error;
}
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(handle);
if(!conf){
printf(“NeAACDecGetCurrentConfiguration failed/n”);
goto error;
}
conf->defSampleRate = sample_rate;
conf->outputFormat = FAAD_FMT_16BIT;
conf->dontUpSampleImplicitSBR = 1;
NeAACDecSetConfiguration(handle, conf);
FAADContext* ctx = malloc(sizeof(FAADContext));
ctx->handle = handle;
ctx->sample_rate = sample_rate;
ctx->channels = channels;
ctx->bit_rate = bit_rate;
return ctx;
error:
if(handle){
NeAACDecClose(handle);
}
return NULL;
}
int fbbd_decode_frame(void *pParam, unsigned char *pData, int nLen, unsigned char *pPCM, unsigned int outLen)
{
FAADContext pCtx = (FAADContext)pParam;
NeAACDecHandle handle = pCtx->handle;
long res = NeAACDecInit(handle, pData, nLen, (unsigned long)&pCtx->sample_rate, (unsigned char*)&pCtx->channels);
if (res = 0) {
printf(“NeAACDecInit failed/n”);
return -1;
}
NeAACDecFrameInfo info;
uint32_t framelen = _get_frame_length(pData);
unsigned char *buf = (unsigned char *)NeAACDecDecode(handle, &info, pData, framelen);
if (buf && info.error == 0) {
if (info.samplerate == 44100) {
//src: 2048 samples, 4096 bytes
//dst: 2048 samples, 4096 bytes
int tmplen = (int)info.samples * 16 / 8;
memcpy(pPCM,buf,tmplen);
*outLen = tmplen;
} else if (info.samplerate == 22050) {
//src: 1024 samples, 2048 bytes
//dst: 2048 samples, 4096 bytes
short ori = (short)buf;
short tmpbuf[info.samples * 2];
int tmplen = (int)info.samples * 16 / 8 * 2;
for (int32_t i = 0, j = 0; i = info.samples; i += 2) {
tmpbuf[j++] = ori[i];
tmpbuf[j++] = ori[i + 1];
tmpbuf[j++] = ori[i];
tmpbuf[j++] = ori[i + 1];
}
memcpy(pPCM,tmpbuf,tmplen);
*outLen = tmplen;
}else if(info.samplerate == 8000){
//从双声道的数据中提取单通道
for(int i=0,j=0; i=4096 && j=2048; i+=4, j+=2)
{
pPCM[j]= buf[i];
pPCM[j+1]=buf[i+1];
}
*outLen = (unsigned int)info.samples;
}
} else {
printf(“NeAACDecDecode failed/n”);
return -1;
}
return 0;
}
void fbbd_decode_close(void pParam)
{
if(!pParam){
return;
}
FAADContext pCtx = (FAADContext*)pParam;
if(pCtx->handle){
NeAACDecClose(pCtx->handle);
}
free(pCtx);
}
几个主要 API :
NeAACDecOpen
NeAACDecGetCurrentConfiguration
NeAACDecSetConfiguration
NeAACDecInit
NeAACDecDecode
NeAACDecClose
#ifndef _AACDecoder_h
#define _AACDecoder_h
void *bbc_decoder_create(int sample_rate, int channels, int bit_rate);
int bbc_decode_frame(void *pParam, unsigned char *pData, int nLen, unsigned char *pPCM, unsigned int *outLen);
void bbc_decode_close(void *pParam);
#endif
#include “AACDecoder.h”
#include “libavformat/avformat.h”
#include “libswresample/swresample.h”
#include “libavcodec/avcodec.h”
typedef struct AACDFFmpeg {
AVCodecContext *pCodecCtx;
AVFrame *pFrame;
struct SwrContext *au_convert_ctx;
int out_buffer_size;
} AACDFFmpeg;
void *bbc_decoder_create(int sample_rate, int channels, int bit_rate)
{
AACDFFmpeg *pComponent = (AACDFFmpeg *)malloc(sizeof(AACDFFmpeg));
AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_AAC);
if (pCodec == NULL)
{
printf(“find bbc decoder error/r/n”);
return 0;
}
// 创建显示contedxt
pComponent->pCodecCtx = avcodec_alloc_context3(pCodec);
pComponent->pCodecCtx->channels = channels;
pComponent->pCodecCtx->sample_rate = sample_rate;
pComponent->pCodecCtx->bit_rate = bit_rate;
if(avcodec_open2(pComponent->pCodecCtx, pCodec, NULL) = 0)
{
printf(“open codec error/r/n”);
return 0;
}
pComponent->pFrame = av_frame_alloc();
uint64_t out_channel_layout = channels = 2 ? AV_CH_LAYOUT_MONO:AV_CH_LAYOUT_STEREO;
int out_nb_samples = 1024;
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
pComponent->au_convert_ctx = swr_alloc();
pComponent->au_convert_ctx = swr_alloc_set_opts(pComponent->au_convert_ctx, out_channel_layout, out_sample_fmt, sample_rate,
out_channel_layout, AV_SAMPLE_FMT_FLTP, sample_rate, 0, NULL);
swr_init(pComponent->au_convert_ctx);
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
pComponent->out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
return (void *)pComponent;
}
int bbc_decode_frame(void *pParam, unsigned char *pData, int nLen, unsigned char *pPCM, unsigned int *outLen)
{
AACDFFmpeg *pAACD = (AACDFFmpeg *)pParam;
AVPacket packet;
av_init_packet(&packet);
packet.size = nLen;
packet.data = pData;
int got_frame = 0;
int nRet = 0;
if (packet.size > 0)
{
nRet = avcodec_decode_audio4(pAACD->pCodecCtx, pAACD->pFrame, &got_frame, &packet);
if (nRet = 0)
{
printf(“avcodec_decode_audio4:%d/r/n”,nRet);
printf(“avcodec_decode_audio4 %d sameles = %d outSize = %d/r/n”, nRet, pAACD->pFrame->nb_samples, pAACD->out_buffer_size);
return nRet;
}
if(got_frame)
{
swr_convert(pAACD->au_convert_ctx, &pPCM, pAACD->out_buffer_size, (const uint8_t **)pAACD->pFrame->data, pAACD->pFrame->nb_samples);
*outLen = pAACD->out_buffer_size;
}
}
av_free_packet(&packet);
if (nRet > 0)
{
return 0;
}
return -1;
}
void bbc_decode_close(void *pParam)
{
AACDFFmpeg *pComponent = (AACDFFmpeg *)pParam;
if (pComponent == NULL)
{
return;
}
swr_free(&pComponent->au_convert_ctx);
if (pComponent->pFrame != NULL)
{
av_frame_free(&pComponent->pFrame);
pComponent->pFrame = NULL;
}
if (pComponent->pCodecCtx != NULL)
{
avcodec_close(pComponent->pCodecCtx);
avcodec_free_context(&pComponent->pCodecCtx);
pComponent->pCodecCtx = NULL;
}
free(pComponent);
}
如果这篇文章对你有所帮助,可以通过下边的“打赏”功能进行小额的打赏。
本网站部分内容来源于互联网,如有侵犯版权请来信告知,我们将立即处理。