언어 | Framework/FFMPEG

[FFMPEG] 압축 해제, 디코딩

woongs_93 2022. 11. 23. 10:03
반응형

  • 스트림 : 시간 흐름에 따른 프레임의 연속.
  • 프레임 : 장면을 구성하는 이미지 하나하나.
  • 패킷 : 스트림속 원본 프레임은 압축되어 있는 상태이며 그 중 일부를 추출한 데이터를 패킷이라고 함.
  • 코덱 : 패킷 자체는 압축되어 있어 바로 쓸 수 없는데, 이를 코덱으로 압축 해제하여 프레임을 얻음.

 

AVFrame

압축을 푼 데이터가 들어 있는 프레임.

포맷 설명
int format 프레임의 색상 포맷
int width 영상의 폭
int height 영상의 높이
uint8_t* data[8] 이미지 플레인의 포인터 배열
int linesize[8] 각 플레인의 한줄 길이
extended_data 프레임, 채널 데이터. 비디오는 data를 가리킨다.

평면 오디오인 경우 채널별로 데이터를 가지고
 linesize[0]에 각 채널 버퍼의 크기가 들어 있다.

팩 오디오는 하나의 데이터 포인터만 있다. data가 수용할 수 있는 것보다 더 많은 채널이 있을 때
이 멤버를 사용한다
.
pts 타임 스탬프, time_base 단위

 

 

Decoding

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
}

#include <stdio.h>
#include <Windows.h>
#include <conio.h>


AVFormatContext* fmtCtx;
int vidx = -1;
AVStream* vStream;
AVCodecParameters* vPara;
AVCodecContext* vCtx;


void arDump(void* array, int length)
{
    for (int i = 0; i < length; i++) {
        printf("%02X ", *((unsigned char*)array + i));
    }

    return;
}


int main(void) {
    AVPacket packet = { 0, };
    AVFrame vFrame = { 0, };

    int ret = avformat_open_input(&fmtCtx, "D:\\project2\\FFMPEG_TEST\\test_org.avi", NULL, NULL);
    if (ret != 0) { return -1; }
    avformat_find_stream_info(fmtCtx, NULL);

    vidx = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

    if (vidx >= 0) {
        vStream = fmtCtx->streams[vidx];
        vPara = vStream->codecpar;
        const AVCodec* vCodec = avcodec_find_decoder(vPara->codec_id);
        vCtx = avcodec_alloc_context3(vCodec);
        avcodec_parameters_to_context(vCtx, vPara);
        avcodec_open2(vCtx, vCodec, NULL);
    }


    int vcount = 0;

    // 패킷 읽음
    while (av_read_frame(fmtCtx, &packet) == 0) {
        if (packet.stream_index == vidx) {
            // 압축 풀어 줘
            ret = avcodec_send_packet(vCtx, &packet);
            if (ret != 0) { continue; }

            for (;;) {
                // 압축 푼거 내 놔
                ret = avcodec_receive_frame(vCtx, &vFrame);
                if (ret == AVERROR(EAGAIN)) 
                    break;

                if (vcount == 0) {
                    printf("Video format : %d(%d x %d).\n",
                        vFrame.format, vFrame.width, vFrame.height);
                }

                printf("V%d => ppts:%I64d, pts:%I64d, dts:%I64d, best=%I64d              ",
                    vcount++, packet.pts, vFrame.pts, vFrame.pkt_dts, vFrame.best_effort_timestamp);

                for (int i = 0; i < 3; i++) {
                    printf("%d ", vFrame.linesize[i]);
                }

                arDump(vFrame.data[0], 4);
                arDump(vFrame.data[1], 2);
                arDump(vFrame.data[2], 2);
            }
        }

        av_packet_unref(&packet);
        printf("\n");
    }

    // 메모리 해제
    av_frame_unref(&vFrame);
    avcodec_free_context(&vCtx);
    avformat_close_input(&fmtCtx);
}

 

 

int av_read_frame (AVFormatContext *s, AVPacket *pkt)

- 동영상 파일에서 패킷을 읽음

- 포맷 컨텍스트 핸들과 패킷 구조체를 주면 패킷을 순서대로 읽어 채워 준다.

 

int avcodec_send_packet (AVCodecContext *avctx, const AVPacket *avpkt)

- 디코더에게 압축 해제를 요청.

- 패킷의 정보를 받아 저장해 두고 일부 정보를 복사하여 디코딩 준비를 한다.

 

int avcodec_receive_frame(AVCodecContect *avctx, AVFrame *frame)

- 압축을 푼 프레임을 요청하여 받음.

 

 

 

반응형