Skip to content
This repository has been archived by the owner on Apr 20, 2024. It is now read-only.

Commit

Permalink
implement first draft of video module functions
Browse files Browse the repository at this point in the history
  • Loading branch information
MGTheTrain committed Apr 18, 2024
1 parent 5e8c04f commit 0bf3b67
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 2 deletions.
28 changes: 27 additions & 1 deletion modules/video/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
TBD
# The MIT License
#
# Copyright (c) 2024 MGTheTrain
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

if(BUILD_LIB)
add_subdirectory(src)
endif()

if(BUILD_TEST)
add_subdirectory(test)
endif()
Empty file removed modules/video/include/.gitignore
Empty file.
51 changes: 51 additions & 0 deletions modules/video/include/video.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// The MIT License
//
// Copyright (c) 2024 MGTheTrain
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <SDL2/SDL.h>
#include <libavformat/avformat.h>
#include <iostream>

#ifdef __cplusplus
extern "C" {
#endif
static AVFormatContext* formatContext = nullptr;
static AVCodecParameters* codecParameters = nullptr;
static AVCodec* codec = nullptr;
static AVCodecContext* codecContext = nullptr;
static SDL_Window* window = nullptr;
static SDL_Renderer* renderer = nullptr;
static SDL_Texture* texture = nullptr;
static struct SwsContext* swsContext = nullptr;
static AVFrame* frame = nullptr;
static int videoStream = -1;
static AVRational timeBase;
static double frameDuration;

bool initVideoPlayer();
bool loadVideo(const char* filename);
void playVideo();
void closeVideoPlayer();
#ifdef __cplusplus
}
#endif
Empty file removed modules/video/src/.gitignore
Empty file.
46 changes: 45 additions & 1 deletion modules/video/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
TBD
# The MIT License
#
# Copyright (c) 2024 MGTheTrain
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

cmake_minimum_required(VERSION 3.10)

set(TARGET video)
project(${TARGET})

find_package(portaudio CONFIG REQUIRED)
find_package(SndFile CONFIG REQUIRED)

include_directories(${PROJECT_SOURCE_DIR}/../include)

set(VIDEO_SRC
video.cpp
)

add_library(${TARGET} STATIC ${VIDEO_SRC})
add_library(${TARGET}_wrapper SHARED ${VIDEO_SRC}) # required for bindings

if(APPLE)
target_link_libraries(${TARGET} SndFile::sndfile portaudio_static)
target_link_libraries(${TARGET}_wrapper SndFile::sndfile portaudio_static)
else()
target_link_libraries(${TARGET} SndFile::sndfile portaudio)
target_link_libraries(${TARGET}_wrapper SndFile::sndfile portaudio)
endif()
install(TARGETS ${TARGET})
156 changes: 156 additions & 0 deletions modules/video/src/video.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// The MIT License
//
// Copyright (c) 2024 MGTheTrain
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <video.h>

bool initVideoPlayer() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "SDL initialization failed: " << SDL_GetError() << "\n";
return false;
}

av_register_all();
if (avformat_network_init() < 0) {
std::cerr << "Failed to initialize FFmpeg network\n";
return false;
}

return true;
}

bool loadVideo(const char* filename) {
if (avformat_open_input(&formatContext, filename, NULL, NULL) != 0) {
std::cerr << "Couldn't open video file\n";
return false;
}

if (avformat_find_stream_info(formatContext, NULL) < 0) {
std::cerr << "Couldn't find stream information\n";
return false;
}

videoStream = -1;
for (int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}

if (videoStream == -1) {
std::cerr << "Couldn't find a video stream\n";
return false;
}

codecParameters = formatContext->streams[videoStream]->codecpar;
codec = avcodec_find_decoder(codecParameters->codec_id);
if (!codec) {
std::cerr << "Unsupported codec\n";
return false;
}

codecContext = avcodec_alloc_context3(codec);
if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
std::cerr << "Failed to copy codec parameters to codec context\n";
return false;
}

if (avcodec_open2(codecContext, codec, NULL) < 0) {
std::cerr << "Failed to open codec\n";
return false;
}

window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, codecContext->width, codecContext->height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (!window) {
std::cerr << "Failed to create SDL window: " << SDL_GetError() << "\n";
return false;
}

renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
std::cerr << "Failed to create SDL renderer: " << SDL_GetError() << "\n";
return false;
}

texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height);
if (!texture) {
std::cerr << "Failed to create SDL texture: " << SDL_GetError() << "\n";
return false;
}

swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, codecContext->width, codecContext->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);
if (!swsContext) {
std::cerr << "Failed to create SwsContext\n";
return false;
}

timeBase = formatContext->streams[videoStream]->time_base;
frameDuration = av_q2d(timeBase);

return true;
}

void playVideo() {
AVPacket packet;
Uint32 previousFrameTime = SDL_GetTicks();

while (av_read_frame(formatContext, &packet) >= 0) {
if (packet.stream_index == videoStream) {
if (avcodec_send_packet(codecContext, &packet) < 0) {
std::cerr << "Error sending a packet for decoding\n";
continue;
}

if (avcodec_receive_frame(codecContext, frame) == 0) {
sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, frame->data, frame->linesize);

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);

Uint32 currentFrameTime = SDL_GetTicks();
Uint32 elapsedTime = currentFrameTime - previousFrameTime;
previousFrameTime = currentFrameTime;

int desiredDelay = (int)(frameDuration * 1000) - elapsedTime;

if (desiredDelay > 0) {
SDL_Delay(desiredDelay);
}
}
}
av_packet_unref(&packet);
}
}

void closeVideoPlayer() {
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

av_frame_free(&frame);
avcodec_close(codecContext);
avformat_close_input(&formatContext);
avformat_network_deinit();
}
Empty file removed modules/video/test/.gitignore
Empty file.

0 comments on commit 0bf3b67

Please sign in to comment.