728x90
//Debian GNU/Linux 12 (bookworm)"
//sudo apt update
//sudo apt install libavcodec-dev libavformat-dev libavutil-dev libsdl2-dev libswscale-dev
//g++ -o video_player video_player.cpp -lavformat -lavcodec -lavutil -lswscale -lSDL2 -lX11 -lvdpau -ldrm -lopus -lz -lva -lva-drm -lva-x11 -lvpx -lx264 -lx265 -lfdk-aac -lswresample
#include <iostream>
#include <stdexcept>
#include <chrono>
#include <thread>
#include <SDL2/SDL.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
class FFmpegDecoder {
public:
FFmpegDecoder(const std::string& filename);
~FFmpegDecoder();
AVFrame* decodeFrame();
void seekToStart(); // 비디오 파일 처음으로 이동
int getWidth() const { return codecContext->width; }
int getHeight() const { return codecContext->height; }
double getFrameRate() const;
private:
AVFormatContext* formatContext;
AVCodecContext* codecContext;
AVCodec* codec;
int videoStreamIndex;
AVPacket* packet;
};
FFmpegDecoder::FFmpegDecoder(const std::string& filename)
: formatContext(nullptr), codecContext(nullptr), codec(nullptr), packet(nullptr) {
avformat_network_init();
if (avformat_open_input(&formatContext, filename.c_str(), nullptr, nullptr) != 0) {
throw std::runtime_error("Failed to open video file");
}
if (avformat_find_stream_info(formatContext, nullptr) < 0) {
throw std::runtime_error("Failed to get stream info");
}
videoStreamIndex = -1;
for (int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
throw std::runtime_error("Failed to find video stream");
}
AVCodecParameters* codecParams = formatContext->streams[videoStreamIndex]->codecpar;
codec = const_cast<AVCodec*>(avcodec_find_decoder(codecParams->codec_id));
if (!codec) {
throw std::runtime_error("Unsupported codec");
}
codecContext = avcodec_alloc_context3(codec);
if (avcodec_parameters_to_context(codecContext, codecParams) < 0) {
throw std::runtime_error("Failed to initialize codec context");
}
if (avcodec_open2(codecContext, codec, nullptr) < 0) {
throw std::runtime_error("Failed to open codec");
}
packet = av_packet_alloc();
}
FFmpegDecoder::~FFmpegDecoder() {
av_packet_free(&packet);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
avformat_network_deinit();
}
double FFmpegDecoder::getFrameRate() const {
AVRational frameRate = formatContext->streams[videoStreamIndex]->avg_frame_rate;
return av_q2d(frameRate); // Returns frames per second (FPS)
}
AVFrame* FFmpegDecoder::decodeFrame() {
AVFrame* frame = av_frame_alloc();
while (av_read_frame(formatContext, packet) >= 0) {
if (packet->stream_index == videoStreamIndex) {
if (avcodec_send_packet(codecContext, packet) == 0) {
if (avcodec_receive_frame(codecContext, frame) == 0) {
av_packet_unref(packet);
return frame;
}
}
}
av_packet_unref(packet);
}
av_frame_free(&frame);
return nullptr;
}
void FFmpegDecoder::seekToStart() {
av_seek_frame(formatContext, videoStreamIndex, 0, AVSEEK_FLAG_BACKWARD); // 비디오 파일의 처음으로 이동
avcodec_flush_buffers(codecContext); // 디코더 버퍼를 비웁니다.
}
class VideoPlayer {
public:
VideoPlayer(int width, int height);
~VideoPlayer();
void displayFrame(AVFrame* frame);
void handleInput(bool& isPaused, bool& quit);
private:
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* texture;
};
VideoPlayer::VideoPlayer(int width, int height) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("Failed to initialize SDL");
}
window = SDL_CreateWindow(
"FFmpeg Video Player",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
width, height, 0
);
if (!window) {
throw std::runtime_error("Failed to create SDL window");
}
renderer = SDL_CreateRenderer(window, -1, 0);
texture = SDL_CreateTexture(
renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, width, height
);
}
VideoPlayer::~VideoPlayer() {
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
void VideoPlayer::displayFrame(AVFrame* frame) {
SDL_UpdateYUVTexture(
texture, nullptr,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]
);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
void VideoPlayer::handleInput(bool& isPaused, bool& quit) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
} else if (event.type == SDL_KEYDOWN) {
if (event.key.keysym.sym == SDLK_SPACE) {
isPaused = !isPaused; // Toggle pause on space bar
}
}
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <video file>" << std::endl;
return -1;
}
try {
FFmpegDecoder decoder(argv[1]);
VideoPlayer player(decoder.getWidth(), decoder.getHeight());
double frameRate = decoder.getFrameRate();
int frameDelay = static_cast<int>(1000.0 / frameRate); // Calculate frame delay in milliseconds
bool quit = false;
bool isPaused = false;
AVFrame* frame = nullptr;
while (!quit) {
player.handleInput(isPaused, quit);
if (!isPaused) {
frame = decoder.decodeFrame();
if (frame) {
player.displayFrame(frame);
av_frame_free(&frame);
std::this_thread::sleep_for(std::chrono::milliseconds(frameDelay)); // Delay to match the frame rate
} else {
decoder.seekToStart(); // 비디오 끝에 도달하면 처음으로 돌아감
}
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Small delay when paused
}
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return -1;
}
return 0;
}
728x90
'MFC(Window Programming)' 카테고리의 다른 글
| [linux]ffmpeg mp4 player (0) | 2024.10.23 |
|---|---|
| [linux]C++에서 fork와 exec를 사용해 명령 실행하기 (0) | 2024.10.11 |
| Mutex, Semaphore, Critical Section 동기화 메커니즘 성능 비교 (0) | 2024.09.27 |
| ifstream, ostream, stringstream, std::getline (1) | 2024.09.25 |
| C++ 정규식과 Raw String Literal: 매칭과 사용법 (1) | 2024.09.24 |