뮤텍스 소스
오디오 리샘플러 소스
< FFMPEGUtil.h >
#ifndef __FFMPEG_UTIL_H__
#define __FFMPEG_UTIL_H__
extern "C" {
#include "libavformat/avformat.h"
#include "libavutil/pixdesc.h"
}
void InitFFmpegLib();
enum AVCodecID GetCodecID(const char *codecName);
enum AVSampleFormat GetSampleFormat(enum AVCodecID codec_id);
unsigned int GetCodecTag(const AVCodecTag *tags, enum AVCodecID id);
enum AVCodecID GetCodecID(const AVCodecTag *tags, unsigned int tag);
char* GetCodecName(enum AVCodecID codec_id);
int GetBitPerPixel(AVPixelFormat pix_fmt);
#endif
< FFMPEGUtil.cpp >
#include "util.h"
#include "FFMPEGUtil.h"
#include "GlobalEnv.h"
#include "Mutex.h"
#include <ctype.h>
typedef struct AVCodecTag {
enum AVCodecID id;
unsigned int tag;
} AVCodecTag;
static bool isInit = false;
static MUTEX hMutex = PTHREAD_MUTEX_INITIALIZER;
static int lockmgr(void **mtx, enum AVLockOp op)
{
switch(op) {
case AV_LOCK_CREATE:
MUTEX_INIT((MUTEX *)mtx);
if(!*mtx)
return 1;
return 0;
case AV_LOCK_OBTAIN:
return !!MUTEX_LOCK((MUTEX *)mtx);
case AV_LOCK_RELEASE:
return !!MUTEX_UNLOCK((MUTEX *)mtx);
case AV_LOCK_DESTROY:
MUTEX_DESTROY((MUTEX *)mtx);
return 0;
}
return 1;
}
void InitFFmpegLib()
{
MUTEX_LOCK(&hMutex);
if (!isInit) {
av_register_all();
if (av_lockmgr_register(lockmgr)) {
DXPRINTF("Could not initialize lock manager!\n");
exit(1);
}
isInit = true;
}
MUTEX_UNLOCK(&hMutex);
}
enum AVCodecID GetCodecID(const char *codecName)
{
if (!codecName) return AV_CODEC_ID_NONE;
if (strcmp(codecName, "H264") == 0)
return AV_CODEC_ID_H264;
else if (strcmp(codecName, "MP4V-ES") == 0)
return AV_CODEC_ID_MPEG4;
else if (strcmp(codecName, "MPEG4-GENERIC") == 0)
return AV_CODEC_ID_AAC;
else if (strcmp(codecName, "JPEG") == 0)
return AV_CODEC_ID_MJPEG;
else if (strcmp(codecName, "AC3") == 0)
return AV_CODEC_ID_AC3;
else if (strcmp(codecName, "L16") == 0)
return AV_CODEC_ID_PCM_S16BE;
else if (strcmp(codecName, "PCMU") == 0)
return AV_CODEC_ID_PCM_MULAW;
else if (strcmp(codecName, "PCMA") == 0)
return AV_CODEC_ID_PCM_ALAW;
else DXPRINTF("cannot find %s codec id\n", codecName);
return AV_CODEC_ID_NONE;
}
char* GetCodecName(AVCodecID codec_id)
{
char *temp = NULL;
switch (codec_id) {
case AV_CODEC_ID_H264: { temp = "H264"; break; }
case AV_CODEC_ID_MPEG4: { temp = "MP4V-ES"; break; }
case AV_CODEC_ID_AAC: { temp = "MPEG4-GENERIC"; break; }
case AV_CODEC_ID_MJPEG: { temp = "JPEG"; break; }
case AV_CODEC_ID_AC3: { temp = "AC3"; break; }
case AV_CODEC_ID_PCM_S16BE: { temp = "L16"; break; }
case AV_CODEC_ID_PCM_MULAW: { temp = "PCMU"; break; }
case AV_CODEC_ID_PCM_ALAW: { temp = "PCMA"; break; }
}
return strDup(temp);
}
enum AVSampleFormat GetSampleFormat(enum AVCodecID codec_id)
{
if (codec_id == AV_CODEC_ID_PCM_MULAW)
return AV_SAMPLE_FMT_U8;
else if (codec_id == AV_CODEC_ID_AAC)
return AV_SAMPLE_FMT_S16;
else if (codec_id == AV_CODEC_ID_AC3)
return AV_SAMPLE_FMT_S16;
else if (codec_id == AV_CODEC_ID_PCM_S16BE)
return AV_SAMPLE_FMT_S16;
else DXPRINTF("cannot find codec id %d sample format\n", codec_id);
return AV_SAMPLE_FMT_NONE;
}
unsigned int GetCodecTag(const AVCodecTag *tags, enum AVCodecID id)
{
while (tags->id != AV_CODEC_ID_NONE) {
if (tags->id == id)
return tags->tag;
tags++;
}
return 0;
}
static unsigned int avpriv_toupper4(unsigned int x)
{
return toupper(x & 0xFF) +
(toupper((x >> 8) & 0xFF) << 8) +
(toupper((x >> 16) & 0xFF) << 16) +
(toupper((x >> 24) & 0xFF) << 24);
}
enum AVCodecID GetCodecID(const AVCodecTag *tags, unsigned int tag)
{
int i;
for(i=0; tags[i].id != AV_CODEC_ID_NONE;i++) {
if(tag == tags[i].tag)
return tags[i].id;
}
for(i=0; tags[i].id != AV_CODEC_ID_NONE; i++) {
if (avpriv_toupper4(tag) == avpriv_toupper4(tags[i].tag))
return tags[i].id;
}
return AV_CODEC_ID_NONE;
}
int GetBitPerPixel(AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
return av_get_bits_per_pixel(desc);
}
< Decoder.h >
#ifndef __DECODER_H__
#define __DECODER_H__
#ifdef WIN32
#include <windows.h>
#endif
#include "Mutex.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 *codecCtx, 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 *codecCtx, int channel) = 0;
int prepareOpen(enum AVCodecID codecId, AVCodecContext *codecCtx);
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 MUTEX m_hMutex;
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;
MUTEX Decoder::m_hMutex = PTHREAD_MUTEX_INITIALIZER;
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()
{
MUTEX_LOCK(&m_hMutex);
if (!m_bInit) {
InitFFmpegLib();
#ifdef WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
m_nCPU = sysinfo.dwNumberOfProcessors;
#else
int ret = sysconf(_SC_NPROCESSORS_ONLN);
if (ret != -1) m_nCPU = ret;
#endif
DXPRINTF("CPU : %d\n", m_nCPU);
m_bInit = true;
}
MUTEX_UNLOCK(&m_hMutex);
}
int Decoder::open(AVCodecID codecId, int channel)
{
return open(codecId, NULL, channel);
}
int Decoder::open(AVCodecContext *codecCtx, int channel)
{
return open(codecCtx->codec_id, codecCtx, channel);
}
int Decoder::prepareOpen(enum AVCodecID codecId, AVCodecContext *codecCtx)
{
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 (codecCtx) {
m_pCodecCtx = codecCtx;
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_pCodecCtx)
avcodec_close(m_pCodecCtx);
if (m_bCodecCtxAlloc) {
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);
}
< VideoDecoder.h >
#ifndef __VIDEO_DECODER_H__
#define __VIDEO_DECODER_H__
#include "Decoder.h"
class VideoDecoder : public Decoder
{
public:
VideoDecoder();
virtual ~VideoDecoder();
virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame);
protected:
virtual int open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel);
protected:
int writeYUVStream(FILE *fp, unsigned char *buf, int wrap, int xsize, int ysize);
};
#endif
< VideoDecoder.cpp >
#include "VideoDecoder.h"
#include "GlobalEnv.h"
VideoDecoder::VideoDecoder() : Decoder()
{
}
VideoDecoder::~VideoDecoder()
{
}
int VideoDecoder::open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel)
{
int err;
char errbuf[128];
err = prepareOpen(codecId, codecCtx);
if (err < 0) return -1;
m_pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
m_pCodecCtx->thread_type = FF_THREAD_SLICE;
m_pCodecCtx->thread_count = m_nCPU;
if (codecId == CODEC_ID_H264) {
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
}
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;
}
DXPRINTF("video decoder opened %s\n", avcodec_get_name(codecId));
//m_pFile = fopen("c:/video.yuv", "wb");
exit:
return err;
}
int VideoDecoder::decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame)
{
if (!m_pCodecCtx || !inBuf || inLen <= 0) return 0;
int retLen = 0, got_picture;
prepareDecBuffer(inBuf, inLen);
retLen = avcodec_decode_video2(m_pCodecCtx, frame, &got_picture, &m_avPacket);
if (frame->pict_type == AV_PICTURE_TYPE_NONE)
return 0;
if (m_pFile) {
writeYUVStream(m_pFile, frame->data[0], frame->linesize[0], m_pCodecCtx->width, m_pCodecCtx->height);
writeYUVStream(m_pFile, frame->data[1], frame->linesize[0]/2, m_pCodecCtx->width/2, m_pCodecCtx->height/2);
writeYUVStream(m_pFile, frame->data[2], frame->linesize[0]/2, m_pCodecCtx->width/2, m_pCodecCtx->height/2);
}
return retLen;
}
int VideoDecoder::writeYUVStream(FILE *fp, unsigned char *buf, int wrap, int xsize, int ysize)
{
for(int i=0;i<ysize;i++) {
if (fp)
fwrite(buf + i * wrap, 1, xsize, fp);
}
return 0;
}
< AudioDecoder.h >
#ifndef __AUDIO_DECODER_H__
#define __AUDIO_DECODER_H__
#include "Decoder.h"
#include "Resampler.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 const *extradata, unsigned extradatasize);
int sampleRate();
int channels();
uint64_t channelLayout();
protected:
virtual int open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel);
int initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt);
int checkResampler();
protected:
Resampler* m_pResampler;
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_pResampler(NULL), m_pOutBuf(NULL), m_nOutBufSize(0), m_nOutBufMaxSize(0), m_pExtraData(NULL), m_nExtraDataSize(0)
{
}
AudioDecoder::~AudioDecoder()
{
DX_DELETE_OBJECT(m_pResampler);
}
int AudioDecoder::open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel)
{
int err;
char errbuf[128];
err = prepareOpen(codecId, codecCtx);
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);
return err;
}
if (m_pCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16 || m_pCodecCtx->channels > 2) {
m_pResampler = new Resampler();
err = m_pResampler->open(
m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt,
m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16);
if (err < 0) {
DXPRINTF("failed to open resampler %d %d %d %d\n",
m_pCodecCtx->sample_rate,
m_pCodecCtx->channels,
m_pCodecCtx->channel_layout,
AV_SAMPLE_FMT_S16);
return err;
}
}
DXPRINTF("audio decoder opened %s\n", avcodec_get_name(codecId));
#if 0
m_pFile = fopen("audio_decoder.wav", "wb");
#endif
return err;
}
void AudioDecoder::close()
{
Decoder::close();
if (m_pResampler) {
m_pResampler->close();
DX_DELETE_OBJECT(m_pResampler);
}
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);
retLen = avcodec_decode_audio4(m_pCodecCtx, m_pFrame, &got_frame, &m_avPacket);
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 <= 0) {
return -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_pResampler) {
if (m_pResampler->checkResampler(m_pCodecCtx->sample_rate, m_pCodecCtx->channels,
m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt) < 0)
{
m_pResampler->close();
int err = m_pResampler->open(
m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt,
m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16);
if (err < 0) {
DXPRINTF("failed to open resampler %d %d %d %d\n",
m_pCodecCtx->sample_rate,
m_pCodecCtx->channels,
m_pCodecCtx->channel_layout,
AV_SAMPLE_FMT_S16);
return err;
}
}
retLen = m_pResampler->resample(m_pFrame, out_size);
out_size = m_pResampler->outBufIndex();
memcpy(m_pOutBuf, m_pResampler->outBuf(), out_size);
m_pResampler->resetOutBufIndex();
} else {
if (m_pCodecCtx->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 const *extradata, unsigned extradatasize)
{
if (m_pExtraData) {
delete[] m_pExtraData;
m_pExtraData = NULL;
m_nExtraDataSize = 0;
}
if (!extradatasize)
return;
m_pExtraData = new unsigned char[extradatasize];
memset(m_pExtraData, 0, extradatasize);
memcpy(m_pExtraData, extradata, extradatasize);
m_nExtraDataSize = extradatasize;
}
int AudioDecoder::sampleRate()
{
if (m_pCodecCtx) return m_pCodecCtx->sample_rate;
return 0;
}
int AudioDecoder::channels()
{
if (m_pCodecCtx) return m_pCodecCtx->channels;
return 0;
}
uint64_t AudioDecoder::channelLayout()
{
if (m_pCodecCtx) return m_pCodecCtx->channel_layout;
return 0;
}
< 라이브러리 사용 >
Decoder *pVideoDecoder = new VideoDecoder();
Decoder *pAudioDecoder = new AudioDecoder();
...
// 코덱열기
pVideoDecoder->open(AV_CODEC_ID_H264, 0); // 비디오 코덱은 채널 사용X
pAudioDecoder->open(AV_CODEC_ID_AAC, 2);
...
// 디코딩
unsigned char *buf => encoded data buffer
int size => encoded data buffer size
if (pVideoDecoder->decodeFrame(buf, size, pVideoDecoder->frame()) > 0) {
// now pVideoDecoder->frame() has decoded picture
}
...
if (pAudioDecoder->decodeFrame(buf, size, pAudioDecoder->frame()) > 0) {
// now pAudioDecoder->outBuf() has decoded audio pcm data &&
// pAudioDecoder->outBufSize() has decoded audio pcm data size
}
...
// 코덱닫기
pVideoDecoder->close();
pAudioDecoder->close();
댓글 없음:
댓글 쓰기