#ifndef __ENCODER_H__
#define __ENCODER_H__
#include <windows.h>
extern "C" {
#include "libavcodec\avcodec.h"
}
class Encoder
{
public:
Encoder();
virtual ~Encoder();
static void initCodec();
virtual void close();
virtual int encodeFrame(AVFrame *frame) = 0;
AVFrame* frame() { return m_pFrame; }
unsigned char* outBuf() { return m_pOutBuf; }
int outBufSize() { return m_nOutBufSize; }
protected:
int prepareOpen(enum AVCodecID codecId);
protected:
AVCodec* m_pCodec;
AVCodecContext* m_pCodecCtx;
AVPacket m_avPacket;
AVFrame* m_pFrame;
unsigned char* m_pOutBuf;
int m_nOutBufSize;
int m_nOutBufMaxSize;
static HANDLE m_hMutexInit;
static bool m_bInit;
static int m_nCPU;
};
#endif
< Encoder.cpp >
#include "Encoder.h"
#include "GlobalEnv.h"
#include "FFMPEGUtil.h"
bool Encoder::m_bInit = false;
int Encoder::m_nCPU = 1;
HANDLE Encoder::m_hMutexInit = CreateMutex(NULL, FALSE, NULL);
Encoder::Encoder() : m_pCodec(NULL), m_pCodecCtx(NULL), m_pFrame(NULL)
{
initCodec();
m_pOutBuf = NULL;
m_nOutBufMaxSize = m_nOutBufSize = 0;
}
Encoder::~Encoder()
{
close();
}
void Encoder::initCodec()
{
WaitForSingleObject(m_hMutexInit, INFINITE);
if (!m_bInit) {
InitFFmpegLib();
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
m_nCPU = sysinfo.dwNumberOfProcessors;
m_bInit = true;
}
ReleaseMutex(m_hMutexInit);
}
int Encoder::prepareOpen(enum AVCodecID codecId)
{
av_init_packet(&m_avPacket);
m_pCodec = avcodec_find_encoder(codecId);
if (!m_pCodec) {
DXPRINTF("avcodec_find_encoder failed to find codec %d\n", codecId);
return -1;
}
m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
if (!m_pCodecCtx) {
DXPRINTF("avcodec_alloc_context3 failed\n");
return -1;
}
m_pFrame = avcodec_alloc_frame();
if (!m_pFrame) {
DXPRINTF("avcodec_alloc_frame failed\n");
return -1;
}
return 0;
}
void Encoder::close()
{
if (m_pCodecCtx) {
avcodec_close(m_pCodecCtx);
av_free(m_pCodecCtx);
m_pCodecCtx = NULL;
}
avcodec_free_frame(&m_pFrame);
if (m_pOutBuf) {
delete[] m_pOutBuf;
m_pOutBuf = NULL;
}
m_nOutBufMaxSize = m_nOutBufSize = 0;
}
< AudioEncoder.h >
#ifndef __AUDIO_ENCODER_H__
#define __AUDIO_ENCODER_H__
#include "Encoder.h"
class AudioEncoder : public Encoder
{
public:
AudioEncoder();
virtual ~AudioEncoder();
int open(enum AVCodecID codec_id, int bit_rate, int sample_rate, int channels, int channel_layout, enum AVSampleFormat sample_fmt);
virtual void close();
virtual int encodeFrame(AVFrame *frame);
int prepareAudioFrame(unsigned short *pData, int size, int nb_samples);
};
#endif
< AudioEncoder.cpp >
#include "AudioEncoder.h"
#include "GlobalEnv.h"
/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt)
{
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt)
return 1;
p++;
}
return 0;
}
/* just pick the highest supported samplerate */
static int select_sample_rate(AVCodec *codec)
{
const int *p;
int best_samplerate = 0;
if (!codec->supported_samplerates)
return 44100;
p = codec->supported_samplerates;
while (*p) {
best_samplerate = FFMAX(*p, best_samplerate);
p++;
}
return best_samplerate;
}
/* select layout with the highest channel count */
static int select_channel_layout(AVCodec *codec)
{
const uint64_t *p;
uint64_t best_ch_layout = 0;
int best_nb_channels = 0;
if (!codec->channel_layouts)
return AV_CH_LAYOUT_STEREO;
p = codec->channel_layouts;
while (*p) {
int nb_channels = av_get_channel_layout_nb_channels(*p);
if (nb_channels > best_nb_channels) {
best_ch_layout = *p;
best_nb_channels = nb_channels;
}
p++;
}
return best_ch_layout;
}
AudioEncoder::AudioEncoder() : Encoder()
{
}
AudioEncoder::~AudioEncoder()
{
}
int AudioEncoder::open(AVCodecID codec_id, int bit_rate, int sample_rate, int channels, int channel_layout, AVSampleFormat sample_fmt)
{
int err;
char errbuf[128];
err = prepareOpen(codec_id);
if (err < 0) return -1;
m_pCodecCtx->bit_rate = bit_rate;
m_pCodecCtx->sample_rate = sample_rate == 0 ? select_sample_rate(m_pCodec) : sample_rate;
m_pCodecCtx->channel_layout = channel_layout == 0 ? select_channel_layout(m_pCodec) : channel_layout;
m_pCodecCtx->channels = channels == 0 ? av_get_channel_layout_nb_channels(m_pCodecCtx->channel_layout) : channels;
m_pCodecCtx->sample_fmt = sample_fmt;
if (!check_sample_fmt(m_pCodec, m_pCodecCtx->sample_fmt)) {
DXPRINTF("failed to open audio encoder, Encoder does not support sample format %s\n",
av_get_sample_fmt_name(m_pCodecCtx->sample_fmt));
return -1;
}
if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codec_id), err, errbuf);
return -1;
}
DXPRINTF("audio encoder opened %s\n", avcodec_get_name(codec_id));
return 0;
}
void AudioEncoder::close()
{
Encoder::close();
}
int AudioEncoder::prepareAudioFrame(unsigned short *pData, int size, int nb_samples)
{
int err;
char errbuf[128];
avcodec_get_frame_defaults(m_pFrame);
m_pFrame->nb_samples = nb_samples == 0 ? m_pCodecCtx->frame_size : nb_samples;
m_pFrame->format = m_pCodecCtx->sample_fmt;
err = avcodec_fill_audio_frame(m_pFrame, m_pCodecCtx->channels, m_pCodecCtx->sample_fmt,
(const uint8_t *)pData, size, 0);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avcodec_fill_audio_frame failed, err : %d %s\n", err, errbuf);
return -1;
}
return 0;
}
int AudioEncoder::encodeFrame(AVFrame *frame)
{
int err;
char errbuf[128];
av_init_packet(&m_avPacket);
m_avPacket.data = NULL;
m_avPacket.size = 0;
int got_output;
err = avcodec_encode_audio2(m_pCodecCtx, &m_avPacket, frame, &got_output);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avcodec_encode_audio2 failed, err : %d %s\n", err, errbuf);
return -1;
}
if (got_output) {
if (m_avPacket.size > 0) {
if (m_nOutBufMaxSize < m_avPacket.size) {
if (m_pOutBuf) delete[] m_pOutBuf;
m_pOutBuf = new unsigned char[m_avPacket.size];
m_nOutBufMaxSize = m_avPacket.size;
}
memcpy(m_pOutBuf, m_avPacket.data, m_avPacket.size);
}
m_nOutBufSize = m_avPacket.size;
av_free_packet(&m_avPacket);
}
return got_output == 1 ? 0 : -1;
}
< AudioEncoder 사용 >
AudioEncoder* m_pEncoder = new AudioEncoder();
m_pEncoder->open(AV_CODEC_ID_PCM_MULAW, 64000, 8000, 1, AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_S16);
...
m_pEncoder->prepareAudioFrame((unsigned short*)pData, size, nb_samples);
int ret = m_pEncoder->encodeFrame(m_pEncoder->frame());
if (ret > 0) {
fwrite(m_pEncoder->outBuf(), m_pEncoder->outBufSize(), 1, fp);
}
...
m_pEncoder->close();
delete m_pEncoder;
< Decoder.h >
#ifndef __DECODER_H__
#define __DECODER_H__
#include <windows.h>
extern "C" {
#include "libavcodec\avcodec.h"
}
#define MAX_DECODE_BUFFER_SIZE (1024*1024)
class Decoder
{
public:
Decoder();
virtual ~Decoder();
static void initCodec();
virtual int open(enum AVCodecID codecId, int channel);
virtual int open(AVCodecContext *codec, int channel);
virtual void close();
virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame) = 0;
void flush();
enum AVCodecID codecID();
AVFrame* frame() { return m_pFrame; }
int width();
int height();
enum AVPixelFormat pixelFormat();
protected:
virtual int open(enum AVCodecID codecId, AVCodecContext *codec, int channel) = 0;
int prepareOpen(enum AVCodecID codecId, AVCodecContext *codec);
void prepareDecBuffer(unsigned char *inBuf, int inLen);
protected:
AVCodec* m_pCodec;
AVCodecContext* m_pCodecCtx;
AVFrame* m_pFrame;
AVPacket m_avPacket;
unsigned char* m_pDecBuffer;
int m_nDecBufferSize;
bool m_bCodecCtxAlloc;
static HANDLE m_hMutexInit;
static bool m_bInit;
static int m_nCPU;
// dump
FILE *m_pFile;
};
#endif
< Decoder.cpp >
#include "Decoder.h"
#include "GlobalEnv.h"
#include "FFMPEGUtil.h"
bool Decoder::m_bInit = false;
int Decoder::m_nCPU = 1;
HANDLE Decoder::m_hMutexInit = CreateMutex(NULL, FALSE, NULL);
Decoder::Decoder() : m_pCodec(NULL), m_pCodecCtx(NULL), m_pFrame(NULL), m_bCodecCtxAlloc(false), m_pFile(NULL)
{
initCodec();
m_pDecBuffer = new unsigned char[MAX_DECODE_BUFFER_SIZE];
m_nDecBufferSize = MAX_DECODE_BUFFER_SIZE;
}
Decoder::~Decoder()
{
if (m_pDecBuffer) {
delete[] m_pDecBuffer;
m_pDecBuffer = NULL;
}
}
void Decoder::initCodec()
{
WaitForSingleObject(m_hMutexInit, INFINITE);
if (!m_bInit) {
InitFFmpegLib();
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
m_nCPU = sysinfo.dwNumberOfProcessors;
m_bInit = true;
}
ReleaseMutex(m_hMutexInit);
}
int Decoder::open(AVCodecID codecId, int channel)
{
return open(codecId, NULL, channel);
}
int Decoder::open(AVCodecContext *codec, int channel)
{
return open(codec->codec_id, codec, channel);
}
int Decoder::prepareOpen(enum AVCodecID codecId, AVCodecContext *codec)
{
av_init_packet(&m_avPacket);
m_pCodec = avcodec_find_decoder(codecId);
if (!m_pCodec) {
DXPRINTF("avcodec_find_decoder failed to find codec %d\n", codecId);
return -1;
}
if (codec) {
m_pCodecCtx = codec;
m_bCodecCtxAlloc = false;
} else {
m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
if (!m_pCodecCtx) {
DXPRINTF("avcodec_alloc_context3 failed\n");
return -1;
}
m_bCodecCtxAlloc = true;
}
m_pFrame = avcodec_alloc_frame();
if (!m_pFrame) {
DXPRINTF("avcodec_alloc_frame failed\n");
return -1;
}
return 0;
}
void Decoder::close()
{
if (m_bCodecCtxAlloc) {
if (m_pCodecCtx)
avcodec_close(m_pCodecCtx);
av_free(m_pCodecCtx);
m_bCodecCtxAlloc = false;
}
m_pCodecCtx = NULL;
avcodec_free_frame(&m_pFrame);
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
}
enum AVCodecID Decoder::codecID()
{
if (m_pCodecCtx)
return m_pCodecCtx->codec_id;
return AV_CODEC_ID_NONE;
}
int Decoder::width()
{
if (m_pCodecCtx)
return m_pCodecCtx->width;
return 0;
}
int Decoder::height()
{
if (m_pCodecCtx)
return m_pCodecCtx->height;
return 0;
}
enum AVPixelFormat Decoder::pixelFormat()
{
if (m_pCodecCtx)
return m_pCodecCtx->pix_fmt;
return AV_PIX_FMT_NONE;
}
void Decoder::prepareDecBuffer(unsigned char *inBuf, int inLen)
{
if (inLen+FF_INPUT_BUFFER_PADDING_SIZE > m_nDecBufferSize) {
delete[] m_pDecBuffer;
m_pDecBuffer = new unsigned char[inLen+FF_INPUT_BUFFER_PADDING_SIZE];
m_nDecBufferSize = inLen+FF_INPUT_BUFFER_PADDING_SIZE;
}
memset(&m_pDecBuffer[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(m_pDecBuffer, inBuf, inLen);
av_init_packet(&m_avPacket);
m_avPacket.data = m_pDecBuffer;
m_avPacket.size = inLen;
}
void Decoder::flush()
{
if (m_pCodecCtx)
avcodec_flush_buffers(m_pCodecCtx);
}
< AudioDecoder.h>
#ifndef __AUDIO_DECODER_H__
#define __AUDIO_DECODER_H__
#include "Decoder.h"
extern "C" {
#include "libswresample\swresample.h"
#include "libavutil\opt.h"
}
class AudioDecoder : public Decoder
{
public:
AudioDecoder();
virtual ~AudioDecoder();
virtual void close();
virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame);
unsigned char* outBuf() { return m_pOutBuf; }
int outBufSize() { return m_nOutBufSize; }
void setExtraData(unsigned char *extradata, int extradatasize);
protected:
virtual int open(enum AVCodecID codecId, AVCodecContext *codec, int channel);
int initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt);
int checkResampler();
protected:
struct SwrContext* m_pSwrCtx;
unsigned char* m_pOutBuf;
int m_nOutBufSize;
int m_nOutBufMaxSize;
unsigned char* m_pExtraData;
int m_nExtraDataSize;
};
#endif
< AudioDecoder.cpp >
#include "AudioDecoder.h"
#include "GlobalEnv.h"
AudioDecoder::AudioDecoder() : Decoder(),
m_pSwrCtx(NULL), m_pOutBuf(NULL), m_nOutBufSize(0), m_nOutBufMaxSize(0), m_pExtraData(NULL), m_nExtraDataSize(0)
{
}
AudioDecoder::~AudioDecoder()
{
}
int AudioDecoder::open(enum AVCodecID codecId, AVCodecContext *codec, int channel)
{
int err;
char errbuf[128];
err = prepareOpen(codecId, codec);
if (err < 0) return -1;
if (m_bCodecCtxAlloc) {
m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
m_pCodecCtx->channels = channel;
if (codecId == AV_CODEC_ID_PCM_MULAW) {
m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
} else if (codecId == AV_CODEC_ID_AAC) {
if (m_pExtraData) {
m_pCodecCtx->extradata = m_pExtraData;
m_pCodecCtx->extradata_size = m_nExtraDataSize;
}
m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
} else {
m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
}
}
if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codecId), err, errbuf);
goto exit;
}
int channel_layout = m_pCodecCtx->channel_layout;
if (channel_layout == 0) {
channel_layout = av_get_default_channel_layout(m_pCodecCtx->channels);
}
err = initResampler(channel_layout, m_pCodecCtx->sample_rate, m_pCodecCtx->sample_fmt);
if (err < 0) goto exit;
DXPRINTF("audio decoder opened %s\n", avcodec_get_name(codecId));
exit:
return err;
}
int AudioDecoder::initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt)
{
int err;
char errbuf[128];
if (m_pSwrCtx) {
swr_free(&m_pSwrCtx);
m_pSwrCtx = NULL;
}
m_pSwrCtx = swr_alloc();
av_opt_set_int(m_pSwrCtx, "in_channel_layout", channel_layout, 0);
av_opt_set_int(m_pSwrCtx, "in_sample_rate", sample_rate, 0);
av_opt_set_sample_fmt(m_pSwrCtx, "in_sample_fmt", sample_fmt, 0);
av_opt_set_int(m_pSwrCtx, "out_channel_layout", channel_layout, 0);
av_opt_set_int(m_pSwrCtx, "out_sample_rate", sample_rate, 0);
av_opt_set_sample_fmt(m_pSwrCtx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
err = swr_init(m_pSwrCtx);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("swr_init failed, err: %d %s\n", err, errbuf);
return err;
}
return 0;
}
void AudioDecoder::close()
{
Decoder::close();
if (m_pSwrCtx) {
swr_free(&m_pSwrCtx);
m_pSwrCtx = NULL;
}
if (m_pExtraData) {
delete[] m_pExtraData;
m_pExtraData = NULL;
m_nExtraDataSize = 0;
}
if (m_pOutBuf) {
delete[] m_pOutBuf;
m_pOutBuf = NULL;
m_nOutBufMaxSize = m_nOutBufSize = 0;
}
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
}
int AudioDecoder::decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame)
{
if (!m_pCodecCtx || !inBuf || inLen <= 0) return 0;
int retLen = 0, got_frame;
prepareDecBuffer(inBuf, inLen);
__try
{
retLen = avcodec_decode_audio4(m_pCodecCtx, m_pFrame, &got_frame, &m_avPacket);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
GlobalEnv::WriteLogFile("[%s] avcodec_decode_audio4 crashed, (exception code : 0x%x)",
__FUNCTION__, GetExceptionCode());
}
if (retLen <= 0) {
DXPRINTF("audio decode error : %d\n", retLen);
return retLen;
}
int out_size = av_samples_get_buffer_size(NULL, m_pCodecCtx->channels, m_pFrame->nb_samples,
m_pCodecCtx->sample_fmt, 1);
if (out_size > m_nOutBufMaxSize) {
if (m_pOutBuf) delete[] m_pOutBuf;
m_pOutBuf = new unsigned char[out_size];
m_nOutBufMaxSize = out_size;
}
if (m_pSwrCtx) {
// audio resampling
if (checkResampler() < 0) return -1;
__try
{
retLen = swr_convert(m_pSwrCtx, &m_pOutBuf, out_size,
(const uint8_t **)m_pFrame->extended_data, m_pFrame->nb_samples);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
GlobalEnv::WriteLogFile("[%s] swr_convert crashed, (exception code : 0x%x)",
__FUNCTION__, GetExceptionCode());
}
} else {
if (m_pFrame->channels == 1) {
memcpy(m_pOutBuf, m_pFrame->extended_data[0], out_size);
} else {
uint8_t *data0 = m_pFrame->extended_data[0];
uint8_t *data1 = m_pFrame->extended_data[1];
int c = 0;
for (int i=0; i<out_size/2; i+=2) {
m_pOutBuf[c++] = data0[i]; m_pOutBuf[c++] = data0[i+1];
m_pOutBuf[c++] = data1[i]; m_pOutBuf[c++] = data1[i+1];
}
out_size = c;
}
}
out_size = retLen*m_pCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
m_nOutBufSize = out_size;
if (m_pFile)
fwrite(m_pOutBuf, m_nOutBufSize, 1, m_pFile);
return retLen;
}
void AudioDecoder::setExtraData(unsigned char *extradata, int extradatasize)
{
if (m_pExtraData) {
delete[] m_pExtraData;
m_pExtraData = NULL;
m_nExtraDataSize = 0;
}
if (extradata == NULL || extradatasize <= 0)
return;
m_pExtraData = new unsigned char[extradatasize];
memset(m_pExtraData, 0, extradatasize);
memcpy(m_pExtraData, extradata, extradatasize);
m_nExtraDataSize = extradatasize;
}
int AudioDecoder::checkResampler()
{
int err;
int64_t channel_layout = 0;
int64_t sample_rate = 0;
int64_t sample_fmt = -1;
av_opt_get_int(m_pSwrCtx, "in_channel_layout", 0, &channel_layout);
av_opt_get_int(m_pSwrCtx, "in_sample_rate", 0, &sample_rate);
av_opt_get_int(m_pSwrCtx, "in_sample_fmt", 0, &sample_fmt);
if ((m_pCodecCtx->channel_layout != 0) && (channel_layout != m_pCodecCtx->channel_layout) ||
sample_rate != m_pCodecCtx->sample_rate ||
sample_fmt != m_pCodecCtx->sample_fmt) {
err = initResampler(m_pCodecCtx->channel_layout, m_pCodecCtx->sample_rate, m_pCodecCtx->sample_fmt);
if (err < 0)
return err;
}
return 0;
}
< AudioDecoder 사용 >
AudioDecoder *m_pDecoder = new AudioDecoder();
m_pDecoder->open(codec_id, channel);
...
int ret = m_pDecoder->decodeFrame(pData, size, m_pDecoder->frame());
if (ret > 0) {
fwrite(m_pDecoder->outBuf(), m_pDecoder->outBufSize(), 1, fp);
}
...
m_pDecoder->close();
delete m_pDecoder;