commit 7be6ecc9a3f1c47cdbb0d96f1e61dc196c4c4821 Author: Jie Date: Tue Feb 20 11:16:08 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9b4f01 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build/* diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..caf4b53 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) 启动", + "type": "cppdbg", + "request": "launch", + "program": "/home/jie/code/cc/sdl2/videoPlayer/videoPlayer", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "为 gdb 启用整齐打印", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "将反汇编风格设置为 Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "build" + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3f443 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "*.cpp": "cpp", + "type_traits": "cpp", + "filesystem": "cpp", + "fstream": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f942aa9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.26) +set(PROJECT_N VideoPlayer) +project(${PROJECT_N} VERSION 1.0) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +include_directories(/usr/include/x86_64-linux-gnu) +set(GLAD_DIR /home/jie/documents/third/glad) +set(GLM_DIR /home/jie/documents/third/glm) +include_directories(${GLAD_DIR}/include) +include_directories(${GLM_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/include) +find_package(SDL2 REQUIRED) +file(GLOB_RECURSE srcs ${PROJECT_SOURCE_DIR}/src/*.cc) +add_executable(${PROJECT_N} + main.cc + ${GLAD_DIR}/src/glad.c + ${srcs} +) +target_link_libraries(${PROJECT_N} + ${SDL2_LIBRARIES} + SDL2_image + OpenGL + avcodec + avformat + vorbis + avutil +) diff --git a/include/decoder.h b/include/decoder.h new file mode 100644 index 0000000..505d004 --- /dev/null +++ b/include/decoder.h @@ -0,0 +1,25 @@ +#ifndef DECODER_H +#define DECODER_H +extern "C"{ + #include "libavcodec/avcodec.h" + #include "libavformat/avformat.h" + #include "libavutil/imgutils.h" +} + +struct DecoderParam{ + AVFormatContext* fmtCtx; + AVCodecContext* codecCtx; + int width; + int height; + int videoStreamIndex; +}; + +extern "C"{ + #include "libavcodec/avcodec.h" + #include "libavformat/avformat.h" + #include "libavutil/imgutils.h" +} + +void InitDecoder(const char* filepath, DecoderParam& param); +AVFrame* RequestFrame(DecoderParam& param); +#endif \ No newline at end of file diff --git a/include/shaderService.h b/include/shaderService.h new file mode 100644 index 0000000..ccc3f88 --- /dev/null +++ b/include/shaderService.h @@ -0,0 +1,56 @@ +// +// Created by jie on 2023/10/11. +// + +#ifndef LEARNOPENGL_SHADERHELPER_H +#define LEARNOPENGL_SHADERHELPER_H + +#include "glad/glad.h" +#include +#include +#include + +class ShaderService +{ +private: + unsigned int programId; + +public: + ShaderService(const std::filesystem::path &vertexShaderPath, const std::filesystem::path &fragShaderPath); + bool CheckShader(unsigned int shaderIndex, bool isProgram = false); + void Use(); + inline unsigned int GetId() { return this->programId; } + template + requires std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v + void SetUniform(std::string_view name, T value); +}; + +template + requires std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v +void ShaderService::SetUniform(std::string_view name, T value) +{ + if constexpr (std::is_same_v) + { + glUniform1i(glGetUniformLocation(programId, name.data()), (int)value); + } + else if constexpr (std::is_same_v) + { + glUniform1i(glGetUniformLocation(programId, name.data()), value); + } + else if constexpr (std::is_same_v) + { + glUniform1f(glGetUniformLocation(programId, name.data()), value); + } + else if constexpr (std::is_same_v) + { + const auto index = glGetUniformLocation(this->programId, name.data()); + glUniformMatrix4fv(index, 1, GL_FALSE, &value[0][0]); + } + else if constexpr (std::is_same_v){ + glUniform3fv(glGetUniformLocation(programId, name.data()), 1, &value[0]); + } +} + +#endif // LEARNOPENGL_SHADERHELPER_H diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..79a602f --- /dev/null +++ b/main.cc @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include "decoder.h" +#include "shaderService.h" +using namespace std::filesystem; +using std::cout, std::endl; + +int main(int argc, char **const argv) +{ + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) + { + cout << SDL_GetError() << "\n"; + return -1; + } + DecoderParam param{}; + InitDecoder("/home/jie/code/cc/sdl2/videoPlayer/video/ocean.mp4", param); + int client_width = param.width / 2; + int client_height = param.height / 2; + SDL_Window *window = SDL_CreateWindow( + "SDL2Demo", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + client_width, + client_height, + SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL); + + if (!window) + { + cout << SDL_GetError() << "\n"; + return -1; + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + std::cout << "Failed to initialize SDL2! \n" + << SDL_GetError() << std::endl; + return -1; + } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + SDL_GLContext glContext = SDL_GL_CreateContext(window); + if (!glContext) + { + cout << SDL_GetError() << "\n"; + return -1; + } + + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) + { + auto error = glad_glGetError(); + return static_cast(error); + } + + float vertices[] = { + 1.f, 1.f, 0.0, 1.0, 0.0, + 1.f, -1.f, 0.0, 1.0, 1.0, + -1.f, -1.f, 0.0, 0.0, 1.0, + -1.f, 1.f, 0.0, 0.0, 0.0}; + unsigned int indices[]{ + 0, + 1, + 3, + 1, + 2, + 3, + }; + + unsigned int VAO, VBO, EBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + unsigned int texs[3]; + glGenTextures(3, texs); + for (int i = 0; i < 3; i++) + { + glBindTexture(GL_TEXTURE_2D, texs[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + ShaderService shaderService{ + "/home/jie/code/cc/sdl2/videoPlayer/shaders/vertexShader.vert", + "/home/jie/code/cc/sdl2/videoPlayer/shaders/fragShader.frag"}; + shaderService.Use(); + shaderService.SetUniform("textureY", 0); + shaderService.SetUniform("textureU", 1); + shaderService.SetUniform("textureV", 2); + + bool quit = false; + SDL_Event event; + while (!quit) + { + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.scancode) + { + case SDL_SCANCODE_ESCAPE: + return -1; + default: + break; + } + break; + case SDL_QUIT: + return -1; + default: + break; + } + } + + // Render + + auto frame = RequestFrame(param); + int64_t pts = frame->pts; + static bool first_frame = true; + // TODO: TIMER + SDL_Delay(100); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texs[0]); + glPixelStoref(GL_UNPACK_ROW_LENGTH, frame->linesize[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame->width, frame->height, 0, GL_RED, GL_UNSIGNED_BYTE, frame->data[0]); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texs[1]); + glPixelStoref(GL_UNPACK_ROW_LENGTH, frame->linesize[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame->width / 2, frame->height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, frame->data[1]); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, texs[2]); + glPixelStoref(GL_UNPACK_ROW_LENGTH, frame->linesize[2]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame->width / 2, frame->height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, frame->data[2]); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + shaderService.Use(); + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + SDL_GL_SwapWindow(window); + } + + avcodec_close(param.codecCtx); + avformat_close_input(&(param.fmtCtx)); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/shaders/fragShader.frag b/shaders/fragShader.frag new file mode 100644 index 0000000..687a302 --- /dev/null +++ b/shaders/fragShader.frag @@ -0,0 +1,25 @@ +#version 330 core + +out vec4 FragColor; + +in vec2 TexCoord; + +uniform sampler2D textureY; +uniform sampler2D textureU; +uniform sampler2D textureV; + +void main(){ + vec3 yuv, rgb; + vec3 yuv2r = vec3(1.164, 0.0, 1.596); + vec3 yuv2g = vec3(1.164, -0.391, -0.813); + vec3 yuv2b = vec3(1.164, 2.018, 0.0); + yuv.x = texture(textureY, TexCoord).r - 0.0625; + yuv.y = texture(textureU, TexCoord).r - 0.5; + yuv.z = texture(textureV, TexCoord).r - 0.5; + + rgb.x = dot(yuv, yuv2r); + rgb.y = dot(yuv, yuv2g); + rgb.z = dot(yuv, yuv2b); + + FragColor = vec4(rgb, 1.0); +} \ No newline at end of file diff --git a/shaders/vertexShader.vert b/shaders/vertexShader.vert new file mode 100644 index 0000000..6afa8ca --- /dev/null +++ b/shaders/vertexShader.vert @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; + +out vec2 TexCoord; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + TexCoord = vec2(aTexCoord.x, aTexCoord.y); +} \ No newline at end of file diff --git a/src/decoder.cc b/src/decoder.cc new file mode 100644 index 0000000..7d76f8d --- /dev/null +++ b/src/decoder.cc @@ -0,0 +1,53 @@ +#include "decoder.h" +#include + +void InitDecoder(const char* filepath, DecoderParam& param){ + if(!std::filesystem::exists(filepath)) return; + AVFormatContext* fmtCtx = nullptr; + AVCodecContext* codecFmt = nullptr; + auto ret = avformat_open_input(&fmtCtx, filepath, NULL, NULL); + + avformat_find_stream_info(fmtCtx, nullptr); + + for(int i=0; inb_streams; i++){ + auto stream = fmtCtx->streams[i]; + auto codec = avcodec_find_decoder(stream->codecpar->codec_id); + if(codec->type == AVMEDIA_TYPE_VIDEO){ + param.videoStreamIndex = i; + codecFmt = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(codecFmt, stream->codecpar); + avcodec_open2(codecFmt, codec, nullptr); + } + } + + param.codecCtx = codecFmt; + param.fmtCtx = fmtCtx; + param.width = codecFmt->width; + param.height = codecFmt->height; +} + +AVFrame* RequestFrame(DecoderParam& param){ + auto& fmtCtx = param.fmtCtx; + auto& codecCtx = param.codecCtx; + auto& videoStreamIndex = param.videoStreamIndex; + + while(true){ + AVPacket* packet = av_packet_alloc(); + int ret = av_read_frame(fmtCtx, packet); + if(ret == 0 && packet->stream_index == videoStreamIndex){ + ret = avcodec_send_packet(codecCtx, packet); + if(ret == 0){ + AVFrame* frame = av_frame_alloc(); + ret = avcodec_receive_frame(codecCtx, frame); + if(ret == 0){ + av_packet_unref(packet); + return frame; + }else if(ret == AVERROR(EAGAIN)){ + av_frame_unref(frame); + } + } + } + av_packet_unref(packet); + } + return nullptr; +} diff --git a/src/shaderService.cc b/src/shaderService.cc new file mode 100644 index 0000000..0426de4 --- /dev/null +++ b/src/shaderService.cc @@ -0,0 +1,74 @@ +// +// Created by jie on 2023/10/11. +// + +#include "shaderService.h" +#include +#include + +ShaderService::ShaderService(const std::filesystem::path &vertexShaderPath, + const std::filesystem::path &fragShaderPath) { + if(!(exists(vertexShaderPath) && exists(fragShaderPath))){ + std::cout<<"file not exist"<