diff --git a/Makefile b/Makefile index 02587b6..a68b66f 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ ALL_LIBS := $(LIBS) $(SDL_LIBS) $(FFMPEG_LIBS) -lm BUILD_DIR ?= build TARGET = $(BUILD_DIR)/gsplash DUMMY_TARGET = $(BUILD_DIR)/dummy_game -SRC = src/gsplash.c +SRC = src/gsplash.c src/video.c DUMMY_SRC = src/dummy_game.c .PHONY: all clean install uninstall check diff --git a/src/gsplash.c b/src/gsplash.c index 831dacf..3bc31a9 100644 --- a/src/gsplash.c +++ b/src/gsplash.c @@ -11,10 +11,7 @@ #include #include -#include -#include -#include -#include +#include "video.h" static void log_info(const char *fmt, ...) { @@ -58,45 +55,6 @@ static RenderMode parse_render_mode(const char *value) return RENDER_STRETCH; } -typedef struct VideoPlayer -{ - AVFormatContext *format_ctx; - AVCodecContext *codec_ctx; - struct SwsContext *sws_ctx; - AVFrame *frame; - AVFrame *rgba_frame; - uint8_t *rgba_buffer; - int rgba_buffer_size; - int stream_index; - int width; - int height; - int frame_delay_ms; - Uint32 next_frame_tick; - SDL_Texture *texture; -} VideoPlayer; - -static bool has_video_extension(const char *path) -{ - const char *dot = strrchr(path, '.'); - if (!dot || dot == path) - { - return false; - } - - char ext[8]; - size_t i = 0; - dot++; - while (*dot && i < sizeof(ext) - 1) - { - ext[i++] = (char)tolower((unsigned char)*dot++); - } - ext[i] = '\0'; - - return strcmp(ext, "mp4") == 0 || strcmp(ext, "mkv") == 0 || strcmp(ext, "webm") == 0 || - strcmp(ext, "avi") == 0 || strcmp(ext, "mov") == 0 || strcmp(ext, "mpg") == 0 || - strcmp(ext, "mpeg") == 0; -} - static void compute_dest_rect(int src_w, int src_h, int out_w, int out_h, RenderMode mode, SDL_Rect *dst) { dst->x = 0; @@ -127,186 +85,6 @@ static void compute_dest_rect(int src_w, int src_h, int out_w, int out_h, Render } } -static bool init_video_player(VideoPlayer *player, SDL_Renderer *renderer, const char *path) -{ - memset(player, 0, sizeof(*player)); - - if (avformat_open_input(&player->format_ctx, path, NULL, NULL) != 0) - { - return false; - } - - if (avformat_find_stream_info(player->format_ctx, NULL) < 0) - { - return false; - } - - player->stream_index = -1; - for (unsigned int i = 0; i < player->format_ctx->nb_streams; ++i) - { - if (player->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - { - player->stream_index = (int)i; - break; - } - } - - if (player->stream_index < 0) - { - return false; - } - - AVCodecParameters *codecpar = player->format_ctx->streams[player->stream_index]->codecpar; - const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id); - if (!decoder) - { - return false; - } - - player->codec_ctx = avcodec_alloc_context3(decoder); - if (!player->codec_ctx) - { - return false; - } - - if (avcodec_parameters_to_context(player->codec_ctx, codecpar) < 0) - { - return false; - } - - if (avcodec_open2(player->codec_ctx, decoder, NULL) < 0) - { - return false; - } - - player->width = player->codec_ctx->width; - player->height = player->codec_ctx->height; - player->frame = av_frame_alloc(); - player->rgba_frame = av_frame_alloc(); - if (!player->frame || !player->rgba_frame) - { - return false; - } - - player->rgba_buffer_size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, player->width, player->height, 1); - player->rgba_buffer = (uint8_t *)av_malloc(player->rgba_buffer_size); - if (!player->rgba_buffer) - { - return false; - } - - av_image_fill_arrays(player->rgba_frame->data, player->rgba_frame->linesize, player->rgba_buffer, - AV_PIX_FMT_RGBA, player->width, player->height, 1); - - player->sws_ctx = sws_getContext(player->width, player->height, player->codec_ctx->pix_fmt, - player->width, player->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, - NULL, NULL, NULL); - if (!player->sws_ctx) - { - return false; - } - - player->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, - SDL_TEXTUREACCESS_STREAMING, player->width, player->height); - if (!player->texture) - { - return false; - } - - AVRational fr = player->format_ctx->streams[player->stream_index]->avg_frame_rate; - if (fr.num > 0 && fr.den > 0) - { - player->frame_delay_ms = (int)(1000.0 * fr.den / fr.num); - } - else - { - player->frame_delay_ms = 33; - } - - player->next_frame_tick = SDL_GetTicks(); - return true; -} - -static void cleanup_video_player(VideoPlayer *player) -{ - if (player->texture) - { - SDL_DestroyTexture(player->texture); - } - if (player->sws_ctx) - { - sws_freeContext(player->sws_ctx); - } - if (player->rgba_buffer) - { - av_free(player->rgba_buffer); - } - if (player->rgba_frame) - { - av_frame_free(&player->rgba_frame); - } - if (player->frame) - { - av_frame_free(&player->frame); - } - if (player->codec_ctx) - { - avcodec_free_context(&player->codec_ctx); - } - if (player->format_ctx) - { - avformat_close_input(&player->format_ctx); - } - memset(player, 0, sizeof(*player)); -} - -static bool decode_next_frame(VideoPlayer *player) -{ - AVPacket packet; - av_init_packet(&packet); - - while (av_read_frame(player->format_ctx, &packet) >= 0) - { - if (packet.stream_index != player->stream_index) - { - av_packet_unref(&packet); - continue; - } - - if (avcodec_send_packet(player->codec_ctx, &packet) < 0) - { - av_packet_unref(&packet); - continue; - } - av_packet_unref(&packet); - - while (true) - { - int ret = avcodec_receive_frame(player->codec_ctx, player->frame); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) - { - break; - } - if (ret < 0) - { - return false; - } - - sws_scale(player->sws_ctx, (const uint8_t *const *)player->frame->data, - player->frame->linesize, 0, player->height, - player->rgba_frame->data, player->rgba_frame->linesize); - - SDL_UpdateTexture(player->texture, NULL, player->rgba_frame->data[0], - player->rgba_frame->linesize[0]); - return true; - } - } - - av_seek_frame(player->format_ctx, player->stream_index, 0, AVSEEK_FLAG_BACKWARD); - avcodec_flush_buffers(player->codec_ctx); - return false; -} - int main(int argc, char *argv[]) { RenderMode render_mode = RENDER_CENTER; diff --git a/src/video.c b/src/video.c new file mode 100644 index 0000000..42ffd40 --- /dev/null +++ b/src/video.c @@ -0,0 +1,205 @@ +#include "video.h" +#include +#include + +bool has_video_extension(const char *path) +{ + const char *dot = strrchr(path, '.'); + if (!dot || dot == path) + { + return false; + } + + char ext[8]; + size_t i = 0; + dot++; + while (*dot && i < sizeof(ext) - 1) + { + ext[i++] = (char)tolower((unsigned char)*dot++); + } + ext[i] = '\0'; + + return strcmp(ext, "mp4") == 0 || strcmp(ext, "mkv") == 0 || strcmp(ext, "webm") == 0 || + strcmp(ext, "avi") == 0 || strcmp(ext, "mov") == 0 || strcmp(ext, "mpg") == 0 || + strcmp(ext, "mpeg") == 0; +} + +bool init_video_player(VideoPlayer *player, SDL_Renderer *renderer, const char *path) +{ + memset(player, 0, sizeof(*player)); + + if (avformat_open_input(&player->format_ctx, path, NULL, NULL) != 0) + { + return false; + } + + if (avformat_find_stream_info(player->format_ctx, NULL) < 0) + { + return false; + } + + player->stream_index = -1; + for (unsigned int i = 0; i < player->format_ctx->nb_streams; ++i) + { + if (player->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + { + player->stream_index = (int)i; + break; + } + } + + if (player->stream_index < 0) + { + return false; + } + + AVCodecParameters *codecpar = player->format_ctx->streams[player->stream_index]->codecpar; + const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id); + if (!decoder) + { + return false; + } + + player->codec_ctx = avcodec_alloc_context3(decoder); + if (!player->codec_ctx) + { + return false; + } + + if (avcodec_parameters_to_context(player->codec_ctx, codecpar) < 0) + { + return false; + } + + if (avcodec_open2(player->codec_ctx, decoder, NULL) < 0) + { + return false; + } + + player->width = player->codec_ctx->width; + player->height = player->codec_ctx->height; + player->frame = av_frame_alloc(); + player->rgba_frame = av_frame_alloc(); + if (!player->frame || !player->rgba_frame) + { + return false; + } + + player->rgba_buffer_size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, player->width, player->height, 1); + player->rgba_buffer = (uint8_t *)av_malloc(player->rgba_buffer_size); + if (!player->rgba_buffer) + { + return false; + } + + av_image_fill_arrays(player->rgba_frame->data, player->rgba_frame->linesize, player->rgba_buffer, + AV_PIX_FMT_RGBA, player->width, player->height, 1); + + player->sws_ctx = sws_getContext(player->width, player->height, player->codec_ctx->pix_fmt, + player->width, player->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, + NULL, NULL, NULL); + if (!player->sws_ctx) + { + return false; + } + + player->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, player->width, player->height); + if (!player->texture) + { + return false; + } + + AVRational fr = player->format_ctx->streams[player->stream_index]->avg_frame_rate; + if (fr.num > 0 && fr.den > 0) + { + player->frame_delay_ms = (int)(1000.0 * fr.den / fr.num); + } + else + { + player->frame_delay_ms = 33; + } + + player->next_frame_tick = SDL_GetTicks(); + return true; +} + +void cleanup_video_player(VideoPlayer *player) +{ + if (player->texture) + { + SDL_DestroyTexture(player->texture); + } + if (player->sws_ctx) + { + sws_freeContext(player->sws_ctx); + } + if (player->rgba_buffer) + { + av_free(player->rgba_buffer); + } + if (player->rgba_frame) + { + av_frame_free(&player->rgba_frame); + } + if (player->frame) + { + av_frame_free(&player->frame); + } + if (player->codec_ctx) + { + avcodec_free_context(&player->codec_ctx); + } + if (player->format_ctx) + { + avformat_close_input(&player->format_ctx); + } + memset(player, 0, sizeof(*player)); +} + +bool decode_next_frame(VideoPlayer *player) +{ + AVPacket packet; + av_init_packet(&packet); + + while (av_read_frame(player->format_ctx, &packet) >= 0) + { + if (packet.stream_index != player->stream_index) + { + av_packet_unref(&packet); + continue; + } + + if (avcodec_send_packet(player->codec_ctx, &packet) < 0) + { + av_packet_unref(&packet); + continue; + } + av_packet_unref(&packet); + + while (true) + { + int ret = avcodec_receive_frame(player->codec_ctx, player->frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + { + break; + } + if (ret < 0) + { + return false; + } + + sws_scale(player->sws_ctx, (const uint8_t *const *)player->frame->data, + player->frame->linesize, 0, player->height, + player->rgba_frame->data, player->rgba_frame->linesize); + + SDL_UpdateTexture(player->texture, NULL, player->rgba_frame->data[0], + player->rgba_frame->linesize[0]); + return true; + } + } + + av_seek_frame(player->format_ctx, player->stream_index, 0, AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(player->codec_ctx); + return false; +} diff --git a/src/video.h b/src/video.h new file mode 100644 index 0000000..5fd0cf5 --- /dev/null +++ b/src/video.h @@ -0,0 +1,34 @@ +#ifndef VIDEO_H +#define VIDEO_H + +#include +#include + +#include +#include +#include +#include + +typedef struct VideoPlayer +{ + AVFormatContext *format_ctx; + AVCodecContext *codec_ctx; + struct SwsContext *sws_ctx; + AVFrame *frame; + AVFrame *rgba_frame; + uint8_t *rgba_buffer; + int rgba_buffer_size; + int stream_index; + int width; + int height; + int frame_delay_ms; + Uint32 next_frame_tick; + SDL_Texture *texture; +} VideoPlayer; + +bool has_video_extension(const char *path); +bool init_video_player(VideoPlayer *player, SDL_Renderer *renderer, const char *path); +void cleanup_video_player(VideoPlayer *player); +bool decode_next_frame(VideoPlayer *player); + +#endif // VIDEO_H