extern "C" {
#include "libavcodec\avcodec.h"
}
#define VIDEO_CODEC_H264 0
#define VIDEO_CODEC_MPEG4 1
// 코덱관련 변수들 선언
class VideoDecoder {
AVCodec *m_pCodec;
AVCodecContext *m_pCodecContext;
AVFrame *m_pPicture;
AVPacket m_avPacket;
...
};
static HANDLE hMutex;
static int g_numCPU = 1;
< 디코더 초기화 - initialize decoder >
void VideoDecoder::initCodec()
{
static int init = 0; // 코덱 초기화는 프로그램 돌때 한번만 해주면됨
if (init == 0) {
SYSTEM_INFO sysinfo;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
g_numCPU = sysinfo.dwNumberOfProcessors; // cpu 코어 갯수
#ifdef FFMPEG_0_8
avcodec_init();
#endif
#endif
avcodec_register_all();
hMutex = CreateMutex(NULL, FALSE, NULL);
hMutex = CreateMutex(NULL, FALSE, NULL);
init = 1;
}
}
}
< 디코더 열기 - open decoder >
int VideoDecoder::openDecoder(int codec)
{
WaitForSingleObject(hMutex, INFINITE); // 뮤텍스 처리 (thread-safe 하지않음)
av_init_packet(&m_avPacket);
if (codec == VIDEO_CODEC_H264)
m_pCodec = avcodec_find_decoder(CODEC_ID_H264); else if (codec == VIDEO_CODEC_MPEG4)
m_pCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
else {
ReleaseMutex(hMutex);
return -1;
}
#ifdef FFMPEG_0_8
m_pCodec = avcodec_find_decoder(CODEC_ID_H264); else if (codec == VIDEO_CODEC_MPEG4)
m_pCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
else {
ReleaseMutex(hMutex);
return -1;
}
#ifdef FFMPEG_0_8
m_pCodecContext = avcodec_alloc_context();
#else
m_pCodecContext = avcodec_alloc_context3(m_pCodec);
#endif
m_pCodecContext = avcodec_alloc_context3(m_pCodec);
#endif
//m_pCodecContext->thread_type = FF_THREAD_FRAME;
m_pCodecContext->thread_type = FF_THREAD_SLICE;
m_pCodecContext->thread_count = g_numCPU;
m_pCodecContext->thread_count = g_numCPU;
m_pPicture = avcodec_alloc_frame();
if (codec == VIDEO_CODEC_H264) // MPEG4('mp4v') 일때 영상깨짐
{
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; }
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; }
#ifdef FFMPEG_0_8
if (avcodec_open(m_pCodecContext, m_pCodec) < 0) {
#else
if (avcodec_open2(m_pCodecContext, m_pCodec, NULL) < 0)
#endif
ReleaseMutex(hMutex);
#else
if (avcodec_open2(m_pCodecContext, m_pCodec, NULL) < 0)
#endif
ReleaseMutex(hMutex);
return -1;
}
ReleaseMutex(hMutex);
return 0;
}
< 디코더 닫기 - close decoder >
int VideoDecoder::closeDecoder()
{
WaitForSingleObject(hMutex, INFINITE);
if (m_pCodecContext)
avcodec_close(m_pCodecContext);
av_free(m_pCodecContext);
av_free(m_pPicture);
ReleaseMutex(hMutex);
return 0;
}
< 디코더 사용 - decode >
int VideoDecoder::Decode(char *inBuf, int inLen)
{
int retLen, got_picture;
char *buf = (char *)malloc(inLen+FF_INPUT_BUFFER_PADDING_SIZE);
memset(&buf[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(buf, inBuf, inLen);
m_avPacket.data = (uint8_t *)buf;
m_avPacket.size = inLen;
char *buf = (char *)malloc(inLen+FF_INPUT_BUFFER_PADDING_SIZE);
memset(&buf[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(buf, inBuf, inLen);
m_avPacket.data = (uint8_t *)buf;
m_avPacket.size = inLen;
retLen = avcodec_decode_video2(m_pCodecContext, m_pPicture, &got_picture, &m_avPacket); // 디코딩 => m_pPicture->data[0], data[1], data[2] 이 각각 YCbCr 값을 가짐 (YUV420)
if (retLen < 0) {
goto exit;
}
if (!got_picture) { // 한 프레임을 만들지 못함 -> 한 프레임을 잘라서 주지 못할경우 여기에 대한 처리가 필요
goto exit;
}
...
exit:
if (buf)
free(buf);
return retLen;
exit:
if (buf)
free(buf);
return retLen;
}
사용법은 클래스 생성후 initCodec()(프로그램 실행처음 한번만 하면됨) -> openDecoder() -> Decode() -> closeDecoder() 해주면 된다.
ffmpeg 디코더 라이브러리 => ffmpeg 디코더 참고소스
ffmpeg 디코더 라이브러리 => ffmpeg 디코더 참고소스
인코더 사용법도 알려주시면 안되나요? 굉장히 도움되네요!
답글삭제인코더는 ffmpeg 소스엔 없구요 보통 x264 소스를 이용해서 인코딩합니다. x264 소스에 예제가 있을거에요
답글삭제소스를 보고 참조 잘 하였습니다. 하지만 avcodec_decode_video2()후 결과값이 0값이 리턴되어 더 이상 진도가 나가지 않습니다. 어떻게 해결해야될까요?
답글삭제저는 IP카메라로부터 한프레임씩 인코딩될 데이터를 디코딩합니다...ffmpeg 52버전은 잘되는데 왜 53버전은 잘 안될까요? 예제를 똑같이 사용했는데...
답변 부탁드립니다.
avcodec_decode_video2 리턴값이 0 이면 한 프레임을 디코딩하지 못했다는 의미인데 got_picture 값이 0 일거 같네요. got_picture 가 0 이면 인코딩된 한 프레임을 제대로 주지못했다는 의미입니다. 인코딩된 버퍼가 제대로된 한 프레임인지 확인해보세요. 인코딩된 버퍼를 그데로 파일로 저장해서 Elecard StreamEye 로 재생해보거나 Elecard Stream Analyzer 로 분석해보세요.
답글삭제AAC 엔코딩에 PCM 샘플을 960을 넣었는데 성공하고 나서 디코딩해보면 1024가 나오고 뒤에 64샘플은 공백이 끼워져 있습니다. 왜그런지 혹시 짚이는데 있으신지요?
답글삭제