본문 바로가기

MFC(Window Programming)

[linux]ffmpeg mp4 player

728x90
#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();
    int getWidth() const { return codecContext->width; }
    int getHeight() const { return codecContext->height; }
    AVPixelFormat getPixelFormat() const { return codecContext->pix_fmt; }
    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;
}

class VideoPlayer {
public:
    VideoPlayer(int width, int height);
    ~VideoPlayer();
   
    void displayFrame(AVFrame* frame);
   
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);
}

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

        AVFrame* frame = nullptr;
        while ((frame = decoder.decodeFrame()) != nullptr) {
            player.displayFrame(frame);
            av_frame_free(&frame);
            std::this_thread::sleep_for(std::chrono::milliseconds(frameDelay));  // Delay to match the frame rate
        }

    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return -1;
    }

    return 0;
}

//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*/
728x90