Created
July 27, 2023 14:25
-
-
Save aneury1/b465b97ffc2ddcc89f1a0b3fa153ebe9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// g++ main.cpp -o video_player -lSDL2 -lavformat -lavcodec -lavutil -lswscale | |
#include <SDL2/SDL.h> | |
#include <SDL2/SDL_image.h> | |
extern "C" { | |
#include <libavformat/avformat.h> | |
#include <libswscale/swscale.h> | |
} | |
int main(int argc, char* argv[]) { | |
if (argc != 2) { | |
std::cout << "Usage: " << argv[0] << " <video_file_path>\n"; | |
return 1; | |
} | |
const char* videoFilePath = argv[1]; | |
// Initialize SDL | |
if (SDL_Init(SDL_INIT_VIDEO) != 0) { | |
std::cerr << "SDL_Init error: " << SDL_GetError() << std::endl; | |
return 1; | |
} | |
// Create SDL window and renderer | |
SDL_Window* window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, | |
640, 480, SDL_WINDOW_SHOWN); | |
if (window == nullptr) { | |
std::cerr << "SDL_CreateWindow error: " << SDL_GetError() << std::endl; | |
SDL_Quit(); | |
return 1; | |
} | |
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); | |
if (renderer == nullptr) { | |
std::cerr << "SDL_CreateRenderer error: " << SDL_GetError() << std::endl; | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
// Initialize FFmpeg | |
av_register_all(); | |
avformat_network_init(); | |
AVFormatContext* formatContext = nullptr; | |
if (avformat_open_input(&formatContext, videoFilePath, nullptr, nullptr) != 0) { | |
std::cerr << "Could not open video file" << std::endl; | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
if (avformat_find_stream_info(formatContext, nullptr) < 0) { | |
std::cerr << "Could not find stream information" << std::endl; | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
// Find the first video stream | |
int videoStreamIndex = -1; | |
for (unsigned int i = 0; i < formatContext->nb_streams; i++) { | |
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { | |
videoStreamIndex = i; | |
break; | |
} | |
} | |
if (videoStreamIndex == -1) { | |
std::cerr << "Could not find video stream" << std::endl; | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
// Get video codec context | |
AVCodecContext* codecContext = avcodec_alloc_context3(nullptr); | |
if (codecContext == nullptr) { | |
std::cerr << "Could not allocate video codec context" << std::endl; | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar); | |
AVCodec* codec = avcodec_find_decoder(codecContext->codec_id); | |
if (codec == nullptr) { | |
std::cerr << "Codec not found" << std::endl; | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
if (avcodec_open2(codecContext, codec, nullptr) < 0) { | |
std::cerr << "Could not open codec" << std::endl; | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
// Allocate video frame and buffer | |
AVFrame* frame = av_frame_alloc(); | |
if (frame == nullptr) { | |
std::cerr << "Could not allocate video frame" << std::endl; | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
uint8_t* buffer = static_cast<uint8_t*>(av_malloc(av_image_get_buffer_size( | |
AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1))); | |
AVFrame* rgbFrame = av_frame_alloc(); | |
if (buffer == nullptr || rgbFrame == nullptr) { | |
std::cerr << "Could not allocate frame buffer" << std::endl; | |
av_free(buffer); | |
av_frame_free(&rgbFrame); | |
av_frame_free(&frame); | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, codecContext->width, | |
codecContext->height, 1); | |
// Initialize SwsContext for color conversion | |
SwsContext* swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, | |
codecContext->width, codecContext->height, AV_PIX_FMT_RGB24, | |
SWS_BILINEAR, nullptr, nullptr, nullptr); | |
if (swsContext == nullptr) { | |
std::cerr << "Could not initialize color conversion context" << std::endl; | |
av_free(buffer); | |
av_frame_free(&rgbFrame); | |
av_frame_free(&frame); | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 1; | |
} | |
// Main loop to read and render video frames | |
AVPacket packet; | |
SDL_Texture* texture = nullptr; | |
SDL_Event event; | |
int quit = 0; | |
while (av_read_frame(formatContext, &packet) >= 0 && !quit) { | |
if (packet.stream_index == videoStreamIndex) { | |
avcodec_send_packet(codecContext, &packet); | |
avcodec_receive_frame(codecContext, frame); | |
// Convert the frame to RGB24 | |
sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, rgbFrame->data, | |
rgbFrame->linesize); | |
// Create the texture if it's not created yet | |
if (texture == nullptr) { | |
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, | |
codecContext->width, codecContext->height); | |
if (texture == nullptr) { | |
std::cerr << "Could not create SDL texture: " << SDL_GetError() << std::endl; | |
quit = 1; | |
} | |
} | |
// Update texture with the new frame data | |
SDL_UpdateTexture(texture, nullptr, rgbFrame->data[0], rgbFrame->linesize[0]); | |
// Clear the screen and render the texture | |
SDL_RenderClear(renderer); | |
SDL_RenderCopy(renderer, texture, nullptr, nullptr); | |
SDL_RenderPresent(renderer); | |
} | |
// Handle quit event | |
while (SDL_PollEvent(&event)) { | |
if (event.type == SDL_QUIT) { | |
quit = 1; | |
} | |
} | |
av_packet_unref(&packet); | |
} | |
// Clean up resources | |
av_free(buffer); | |
av_frame_free(&rgbFrame); | |
av_frame_free(&frame); | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
sws_freeContext(swsContext); | |
if (texture != nullptr) { | |
SDL_DestroyTexture(texture); | |
} | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment