이글은 내가 공부하려고, 내맘대로 번역한 글.
원문이 2004년에 작성된거라, 현재 버젼과 차이가 있을거임.
틀린부분 지적해 주시면 감사.

//------------------------------------------------------------------------------
// 설명
//------------------------------------------------------------------------------
많은 동영상 파일 포맷은,
그안의 오디오와 비디오 데이타를 인코딩하는데 어떤 코덱을 사용했는지
기술하지 않는다.
그저 하나의 동영상파일안에, 오디오와 비디오 스트림이 어떻게 조합되어 있는지만을 정의한다.
가끔식 동영상파일을 열었을때, 소리만 들리고, 영상이 안보이는것은 이러한 이유에서다
(올바른 영상 코덱이 너의 컴퓨터에 설치되지 않았지 때문에)
libavformat : 동영상파일을 파싱하고, 그안에 있는 스트림들을 분리하는 역활
libavcodec : 오디오, 비디오 스트림의 데이타를 디코딩 하는 역활


//------------------------------------------------------------------------------
// 비디오 파일 열기
//------------------------------------------------------------------------------
// libavformat, libavcode 초기화
// ffmpeg 라이브러리의 모든 사용가능한 파일포맷과 코덱을 등록한다.
// 그래서, 파일이 오픈되었을때, 적절한 파일포맷과 코덱이 자동으로 사용된다.
// 한번만 호출해야 한다.
// 한두개의 특정한 포맷과 코덱만 등록하는것도 가능하다 (일반적으로 그럴필요는 없지만)
//------------------------------------------------------------------------------
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

av_register_all();

//------------------------------------------------------------------------------
// 비디오파일을 연다
// 뒤의 매개변수 3개는 파일포맷, 버퍼사이즈, 포맷파라메터이다.
// (NULL, 0 으로 셋팅해서, libavformat 이 자동으로 포맷을 탐지하고, 기본퍼버사이즈를 사용하게 함)
//------------------------------------------------------------------------------
AVFormatContext* pFormatCtx;
const char* filename = "myvideo.mpg";
if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0)
error; // can not open file

//------------------------------------------------------------------------------
// 스트림정보를 가져온다.
// AVFormatContext 의 스트림필드를 올바른 정보로 채운다.
//------------------------------------------------------------------------------
if(av_find_stream_info(pFormatCtx) < 0)
error; // can not find stream infomation

dump_format(pFormatCtx, 0, filename, false); // 디버깅 정보 출력, (표준 에러 장치로)


//------------------------------------------------------------------------------
// 첫번째 비디오 스트림을 찾고
// 그 비디오 스트림의 포인터를 저장한다.
// (오디오 스트림은 여기서 설명안함)
//------------------------------------------------------------------------------
int i, videoStream;
AVCodeContext* pCodecCtx;

videoStream = -1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO)
{
videoStream = i;
break;
}

if(videoStream == -1) error; // can not find video stream

pCodecCtx = &pFormatCtx->streams[videoStream]->codec;


//------------------------------------------------------------------------------
// OK, 자 이제 우리는 소위말하는 codec context 포인터를 가지게 되었다. (비디오 스트림을 위한)
// 그러나, 아직 실제 코덱을 찾고, 그 코덱을 열어야만 한다
//------------------------------------------------------------------------------
AVCodec* pCodec;

// 비디오스트림을 위한 디코더 찾기
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL) error; // codec not found

// Inform the codec that we can handle truncated bitstreams -- i.e.,
// bitstreams where frame boundaries can fall in the middle of packets
// 코덱을 알아낸다. 우리가 다룰수 있는 끝이 잘린 비트스트림들
// 패킷의 중간에 떨어진 
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;

// 코덱열기
if(avcodec_open(pCodecCtx, pCodec) < 0)
error; // can not open codec

//------------------------------------------------------------------------------
// truncated bitstreams : 어떤 순간에 비디오스트림의 데이타는 패킷으로 갈라진다.
// 비디오프레임마다 데이타의 양이 다를수 있기 때문에,
// 두개의 비디오 사이의 경계를 프레임경계와 일치시키는게 필요하다.
// Here, we're telling the codec that we can handle this situation
//------------------------------------------------------------------------------



//------------------------------------------------------------------------------
// AVCodecContext structure 안에 저장된 중요한 정보는 비디오의 frame rate 이다.
// 정수형이 아닌 frame rate를 허용하기 위해서(like NTSC's 29.97 fps) 분수로 저장되어 있다.
// pCodecCtx->frame_rate : 분자
// pCodecCtx->frame_rate_base : 분모
// 다른 비디오 파일들을 가지고, ffmpeg library 를 테스트하는동안,
// 어떤 코덱들은 이 값들을 부정확하게 채운다 (특히 asf)
// frame_rate_base 가 1000 대신 1이 들어가 있다
// 다음 꼼수로 쓴다.
// 나중에 이 버그가 수정되더라도, 이 꼼수는 그냥둬도 될것이다.
// 비디오 frame rate 는 1000 fps 보다 크지는 않은거 같다.
//------------------------------------------------------------------------------
if(pCodecCtx->frame_rate > 1000 && pCodecCtx->frame_rate_base == 1)
pCodecCtx->frame_rate_base = 1000;


