반응형
- 스트림 : 시간 흐름에 따른 프레임의 연속.
- 프레임 : 장면을 구성하는 이미지 하나하나.
- 패킷 : 스트림속 원본 프레임은 압축되어 있는 상태이며 그 중 일부를 추출한 데이터를 패킷이라고 함.
- 코덱 : 패킷 자체는 압축되어 있어 바로 쓸 수 없는데, 이를 코덱으로 압축 해제하여 프레임을 얻음.
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)
- 압축을 푼 프레임을 요청하여 받음.
반응형
'언어 | Framework > FFMPEG' 카테고리의 다른 글
[FFMPEG] 스트림 정보 받기 (0) | 2022.11.22 |
---|