ffmpeg 소스에는 다양한 예제가 있다. 이 예제를 사용하면 간단히 프로그램을 만들 수 있고 ffmpeg / ffplay 소스 코드를 사용하여 입맛에 맞는 좋은 프로그램도 만들 수 있다. 물론, 노력과 시간이 필요하겠지만.
어쨌든, 이번에는 ffmpeg 내에 있는 decode_simple.c / decode_simple.h를 사용하여 동영상의 프레임을 저장하는 예제를 간단히 만들어 봤다. 프레임을 뽑아 YUV로 저장하는 예제인데, 아래와 같이 YUV Viewer로 확인 가능하다.
2023.02.15 - [놀기/잡스러운 것] - YUV Viewer
YUV 포맷은 이 자체만으로도 분량이 많으니 위키와 인터넷상의 여러 게시물을 참조하시길
아래는 메인 함수다. 비디오 코덱에 따라 안 되는 경우가 있는데, 그냥 간단히 만드는 예제라 문제없이 동작하는 동영상을 넣어 테스트했다.
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include <signal.h>
#include <inttypes.h>
#include "decode_simple.h"
extern "C" {
#include "libavutil/video_enc_params.h"
#include "libavcodec/avcodec.h"
}
#pragma comment(lib, "Secur32.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "Mfuuid.lib")
#pragma comment(lib, "Strmiids.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "libavcodec.a")
#pragma comment(lib, "libavdevice.a")
#pragma comment(lib, "libavfilter.a")
#pragma comment(lib, "libavformat.a")
#pragma comment(lib, "libavutil.a")
#pragma comment(lib, "libpostproc.a")
#pragma comment(lib, "libswresample.a")
#pragma comment(lib, "libswscale.a")
// AVFrame으로 전달된 프레임을 YUV 포맷으로 저장한다.
void save_vframe(AVFrame *avFrame, int frameNumber)
{
char filename[32];
sprintf(filename, "frame%d.yuv", frameNumber);
FILE *fDump = fopen(filename, "ab");
uint32_t pitchY = avFrame->linesize[0];
uint32_t pitchU = avFrame->linesize[1];
uint32_t pitchV = avFrame->linesize[2];
uint8_t *avY = avFrame->data[0];
uint8_t *avU = avFrame->data[1];
uint8_t *avV = avFrame->data[2];
for (int32_t i = 0; i < avFrame->height; i++) {
fwrite(avY, avFrame->width, 1, fDump);
avY += pitchY;
}
for (int32_t i = 0; i < avFrame->height/2; i++) {
fwrite(avU, avFrame->width/2, 1, fDump);
avU += pitchU;
}
for (int32_t i = 0; i < avFrame->height/2; i++) {
fwrite(avV, avFrame->width/2, 1, fDump);
avV += pitchV;
}
fclose(fDump);
}
// ds_run()이 호출되면 디코딩이 시작되고 아래 함수가 호출되어 후처리를 할 수 있다.
// 프레임 저장이 목적이므로 간단히 저장하는 함수만 호출한다.
static int process_frame(DecodeContext *dc, AVFrame *frame)
{
static bool _show_info = true;
if ((NULL == dc) || (NULL == frame)) {
return -1;
}
if (true == _show_info) {
fprintf(stderr, "%d x %d\n", dc->frame->width, dc->frame->height);
_show_info = false;
}
save_vframe(dc->frame, dc->decoder->frame_number);
return 0;
}
int run_main(void)
{
DecodeContext dc;
unsigned int stream_idx, max_frames;
const char *filename;
int ret = 0;
filename = "E:/_001/test.avi";
stream_idx = 0;
max_frames = 50; // 앞에서 부터 50개의 frame을 읽어 저장
ret = ds_open(&dc, filename, stream_idx);
if (ret < 0) {
goto finish;
}
dc.process_frame = process_frame;
dc.max_frames = max_frames;
ret = ds_run(&dc);
finish:
ds_free(&dc);
return ret;
return 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer::singleShot(0,[](){
run_main();
QCoreApplication::exit(0);
});
return a.exec();
}
'놀기 > 기초 공부' 카테고리의 다른 글
[Shell Script] 시간 설정하기 (grep, head, regex) (1) | 2023.05.27 |
---|---|
[FFMPEG] Error Codes (-1094995529) (0) | 2023.02.14 |
libcurl을 사용하여 Web Page 받기 (0) | 2023.02.13 |
블록체인(Blockchain) 관련 eBook (pdf) (0) | 2021.11.12 |
[javascript] 개행 없이 출력하기 (0) | 2021.08.30 |
댓글