//------------------------------------------------------------------------------
// 하나더 할일, 이미지들로 디코드된 video frame 을 저장하기 위해서 메모리 할당한다
//  다 됐다. 이제 영상을 디코딩하자.
//------------------------------------------------------------------------------
AVFrame* pFrame;
pFrame = avcodec_alloc_frame();


//------------------------------------------------------------------------------
// 비디오프레임들 디코딩하기
//------------------------------------------------------------------------------
// 내가 언급했듯이, 동영상파일은 여러개의 오디와와 비디오 스트림을 포함하고 있다.
// 각 스트림들은 특정 크기의 패킷으로 갈라진다.
// 우리가 해야할일은 libavformat 을 이용해서, 이 패킷들을 하나씩 읽고, 
// 우리가 관심있는 비디오스트림이 아닌것은 걸러낸는것이다.
// 그리고, 디코딩하기위해서 libavcodec에 넘겨준다.
// 이 과정에서, 두 frame 사이의 경계가 패킷중간에 있을수도 있다는 사실에 주의해야 한다.
// 복잡하게 들리는가? 다행히도, 간단하게 다음 비디오프레임을 리턴해주는 다음 함수로 인해서
// 이 모든 처리과정에 고민할필요 없다
//
// 주석 : 아래의 함수는 나중에 다시 잘 보자.
//------------------------------------------------------------------------------
bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, 
                  int videoStream, AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining=0;
    static uint8_t  *rawData;
    static bool     fFirstTime=true;
    int             bytesDecoded;
    int             frameFinished;

    // First time we're called, set packet.data to NULL to indicate it
    // doesn't have to be freed
    if(fFirstTime)
    {
        fFirstTime=false;
        packet.data=NULL;
    }

    // Decode packets until we have decoded a complete frame
    while(true)
    {
        // Work on the current packet until we have decoded all of it
        while(bytesRemaining > 0)
        {
            // Decode the next chunk of data
            bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,
                &frameFinished, rawData, bytesRemaining);

            // Was there an error?
            if(bytesDecoded < 0)
            {
                fprintf(stderr, "Error while decoding frame\n");
                return false;
            }

            bytesRemaining-=bytesDecoded;
            rawData+=bytesDecoded;

            // Did we finish the current frame? Then we can return
            if(frameFinished)
                return true;
        }

        // Read the next packet, skipping all packets that aren't for this
        // stream
        do
        {
            // Free old packet
            if(packet.data!=NULL)
                av_free_packet(&packet);

            // Read new packet
            if(av_read_packet(pFormatCtx, &packet)<0)
                goto loop_exit;
        } while(packet.stream_index!=videoStream);

        bytesRemaining=packet.size;
        rawData=packet.data;
    }

loop_exit:

    // Decode the rest of the last frame
    bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
        rawData, bytesRemaining);

    // Free last packet
    if(packet.data!=NULL)
        av_free_packet(&packet);

    return frameFinished!=0;
}

//------------------------------------------------------------------------------
// 이제, 우리가 해야할일은 루프안에서,
// GetNextFrame() 함수를 false 를 리턴할때까지 호출하는것이다.
// 한가지 더 신경써야 할것은, 대부분의 코덱은 YUV420포맷 이미지를 리턴한다.
// (one luminance and two chrominance channels,
// with the chrominance channels samples at half the spatial resolution of the luminance channel)
// 원하면 이것을 RGB 로 변환할수 있다
// 너가 단지 비디오데이타를 display 만 시킬거면, 변환할 필요없다
// 다행히, libavcodec 은 YUV와 RGB를 변환해주는 img_convert라는 루틴을 제동해준다.
// (다른 다양한 이미지포맷도 가능)
//------------------------------------------------------------------------------
while(GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame))
{
    img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, 
        pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

    // Process the video frame (save to disk etc.)
    DoSomethingWithTheImage(pFrameRGB);
}


//------------------------------------------------------------------------------
// AVFrame* 타입의 pFrameRGB (rgb이미지)는 이렇게 할당한다.
//------------------------------------------------------------------------------
AVFrame *pFrameRGB;
int     numBytes;
uint8_t *buffer;

// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
    handle_error();

// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
    pCodecCtx->height);
buffer=new uint8_t[numBytes];

// Assign appropriate parts of buffer to image planes in pFrameRGB
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
    pCodecCtx->width, pCodecCtx->height);


//------------------------------------------------------------------------------
// Cleaning up
//------------------------------------------------------------------------------

// Free the RGB image
delete [] buffer;
av_free(pFrameRGB);

// Free the YUV frame
av_free(pFrame);

// Close the codec
avcodec_close(pCodecCtx);

// Close the video file
av_close_input_file(pFormatCtx);


반응형

'잡다한 자료' 카테고리의 다른 글

ffmpeg. ffmpeg visual studio 2010  (0) 2014.07.04
ffmpeg. Using libavformat and libavcodec: An Update 번역  (0) 2014.07.04
jquery clone  (0) 2014.07.04
MySQL 함수 생성후, 실행권한 부여  (0) 2014.07.04
MySQL data import  (0) 2014.07.04
Posted by 돌비
,