From 3753778bb7599d2cd2144206428fd45e2f7a611c Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 15:06:23 -0300 Subject: [PATCH 01/12] made it raii --- 03-record-sdl/CMakeLists.txt | 18 +++++---- 03-record-sdl/src/CodecContext.cpp | 48 ++++++++++++++++++++++ 03-record-sdl/src/CodecContext.h | 32 +++++++++++++++ 03-record-sdl/src/FormatOutput.cpp | 41 +++++++++++++++++++ 03-record-sdl/src/FormatOutput.h | 48 ++++++++++++++++++++++ 03-record-sdl/src/Frame.cpp | 42 ++++++++++++++++++++ 03-record-sdl/src/Frame.h | 64 ++++++++++++++++++++++++++++++ 03-record-sdl/src/Output.cpp | 63 +++++++++++++++++++++++++++++ 03-record-sdl/src/Output.h | 42 ++++++++++++++++++++ 03-record-sdl/src/Packet.cpp | 28 +++++++++++++ 03-record-sdl/src/Packet.h | 48 ++++++++++++++++++++++ 03-record-sdl/src/main.cpp | 5 ++- 12 files changed, 470 insertions(+), 9 deletions(-) create mode 100644 03-record-sdl/src/CodecContext.cpp create mode 100644 03-record-sdl/src/CodecContext.h create mode 100644 03-record-sdl/src/FormatOutput.cpp create mode 100644 03-record-sdl/src/FormatOutput.h create mode 100644 03-record-sdl/src/Frame.cpp create mode 100644 03-record-sdl/src/Frame.h create mode 100644 03-record-sdl/src/Output.cpp create mode 100644 03-record-sdl/src/Output.h create mode 100644 03-record-sdl/src/Packet.cpp create mode 100644 03-record-sdl/src/Packet.h diff --git a/03-record-sdl/CMakeLists.txt b/03-record-sdl/CMakeLists.txt index c2a2d9f..402132c 100644 --- a/03-record-sdl/CMakeLists.txt +++ b/03-record-sdl/CMakeLists.txt @@ -5,10 +5,10 @@ project(ffmpeg-sdl) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -std=c++11") add_library(sdldemo - src/Area.cpp - src/SdlTexture.cpp - src/SdlException.cpp - src/SdlWindow.cpp + src/Area.cpp + src/SdlTexture.cpp + src/SdlException.cpp + src/SdlWindow.cpp ) # Agrego la carpeta con los headers autogenerados a los includes @@ -16,9 +16,13 @@ message("Agregando '${PROJECT_BINARY_DIR}' como directorio de includes") include_directories(${PROJECT_BINARY_DIR}) add_executable (record-sdl - src/OutputFormat.cpp - src/FormatContext.cpp - src/main.cpp + src/Output.cpp + src/Frame.cpp + src/Packet.cpp + src/FormatOutput.cpp + src/CodecContext.cpp + src/FormatContext.cpp + src/main.cpp ) target_link_libraries(record-sdl avformat avcodec avutil swscale sdldemo SDL2 SDL2_image) diff --git a/03-record-sdl/src/CodecContext.cpp b/03-record-sdl/src/CodecContext.cpp new file mode 100644 index 0000000..500b478 --- /dev/null +++ b/03-record-sdl/src/CodecContext.cpp @@ -0,0 +1,48 @@ +// +// Created by camix on 10/06/19. +// + +extern "C" { +#include +} +#include "CodecContext.h" + +CodecContext::CodecContext(AVCodec *codec) { + this->codecContext = avcodec_alloc_context3(codec); + // La resolución debe ser múltiplo de 2 + this->codecContext->width = 352; + this->codecContext->height = 288; + this->codecContext->time_base = {1,25}; + this->codecContext->framerate = {25,1}; + this->codecContext->pix_fmt = AV_PIX_FMT_YUV420P; + this->codecContext->gop_size = 10; + this->codecContext->max_b_frames = 2; + if (codec->id == AV_CODEC_ID_H264) { + this->codecContext->profile = FF_PROFILE_H264_BASELINE; + av_opt_set(this->codecContext->priv_data, "preset", "slow", 0); + } + avcodec_open2(this->codecContext, codec, NULL); +} + +CodecContext::~CodecContext() { + avcodec_close(this->codecContext); + avcodec_free_context(&this->codecContext); +} + + +int CodecContext::getPixFmt() { + return this->codecContext->pix_fmt; +} + + +int CodecContext::getWidth() { + return this->codecContext->width; +} + +int CodecContext::getHeight() { + return this->codecContext->height; +} + +AVCodecContext* CodecContext::get() { + return this->codecContext; +} \ No newline at end of file diff --git a/03-record-sdl/src/CodecContext.h b/03-record-sdl/src/CodecContext.h new file mode 100644 index 0000000..0c62d43 --- /dev/null +++ b/03-record-sdl/src/CodecContext.h @@ -0,0 +1,32 @@ +// +// Created by camix on 10/06/19. +// + +#ifndef FFMPEG_DEMO_CODECCONTEXT_H +#define FFMPEG_DEMO_CODECCONTEXT_H + +extern "C" { +#include +} + + +class CodecContext { +private: + AVCodecContext* codecContext; + +public: + explicit CodecContext(AVCodec *codec); + + ~CodecContext(); + + AVCodecContext *get(); + + int getHeight(); + + int getWidth(); + + int getPixFmt(); +}; + + +#endif //FFMPEG_DEMO_CODECCONTEXT_H diff --git a/03-record-sdl/src/FormatOutput.cpp b/03-record-sdl/src/FormatOutput.cpp new file mode 100644 index 0000000..f4134f6 --- /dev/null +++ b/03-record-sdl/src/FormatOutput.cpp @@ -0,0 +1,41 @@ +// +// Created by camix on 10/06/19. +// +extern "C" { +#include +} + +#include "FormatOutput.h" +#include "CodecContext.h" + +FormatOutput::FormatOutput(const std::string& filename) { + // Intenta deducir formato según extensión + this->avOutputFormat = av_guess_format(NULL, filename.c_str(), NULL); + if (!this->avOutputFormat) { + // Intenta usar el formato standard + this->avOutputFormat = av_guess_format("mpeg", NULL, NULL); + } + if (!this->avOutputFormat) { + throw FormatOutputInitException(); + } + // h.264 es bastante popular, pero hay mejores + this->avOutputFormat->video_codec = AV_CODEC_ID_H264; + + + + this->codec = avcodec_find_encoder(this->avOutputFormat->video_codec); + if (!codec) { + throw FormatOutputInitException("No se pudo instanciar codec"); + } + //codecContext.init(codec); +} + + +AVCodec *FormatOutput::getCodec() { + return this->codec; +} + + +FormatOutput::~FormatOutput() { + +} diff --git a/03-record-sdl/src/FormatOutput.h b/03-record-sdl/src/FormatOutput.h new file mode 100644 index 0000000..1b0973e --- /dev/null +++ b/03-record-sdl/src/FormatOutput.h @@ -0,0 +1,48 @@ +// +// Created by camix on 10/06/19. +// + +#ifndef FFMPEG_DEMO_FORMATOUTPUT_H +#define FFMPEG_DEMO_FORMATOUTPUT_H + +#define INIT_FORMAT_OUTPUT_EXC "No output format was found" + +#include + +extern "C" { +#include +} + +class FormatOutputException : public std::exception { +protected: + std::string message; +public: + FormatOutputException() = default; + virtual const char *what() const throw() { + return this->message.c_str(); + } +}; + +class FormatOutputInitException : public FormatOutputException { +public: + FormatOutputInitException() { + message = INIT_FORMAT_OUTPUT_EXC; + } + explicit FormatOutputInitException(std::string msg) { + message = msg; + } +}; + +class FormatOutput { +private: + AVOutputFormat* avOutputFormat; + AVCodec *codec; + +public: + explicit FormatOutput(const std::string& filename); + ~FormatOutput(); + AVCodec *getCodec(); +}; + + +#endif //FFMPEG_DEMO_FORMATOUTPUT_H diff --git a/03-record-sdl/src/Frame.cpp b/03-record-sdl/src/Frame.cpp new file mode 100644 index 0000000..9e42031 --- /dev/null +++ b/03-record-sdl/src/Frame.cpp @@ -0,0 +1,42 @@ +// +// Created by camix on 04/06/19. +// + +#include "Frame.h" +extern "C" { +#include +} + +Frame::Frame(int format, int width, int height) : frame(av_frame_alloc()){ + if (!frame){ + throw FrameInitException(); + } + + this->frame->format = format; + this->frame->width = width; + this->frame->height = height; + + av_frame_get_buffer(this->frame, 0); + this->currentPts = 0; +} + + +Frame::~Frame() { + av_frame_free(&frame); +} + +void Frame::write(const char *data, SwsContext *ctx) { + const u_int8_t* tmp = (const u_int8_t*) data; + // El ancho del video x3 por la cantidad de bytes + int width = 352 * 3; + sws_scale(ctx, &tmp, &width, 0, frame->height, frame->data, frame->linesize); + frame->pts = currentPts; + currentPts++; +} + +void Frame::sendTo(AVCodecContext* codecContext) { + int s = avcodec_send_frame(codecContext, this->frame); + if (s < 0) { + throw FrameSendException(); + } +} \ No newline at end of file diff --git a/03-record-sdl/src/Frame.h b/03-record-sdl/src/Frame.h new file mode 100644 index 0000000..9943e7f --- /dev/null +++ b/03-record-sdl/src/Frame.h @@ -0,0 +1,64 @@ +// +// Created by camix on 04/06/19. +// + +#ifndef PORTAL_FRAME_H +#define PORTAL_FRAME_H + +#include +extern "C" { +#include +#include +} + +#define INIT_FRAME_EXC "There was an error while allocating memory for a Frame\n" +#define SEND_FRAME_EXC "There was an error while sending the frame\n" + +class FrameException : public std::exception { +protected: + std::string message; +public: + explicit FrameException() = default; + virtual const char *what() const throw() { + return this->message.c_str(); + } +}; + +class FrameInitException : public FrameException { +public: + explicit FrameInitException() { + message = INIT_FRAME_EXC; + } +}; + +class FrameSendException : public FrameException { + virtual const char *what() const throw() { + return this->message.c_str(); + } +public: + explicit FrameSendException() { + message = SEND_FRAME_EXC; + } +}; + +class SwsContext; + +class Frame { +private: + AVFrame* frame; + int currentPts; + +public: + Frame(int format, int width, int height); + ~Frame(); + + void write(const char *data, SwsContext *ctx); + void sendTo(AVCodecContext* codecContext); + + + AVFrame* get() {return this->frame;} +}; + + + +#endif //PORTAL_FRAME_H diff --git a/03-record-sdl/src/Output.cpp b/03-record-sdl/src/Output.cpp new file mode 100644 index 0000000..c733ef0 --- /dev/null +++ b/03-record-sdl/src/Output.cpp @@ -0,0 +1,63 @@ +#include "Output.h" +#include "FormatContext.h" +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +static void encode(CodecContext* codecContext, Frame* frame, Packet* pkt, + FILE *outfile) { + int ret = 0; + AVCodecContext* enc_ctx = codecContext->get(); + if (!frame) { + ret = avcodec_send_frame(enc_ctx, NULL); + } else { + ret = avcodec_send_frame(enc_ctx, frame->get()); + } + if (ret < 0) { + throw std::runtime_error("Error al enviar frame"); + } + while (ret >= 0) { + ret = avcodec_receive_packet(enc_ctx, pkt->get()); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + throw std::runtime_error("Error al codificar"); + } + pkt->write(outfile); + } +} + +Output::Output(FormatContext& context, const std::string& filename) : + context(context) , + format(filename), + codecContext(format.getCodec()), + frame(codecContext.getPixFmt(), codecContext.getWidth(), codecContext.getHeight()) { + this->outputFile = fopen(filename.c_str(), "wb"); +} + +void Output::close() { + encode(&codecContext, nullptr, &pkt, this->outputFile); + // add sequence end code to have a real MPEG file + uint8_t endCode[] = { 0, 0, 1, 0xb7 }; + fwrite(endCode, 1, sizeof(endCode), this->outputFile); +} + + + +void Output::writeFrame(const char* data, SwsContext* ctx) { + frame.write(data, ctx); + encode(&codecContext, &frame, &pkt, outputFile); +} + + +Output::~Output() { + fclose(this->outputFile); +} diff --git a/03-record-sdl/src/Output.h b/03-record-sdl/src/Output.h new file mode 100644 index 0000000..045ccf2 --- /dev/null +++ b/03-record-sdl/src/Output.h @@ -0,0 +1,42 @@ +#ifndef OUTPUTFORMAT_H +#define OUTPUTFORMAT_H +#include +#include "Frame.h" +#include "Packet.h" +#include "FormatOutput.h" +#include "CodecContext.h" + +class AVCodec; +class AVFrame; +class AVPacket; +class AVOutputFormat; +class AVStream; +class AVCodecContext; +class FormatContext; +class SwsContext; +/** + * Clase que encapsula lógica la salida de video + * Se recomienda modularizar aun más esta clase, reforzando RAII + */ +class Output { +private: + void codecContextInit(AVCodec* codec); + FormatContext& context; + FormatOutput format; + CodecContext codecContext; + FILE* outputFile; + Frame frame; + Packet pkt; + +public: + // Ctor + Output(FormatContext& context, const std::string& filename); + // Dtor + ~Output(); + // Escribe un frame a disco. Utiliza `swsContext` para convertir + // de RGB24 a YUV420p + void writeFrame(const char* data, SwsContext* swsContext); + // Cierra el stream de video + void close(); +}; +#endif diff --git a/03-record-sdl/src/Packet.cpp b/03-record-sdl/src/Packet.cpp new file mode 100644 index 0000000..0d6c50d --- /dev/null +++ b/03-record-sdl/src/Packet.cpp @@ -0,0 +1,28 @@ +// +// Created by camix on 10/06/19. +// + +#include +#include "Packet.h" + +Packet::Packet() : + pkt(av_packet_alloc()) { + if (!pkt) { + throw PacketInitException(); + } +} + +AVPacket* Packet::get() { + return pkt; +} + +void Packet::write(FILE *outfile) { + //esto es lento porque es escritura en disco + fwrite(pkt->data, 1, pkt->size, outfile); + av_packet_unref(pkt); +} + +Packet::~Packet() { + std::cerr << "entro a destruir el packet" << std::endl; + av_packet_free(&pkt); +} diff --git a/03-record-sdl/src/Packet.h b/03-record-sdl/src/Packet.h new file mode 100644 index 0000000..a671543 --- /dev/null +++ b/03-record-sdl/src/Packet.h @@ -0,0 +1,48 @@ +// +// Created by camix on 10/06/19. +// + +#ifndef FFMPEG_DEMO_PACKET_H +#define FFMPEG_DEMO_PACKET_H + + +#define INIT_PKT_EXC "There was an error while allocating memory for a Packet\n" + +#include + +extern "C" { +#include +} + +class PacketException : public std::exception { +protected: + std::string message; +public: + PacketException() = default; + virtual const char *what() const throw() { + return this->message.c_str(); + } +}; + +class PacketInitException : public PacketException { +public: + PacketInitException() { + message = INIT_PKT_EXC; + } +}; + +class Packet { +private: + AVPacket* pkt; + +public: + Packet(); + ~Packet(); + + AVPacket *get(); + + void write(FILE *outfile); +}; + + +#endif //FFMPEG_DEMO_PACKET_H diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index a03de47..16d466f 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -9,7 +9,7 @@ extern "C" { #include } #include "FormatContext.h" -#include "OutputFormat.h" +#include "Output.h" const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; @@ -23,7 +23,7 @@ int main(int argc, char** argv){ try { av_register_all(); FormatContext context; - OutputFormat videoOutput(context, argv[1]); + Output videoOutput(context, argv[1]); SdlWindow window(800, 600); window.fill(); // Usar factory @@ -63,6 +63,7 @@ int main(int argc, char** argv){ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "RendererReadPixels error", SDL_GetError(), NULL); break; } + //operacion lenta que va a tenre que estar en otro thread videoOutput.writeFrame(dataBuffer.data(), ctx); } videoOutput.close(); From bedd9afe9e1cee76829a4f20f789f28f9ec9512c Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 15:32:36 -0300 Subject: [PATCH 02/12] CodecContantext was't really RAII. Now it is. --- .gitignore | 2 ++ 03-record-sdl/src/CodecContext.cpp | 7 +++++-- 03-record-sdl/src/CodecContext.h | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 513eaeb..8a3340f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build *.kdev4 +.idea +cmake-build-debug/ \ No newline at end of file diff --git a/03-record-sdl/src/CodecContext.cpp b/03-record-sdl/src/CodecContext.cpp index 500b478..fa3ed76 100644 --- a/03-record-sdl/src/CodecContext.cpp +++ b/03-record-sdl/src/CodecContext.cpp @@ -7,8 +7,11 @@ extern "C" { } #include "CodecContext.h" -CodecContext::CodecContext(AVCodec *codec) { - this->codecContext = avcodec_alloc_context3(codec); +CodecContext::CodecContext(AVCodec *codec) : +codecContext(avcodec_alloc_context3(codec)) { + if (!codecContext) { + throw CodecContextInitException(); + } // La resolución debe ser múltiplo de 2 this->codecContext->width = 352; this->codecContext->height = 288; diff --git a/03-record-sdl/src/CodecContext.h b/03-record-sdl/src/CodecContext.h index 0c62d43..58e10a9 100644 --- a/03-record-sdl/src/CodecContext.h +++ b/03-record-sdl/src/CodecContext.h @@ -5,10 +5,29 @@ #ifndef FFMPEG_DEMO_CODECCONTEXT_H #define FFMPEG_DEMO_CODECCONTEXT_H +#define INIT_CODEC_CONTEXT_EXC "There was an error while allocating memory for a CodecContext\n" extern "C" { #include } +#include + +class CodecContextException : public std::exception { +protected: + std::string message; +public: + explicit CodecContextException() = default; + virtual const char *what() const throw() { + return this->message.c_str(); + } +}; + +class CodecContextInitException : public CodecContextException { +public: + explicit CodecContextInitException() { + message = INIT_CODEC_CONTEXT_EXC; + } +}; class CodecContext { private: From f97071152b4f18b4a420ebc3cfd5b4e20fcb859e Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 15:33:48 -0300 Subject: [PATCH 03/12] deleted unused function --- 03-record-sdl/src/Frame.cpp | 7 ------- 03-record-sdl/src/Frame.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/03-record-sdl/src/Frame.cpp b/03-record-sdl/src/Frame.cpp index 9e42031..2ce8bd6 100644 --- a/03-record-sdl/src/Frame.cpp +++ b/03-record-sdl/src/Frame.cpp @@ -33,10 +33,3 @@ void Frame::write(const char *data, SwsContext *ctx) { frame->pts = currentPts; currentPts++; } - -void Frame::sendTo(AVCodecContext* codecContext) { - int s = avcodec_send_frame(codecContext, this->frame); - if (s < 0) { - throw FrameSendException(); - } -} \ No newline at end of file diff --git a/03-record-sdl/src/Frame.h b/03-record-sdl/src/Frame.h index 9943e7f..dadb060 100644 --- a/03-record-sdl/src/Frame.h +++ b/03-record-sdl/src/Frame.h @@ -53,8 +53,6 @@ class Frame { ~Frame(); void write(const char *data, SwsContext *ctx); - void sendTo(AVCodecContext* codecContext); - AVFrame* get() {return this->frame;} }; From 8d8f5b393b9e96bb4e1f6dee20bf4bce77a5d058 Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 15:38:20 -0300 Subject: [PATCH 04/12] deleted unused exception --- 03-record-sdl/src/Frame.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/03-record-sdl/src/Frame.h b/03-record-sdl/src/Frame.h index dadb060..29cfd1c 100644 --- a/03-record-sdl/src/Frame.h +++ b/03-record-sdl/src/Frame.h @@ -12,7 +12,6 @@ extern "C" { } #define INIT_FRAME_EXC "There was an error while allocating memory for a Frame\n" -#define SEND_FRAME_EXC "There was an error while sending the frame\n" class FrameException : public std::exception { protected: @@ -31,16 +30,6 @@ class FrameInitException : public FrameException { } }; -class FrameSendException : public FrameException { - virtual const char *what() const throw() { - return this->message.c_str(); - } -public: - explicit FrameSendException() { - message = SEND_FRAME_EXC; - } -}; - class SwsContext; class Frame { From ccc5b2ec21f23716cd8f4872ebb2988f0edc90a1 Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 16:26:46 -0300 Subject: [PATCH 05/12] removed logic of ffmpeg from the main and encapsulated in SwsContext --- 03-record-sdl/CMakeLists.txt | 1 + 03-record-sdl/src/Output.h | 1 - 03-record-sdl/src/SwsContext.cpp | 39 ++++++++++++++++++++++ 03-record-sdl/src/SwsContext.h | 56 ++++++++++++++++++++++++++++++++ 03-record-sdl/src/main.cpp | 30 +++++------------ 5 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 03-record-sdl/src/SwsContext.cpp create mode 100644 03-record-sdl/src/SwsContext.h diff --git a/03-record-sdl/CMakeLists.txt b/03-record-sdl/CMakeLists.txt index 402132c..aba7011 100644 --- a/03-record-sdl/CMakeLists.txt +++ b/03-record-sdl/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable (record-sdl src/FormatOutput.cpp src/CodecContext.cpp src/FormatContext.cpp + src/SwsContext.cpp src/main.cpp ) diff --git a/03-record-sdl/src/Output.h b/03-record-sdl/src/Output.h index 045ccf2..4f2fc54 100644 --- a/03-record-sdl/src/Output.h +++ b/03-record-sdl/src/Output.h @@ -20,7 +20,6 @@ class SwsContext; */ class Output { private: - void codecContextInit(AVCodec* codec); FormatContext& context; FormatOutput format; CodecContext codecContext; diff --git a/03-record-sdl/src/SwsContext.cpp b/03-record-sdl/src/SwsContext.cpp new file mode 100644 index 0000000..ddc2be9 --- /dev/null +++ b/03-record-sdl/src/SwsContext.cpp @@ -0,0 +1,39 @@ +// +// Created by camix on 10/06/19. +// + +#include +#include +#include +#include "SwsContext.h" + +const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; + +/// first you need to Initialize libavformat and register all the muxers, demuxers and +/// protocols with av_register_all(); +SwsContext::SwsContext(std::string filename) : + videoOutput(context, filename), + ctx(sws_getContext(BUFFER_WIDTH, BUFFER_HEIGHT, + AV_PIX_FMT_RGB24, BUFFER_WIDTH, BUFFER_HEIGHT, + AV_PIX_FMT_YUV420P, 0, 0, 0, 0)) , + // Este buffer tiene el tamaño de la sección de SDL que quiero leer, multiplico + // x3 por la cantidad de bytes (8R,8G,8B) + // A sws parece que no le gusta este tamaño + dataBuffer(BUFFER_WIDTH*BUFFER_HEIGHT*3) {} + +SwsContext::~SwsContext() { + videoOutput.close(); + // Libero escalador + sws_freeContext(ctx); +} + +void SwsContext::write(SdlWindow& window) { + // Obtengo los bytes de la textura en el buffer + int res = SDL_RenderReadPixels(window.getRenderer(), NULL, SDL_PIXELFORMAT_RGB24, dataBuffer.data(), BUFFER_WIDTH * 3); + if (res) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "RendererReadPixels error", SDL_GetError(), NULL); + throw SwsContextRendererReadPixelsException(); + } + //operacion lenta que va a tenre que estar en otro thread + videoOutput.writeFrame(dataBuffer.data(), ctx); +} diff --git a/03-record-sdl/src/SwsContext.h b/03-record-sdl/src/SwsContext.h new file mode 100644 index 0000000..295d980 --- /dev/null +++ b/03-record-sdl/src/SwsContext.h @@ -0,0 +1,56 @@ +// +// Created by camix on 10/06/19. +// + +#ifndef FFMPEG_DEMO_SWSCONTEXT_H +#define FFMPEG_DEMO_SWSCONTEXT_H + + +#define RRP_SWS_CONTEXT_EXC std::__cxx11::string() + +#include +#include +#include "FormatContext.h" +#include "Output.h" +#include "SdlWindow.h" + +extern "C" { +#include +#include +} + + +class SwsContextException : public std::exception { +protected: + std::string message; +public: + SwsContextException() = default; + virtual const char *what() const throw() { + return this->message.c_str(); + } +}; + +class SwsContextRendererReadPixelsException : public SwsContextException { +public: + SwsContextRendererReadPixelsException() { + message = RRP_SWS_CONTEXT_EXC; + } +}; + +// RAII wrapper for struct SwsContext +class SwsContext { +private: + FormatContext context; + Output videoOutput; + // You need it to perform scaling/conversion operations using. + SwsContext* ctx; + std::vector dataBuffer; + +public: + explicit SwsContext(std::string filename); + ~SwsContext(); + void write(SdlWindow& window); +}; + + +#endif //FFMPEG_DEMO_SWSCONTEXT_H diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index 16d466f..a224545 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -10,6 +10,7 @@ extern "C" { } #include "FormatContext.h" #include "Output.h" +#include "SwsContext.h" const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; @@ -22,8 +23,9 @@ int main(int argc, char** argv){ } try { av_register_all(); - FormatContext context; - Output videoOutput(context, argv[1]); + SwsContext ctx((std::string(argv[1]))); + + SdlWindow window(800, 600); window.fill(); // Usar factory @@ -35,14 +37,8 @@ int main(int argc, char** argv){ // Textura sobre la que voy a renderizar lo que quiero grabar. SDL_Texture* videoTexture = SDL_CreateTexture(window.getRenderer(), SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, BUFFER_WIDTH, BUFFER_HEIGHT); - // Contexto para escalar archivos. - SwsContext * ctx = sws_getContext(BUFFER_WIDTH, BUFFER_HEIGHT, - AV_PIX_FMT_RGB24, BUFFER_WIDTH, BUFFER_HEIGHT, - AV_PIX_FMT_YUV420P, 0, 0, 0, 0); - // Este buffer tiene el tamaño de la sección de SDL que quiero leer, multiplico - // x3 por la cantidad de bytes (8R,8G,8B) - // A sws parece que no le gusta este tamaño - std::vector dataBuffer(BUFFER_WIDTH*BUFFER_HEIGHT*3); + + while (running) { // Muevo textura con flechas direccionales handleSDLEvent(x, y, running); @@ -57,18 +53,10 @@ int main(int argc, char** argv){ catTexture.render(srcArea, destArea); // Efectivamente renderiza window.render(); - // Obtengo los bytes de la textura en el buffer - int res = SDL_RenderReadPixels(window.getRenderer(), NULL, SDL_PIXELFORMAT_RGB24, dataBuffer.data(), BUFFER_WIDTH * 3); - if (res) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "RendererReadPixels error", SDL_GetError(), NULL); - break; - } - //operacion lenta que va a tenre que estar en otro thread - videoOutput.writeFrame(dataBuffer.data(), ctx); + + + ctx.write(window); } - videoOutput.close(); - // Libero escalador - sws_freeContext(ctx); } catch (std::exception& e) { std::cout << e.what() << std::endl; return 1; From 3ee427dac33dcc5b6b58309740393ef722b2475d Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 16:47:47 -0300 Subject: [PATCH 06/12] added exception message --- 03-record-sdl/src/SwsContext.h | 2 +- 03-record-sdl/src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/03-record-sdl/src/SwsContext.h b/03-record-sdl/src/SwsContext.h index 295d980..e5f609f 100644 --- a/03-record-sdl/src/SwsContext.h +++ b/03-record-sdl/src/SwsContext.h @@ -6,7 +6,7 @@ #define FFMPEG_DEMO_SWSCONTEXT_H -#define RRP_SWS_CONTEXT_EXC std::__cxx11::string() +#define RRP_SWS_CONTEXT_EXC "There was an error while writing the frame\n" #include #include diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index a224545..a9a197c 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv){ SDL_Texture* videoTexture = SDL_CreateTexture(window.getRenderer(), SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, BUFFER_WIDTH, BUFFER_HEIGHT); - + while (running) { // Muevo textura con flechas direccionales handleSDLEvent(x, y, running); From 62b866e069464da2e43dde88981643fa6c778ace Mon Sep 17 00:00:00 2001 From: camiboj Date: Mon, 10 Jun 2019 16:59:23 -0300 Subject: [PATCH 07/12] deleted unused class --- 03-record-sdl/src/OutputFormat.cpp | 109 ----------------------------- 03-record-sdl/src/OutputFormat.h | 42 ----------- 2 files changed, 151 deletions(-) delete mode 100644 03-record-sdl/src/OutputFormat.cpp delete mode 100644 03-record-sdl/src/OutputFormat.h diff --git a/03-record-sdl/src/OutputFormat.cpp b/03-record-sdl/src/OutputFormat.cpp deleted file mode 100644 index aa739c3..0000000 --- a/03-record-sdl/src/OutputFormat.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "OutputFormat.h" -#include "FormatContext.h" -#include -#include -#include -extern "C" { -#include -#include -#include -#include -} - -static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, - FILE *outfile) { - int ret = avcodec_send_frame(enc_ctx, frame); - if (ret < 0) { - throw std::runtime_error("Error al enviar frame"); - } - while (ret >= 0) { - ret = avcodec_receive_packet(enc_ctx, pkt); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) - return; - else if (ret < 0) { - throw std::runtime_error("Error al codificar"); - } - fwrite(pkt->data, 1, pkt->size, outfile); - av_packet_unref(pkt); - } -} - -OutputFormat::OutputFormat(FormatContext& context, - const std::string& filename) : context(context) { - this->frame = av_frame_alloc(); - if (!frame) { - throw std::runtime_error("No se pudo reservar memoria para frame"); - } - this->pkt = av_packet_alloc(); - // Intenta deducir formato según extensión - this->avOutputFormat = av_guess_format(NULL, filename.c_str(), NULL); - if (!this->avOutputFormat) { - // Intenta usar el formato standard - this->avOutputFormat = av_guess_format("mpeg", NULL, NULL); - } - if (!this->avOutputFormat) { - throw std::runtime_error("No se encontró formato de salida"); - } - // h.264 es bastante popular, pero hay mejores - this->avOutputFormat->video_codec = AV_CODEC_ID_H264; - AVCodec *codec = avcodec_find_encoder(this->avOutputFormat->video_codec); - if (!codec) { - throw std::runtime_error("No se pudo instanciar codec"); - } - codecContextInit(codec); - this->outputFile = fopen(filename.c_str(), "wb"); - initFrame(); -} - -void OutputFormat::close() { - encode(this->codecContext, NULL, this->pkt, this->outputFile); - /* add sequence end code to have a real MPEG file */ - uint8_t endcode[] = { 0, 0, 1, 0xb7 }; - fwrite(endcode, 1, sizeof(endcode), this->outputFile); -} - -void OutputFormat::initFrame() { - this->frame->format = this->codecContext->pix_fmt; - this->frame->width = this->codecContext->width; - this->frame->height = this->codecContext->height; - - av_frame_get_buffer(this->frame, 0); - this->currentPts = 0; -} - -void OutputFormat::writeFrame(const char* data, SwsContext* ctx ) { - const u_int8_t* tmp = (const u_int8_t*) data; - // El ancho del video x3 por la cantidad de bytes - int width = 352 * 3; - sws_scale(ctx, &tmp, &width, 0, frame->height, frame->data, frame->linesize); - //drawFrame(frame, data); - frame->pts = currentPts; - currentPts++; - /* encode the image */ - encode(this->codecContext, frame, pkt, this->outputFile); -} - -void OutputFormat::codecContextInit(AVCodec* codec){ - this->codecContext = avcodec_alloc_context3(codec); - // La resolución debe ser múltiplo de 2 - this->codecContext->width = 352; - this->codecContext->height = 288; - this->codecContext->time_base = {1,25}; - this->codecContext->framerate = {25,1}; - this->codecContext->pix_fmt = AV_PIX_FMT_YUV420P; - this->codecContext->gop_size = 10; - this->codecContext->max_b_frames = 2; - if (codec->id == AV_CODEC_ID_H264) { - this->codecContext->profile = FF_PROFILE_H264_BASELINE; - av_opt_set(this->codecContext->priv_data, "preset", "slow", 0); - } - avcodec_open2(this->codecContext, codec, NULL); -} - -OutputFormat::~OutputFormat() { - avcodec_close(this->codecContext); - avcodec_free_context(&this->codecContext); - av_packet_free(&pkt); - av_frame_free(&frame); - fclose(this->outputFile); -} diff --git a/03-record-sdl/src/OutputFormat.h b/03-record-sdl/src/OutputFormat.h deleted file mode 100644 index 638f91d..0000000 --- a/03-record-sdl/src/OutputFormat.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef OUTPUTFORMAT_H -#define OUTPUTFORMAT_H -#include - -class AVCodec; -class AVFrame; -class AVPacket; -class AVOutputFormat; -class AVStream; -class AVCodecContext; -class FormatContext; -class SwsContext; -/** - * Clase que encapsula lógica la salida de video - * Se recomienda modularizar aun más esta clase, reforzando RAII - */ -class OutputFormat { -public: - // Ctor - OutputFormat(FormatContext& context, const std::string& filename); - // Dtor - ~OutputFormat(); - // Escribe un frame a disco. Utiliza `swsContext` para convertir - // de RGB24 a YUV420p - void writeFrame(const char* data, SwsContext* swsContext); - // Cierra el stream de video - void close(); -private: - // Inicializa frame - void initFrame(); - // Inicializa contexto de codec - void codecContextInit(AVCodec* codec); - FormatContext& context; - AVOutputFormat* avOutputFormat; - AVStream* video_avstream; - AVCodecContext* codecContext; - int currentPts; - FILE* outputFile; - AVFrame* frame; - AVPacket* pkt; -}; -#endif From d2ff0c7be43e36138d9462ab20ba45a4aff1df19 Mon Sep 17 00:00:00 2001 From: camiboj Date: Wed, 12 Jun 2019 10:41:59 -0300 Subject: [PATCH 08/12] Added the necessary classes to make threadpool --- 03-record-sdl/CMakeLists.txt | 3 ++ 03-record-sdl/src/BlockingQueue.cpp | 46 +++++++++++++++++++++++++++ 03-record-sdl/src/BlockingQueue.h | 49 +++++++++++++++++++++++++++++ 03-record-sdl/src/Consumer.cpp | 25 +++++++++++++++ 03-record-sdl/src/Consumer.h | 30 ++++++++++++++++++ 03-record-sdl/src/SwsContext.cpp | 2 ++ 03-record-sdl/src/Thread.cpp | 22 +++++++++++++ 03-record-sdl/src/Thread.h | 28 +++++++++++++++++ 8 files changed, 205 insertions(+) create mode 100644 03-record-sdl/src/BlockingQueue.cpp create mode 100644 03-record-sdl/src/BlockingQueue.h create mode 100644 03-record-sdl/src/Consumer.cpp create mode 100644 03-record-sdl/src/Consumer.h create mode 100644 03-record-sdl/src/Thread.cpp create mode 100644 03-record-sdl/src/Thread.h diff --git a/03-record-sdl/CMakeLists.txt b/03-record-sdl/CMakeLists.txt index aba7011..3b98811 100644 --- a/03-record-sdl/CMakeLists.txt +++ b/03-record-sdl/CMakeLists.txt @@ -23,6 +23,9 @@ add_executable (record-sdl src/CodecContext.cpp src/FormatContext.cpp src/SwsContext.cpp + src/BlockingQueue.cpp + src/Consumer.cpp + src/Thread.cpp src/main.cpp ) diff --git a/03-record-sdl/src/BlockingQueue.cpp b/03-record-sdl/src/BlockingQueue.cpp new file mode 100644 index 0000000..048b3c3 --- /dev/null +++ b/03-record-sdl/src/BlockingQueue.cpp @@ -0,0 +1,46 @@ +// +// Created by camix on 12/06/19. +// + +#include "BlockingQueue.h" +#include + +BlockingQueue::BlockingQueue() { + this->done = false; +} + +BlockingQueue::~BlockingQueue() = default; + +void BlockingQueue::push(std::vector element) { + std::unique_lock lock(this->m); + this->queue.push(element); + this->cond_variable.notify_all(); +} + +void BlockingQueue::close() { + std::vector s; + push(s); +} + +std::vector BlockingQueue::pop() { + std::unique_lock lock(this->m); + + if (this->done) + throw BlockingQueueDoneException(); + + while (this->queue.empty() && ! this->done) { + this->cond_variable.wait(lock); + } + + if (this->done) + throw BlockingQueueDoneException(); + + std::vector data = this->queue.front(); + this->queue.pop(); + if (data.empty()) { + this->done = true; + throw BlockingQueueDoneException(); + } + return data; +} + diff --git a/03-record-sdl/src/BlockingQueue.h b/03-record-sdl/src/BlockingQueue.h new file mode 100644 index 0000000..b4c9918 --- /dev/null +++ b/03-record-sdl/src/BlockingQueue.h @@ -0,0 +1,49 @@ +// +// Created by camix on 12/06/19. +// + +#ifndef FFMPEG_DEMO_BLOCKINGQUEUE_H +#define FFMPEG_DEMO_BLOCKINGQUEUE_H + +#define BQ_CLOSED_EXCEPTION "The queue is closed" + +#include +#include +#include + +class BlockingQueue { +private: + std::queue > queue; + std::mutex m; + std::condition_variable cond_variable; + bool done; + +public: + BlockingQueue(); + ~BlockingQueue(); + + //Guarda un elemento en la cola prioritaria. + void push(std::vector); + + //Saca un elemento de la cola prioritaria. + //Si la cola esta vacia espera a que se ingrese un elemento. + //Si se termino de encolar elementos se lanza una excepcion. + std::vector pop(); + + //Cierra la cola. + void close(); +}; + + + +class BlockingQueueDoneException : public std::exception { + virtual const char* what() const throw () { + std::string message = BQ_CLOSED_EXCEPTION; + return message.c_str(); + } + +public: + explicit BlockingQueueDoneException() = default; +}; + +#endif //FFMPEG_DEMO_BLOCKINGQUEUE_H diff --git a/03-record-sdl/src/Consumer.cpp b/03-record-sdl/src/Consumer.cpp new file mode 100644 index 0000000..fd95f86 --- /dev/null +++ b/03-record-sdl/src/Consumer.cpp @@ -0,0 +1,25 @@ +// +// Created by camix on 12/06/19. +// + +#include "Consumer.h" + + +Consumer::Consumer(BlockingQueue producedFrames, Output& outputVideo, SwsContext* ctx, int i) : + producedFrames(producedFrames), + outputVideo(outputVideo), + ctx(ctx), + i(i) { +} + +void Consumer::run() { + try { + while (true) { + std::vector frame = this->producedFrames.pop(); + outputVideo.writeFrame(frame.data(), ctx); + } + } + catch (BlockingQueueDoneException& e) { + return; + } +} diff --git a/03-record-sdl/src/Consumer.h b/03-record-sdl/src/Consumer.h new file mode 100644 index 0000000..d56e3d3 --- /dev/null +++ b/03-record-sdl/src/Consumer.h @@ -0,0 +1,30 @@ +// +// Created by camix on 12/06/19. +// + +#ifndef FFMPEG_DEMO_CONSUMER_H +#define FFMPEG_DEMO_CONSUMER_H + + +#include "Thread.h" +#include "BlockingQueue.h" +#include "Output.h" + +class Consumer : public Thread { +private: + BlockingQueue& producedFrames; + Output& outputVideo; + SwsContext* ctx; + int i; + +public: + //Recibe una cola bloqueante + Consumer(BlockingQueue producedImages, Output& outputVideo, SwsContext* ctx, int i); + + //Espera la notificación de que hay brainfucks para ejecutar + //y los ejecuta. + virtual void run() override; +}; + + +#endif //FFMPEG_DEMO_CONSUMER_H diff --git a/03-record-sdl/src/SwsContext.cpp b/03-record-sdl/src/SwsContext.cpp index ddc2be9..f4acc91 100644 --- a/03-record-sdl/src/SwsContext.cpp +++ b/03-record-sdl/src/SwsContext.cpp @@ -6,6 +6,8 @@ #include #include #include "SwsContext.h" +#include "BlockingQueue.h" +#include "Consumer.h" const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; diff --git a/03-record-sdl/src/Thread.cpp b/03-record-sdl/src/Thread.cpp new file mode 100644 index 0000000..c00221c --- /dev/null +++ b/03-record-sdl/src/Thread.cpp @@ -0,0 +1,22 @@ +// +// Created by camix on 12/06/19. +// + +#include "Thread.h" + + +Thread::Thread(Thread&& other) { + this->thread = std::move(other.thread); +} +Thread::Thread() = default; +Thread::~Thread() = default; + +void Thread::start() { + this->thread = std::thread(&Thread::run, this); +} + + +void Thread::join() { + this->thread.join(); +} + diff --git a/03-record-sdl/src/Thread.h b/03-record-sdl/src/Thread.h new file mode 100644 index 0000000..84e0bc0 --- /dev/null +++ b/03-record-sdl/src/Thread.h @@ -0,0 +1,28 @@ +// +// Created by camix on 12/06/19. +// + +#ifndef FFMPEG_DEMO_THREAD_H +#define FFMPEG_DEMO_THREAD_H + + +#include + +class Thread { +private: + std::thread thread; + +public: + Thread(); + void start(); + void join(); + virtual void run() = 0; + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + virtual ~Thread(); + Thread(Thread&& other); + Thread& operator=(Thread&& other); +}; + + +#endif //FFMPEG_DEMO_THREAD_H From 839ff33087e4721754a812d4669f8ce7c902af89 Mon Sep 17 00:00:00 2001 From: camiboj Date: Wed, 12 Jun 2019 12:09:54 -0300 Subject: [PATCH 09/12] fighting with threads --- 03-record-sdl/CMakeLists.txt | 4 ++-- 03-record-sdl/src/BlockingQueue.cpp | 6 ++++- 03-record-sdl/src/Constants.h | 12 ++++++++++ 03-record-sdl/src/Consumer.cpp | 22 ++++++++++++------ 03-record-sdl/src/Consumer.h | 16 ++++++++----- 03-record-sdl/src/SwsContext.cpp | 34 ++++++++++++---------------- 03-record-sdl/src/SwsContext.h | 35 ++++++++++------------------- 03-record-sdl/src/Thread.h | 1 - 03-record-sdl/src/main.cpp | 12 ++++++++-- 9 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 03-record-sdl/src/Constants.h diff --git a/03-record-sdl/CMakeLists.txt b/03-record-sdl/CMakeLists.txt index 3b98811..e02b175 100644 --- a/03-record-sdl/CMakeLists.txt +++ b/03-record-sdl/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.6) project(ffmpeg-sdl) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -std=c++11") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -std=c++11 -pthread") add_library(sdldemo src/Area.cpp @@ -31,4 +31,4 @@ add_executable (record-sdl target_link_libraries(record-sdl avformat avcodec avutil swscale sdldemo SDL2 SDL2_image) -install(FILES assets/cat.gif DESTINATION ${CMAKE_BINARY_DIR}) +install(FILES assets/cat.gif DESTINATION ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/03-record-sdl/src/BlockingQueue.cpp b/03-record-sdl/src/BlockingQueue.cpp index 048b3c3..f1c37d8 100644 --- a/03-record-sdl/src/BlockingQueue.cpp +++ b/03-record-sdl/src/BlockingQueue.cpp @@ -4,6 +4,7 @@ #include "BlockingQueue.h" #include +#include BlockingQueue::BlockingQueue() { this->done = false; @@ -15,6 +16,7 @@ void BlockingQueue::push(std::vector element) { std::unique_lock lock(this->m); this->queue.push(element); this->cond_variable.notify_all(); + std::cerr << "Notificanding: " << std::endl; } void BlockingQueue::close() { @@ -27,9 +29,11 @@ std::vector BlockingQueue::pop() { if (this->done) throw BlockingQueueDoneException(); - + int i = 0; while (this->queue.empty() && ! this->done) { + std::cerr << "Entro al while de pop por vez: " << i << std::endl; this->cond_variable.wait(lock); + i++; } if (this->done) diff --git a/03-record-sdl/src/Constants.h b/03-record-sdl/src/Constants.h new file mode 100644 index 0000000..cad0439 --- /dev/null +++ b/03-record-sdl/src/Constants.h @@ -0,0 +1,12 @@ +// +// Created by camix on 12/06/19. +// + +#ifndef FFMPEG_DEMO_CONSTANTS_H +#define FFMPEG_DEMO_CONSTANTS_H + +#define BUFFER_WIDTH 352 +#define BUFFER_HEIGHT 288 + + +#endif //FFMPEG_DEMO_CONSTANTS_H diff --git a/03-record-sdl/src/Consumer.cpp b/03-record-sdl/src/Consumer.cpp index fd95f86..ae8da73 100644 --- a/03-record-sdl/src/Consumer.cpp +++ b/03-record-sdl/src/Consumer.cpp @@ -1,25 +1,33 @@ // // Created by camix on 12/06/19. // - +extern "C" { +#include +} #include "Consumer.h" +#include "Constants.h" - -Consumer::Consumer(BlockingQueue producedFrames, Output& outputVideo, SwsContext* ctx, int i) : +Consumer::Consumer(BlockingQueue& producedFrames, std::string& filename) : producedFrames(producedFrames), - outputVideo(outputVideo), - ctx(ctx), - i(i) { + videoOutput(context, filename), + ctx(sws_getContext(BUFFER_WIDTH, BUFFER_HEIGHT, + AV_PIX_FMT_RGB24, BUFFER_WIDTH, BUFFER_HEIGHT, + AV_PIX_FMT_YUV420P, 0, 0, 0, 0)) { } void Consumer::run() { try { while (true) { std::vector frame = this->producedFrames.pop(); - outputVideo.writeFrame(frame.data(), ctx); + videoOutput.writeFrame(frame.data(), ctx); } } catch (BlockingQueueDoneException& e) { return; } } + +Consumer::~Consumer() { + videoOutput.close(); + sws_freeContext(ctx); +} \ No newline at end of file diff --git a/03-record-sdl/src/Consumer.h b/03-record-sdl/src/Consumer.h index d56e3d3..35adde6 100644 --- a/03-record-sdl/src/Consumer.h +++ b/03-record-sdl/src/Consumer.h @@ -9,21 +9,25 @@ #include "Thread.h" #include "BlockingQueue.h" #include "Output.h" +#include "FormatContext.h" class Consumer : public Thread { private: BlockingQueue& producedFrames; - Output& outputVideo; + FormatContext context; + Output videoOutput; + // You need it to perform scaling/conversion operations using. SwsContext* ctx; - int i; public: - //Recibe una cola bloqueante - Consumer(BlockingQueue producedImages, Output& outputVideo, SwsContext* ctx, int i); + /// first you need to Initialize libavformat and register all the muxers, demuxers and + /// protocols with av_register_all(); + Consumer(BlockingQueue& producedImages, std::string& filename); + + ~Consumer(); - //Espera la notificación de que hay brainfucks para ejecutar - //y los ejecuta. virtual void run() override; + }; diff --git a/03-record-sdl/src/SwsContext.cpp b/03-record-sdl/src/SwsContext.cpp index f4acc91..d9e18af 100644 --- a/03-record-sdl/src/SwsContext.cpp +++ b/03-record-sdl/src/SwsContext.cpp @@ -6,28 +6,21 @@ #include #include #include "SwsContext.h" -#include "BlockingQueue.h" -#include "Consumer.h" - -const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; - -/// first you need to Initialize libavformat and register all the muxers, demuxers and -/// protocols with av_register_all(); -SwsContext::SwsContext(std::string filename) : - videoOutput(context, filename), - ctx(sws_getContext(BUFFER_WIDTH, BUFFER_HEIGHT, - AV_PIX_FMT_RGB24, BUFFER_WIDTH, BUFFER_HEIGHT, - AV_PIX_FMT_YUV420P, 0, 0, 0, 0)) , +#include "Constants.h" + + + +SwsContext::SwsContext(BlockingQueue& producedFrames) : // Este buffer tiene el tamaño de la sección de SDL que quiero leer, multiplico // x3 por la cantidad de bytes (8R,8G,8B) // A sws parece que no le gusta este tamaño - dataBuffer(BUFFER_WIDTH*BUFFER_HEIGHT*3) {} + dataBuffer(BUFFER_WIDTH*BUFFER_HEIGHT*3), + producedFrames(producedFrames) + {} + +SwsContext::~SwsContext() = default; + -SwsContext::~SwsContext() { - videoOutput.close(); - // Libero escalador - sws_freeContext(ctx); -} void SwsContext::write(SdlWindow& window) { // Obtengo los bytes de la textura en el buffer @@ -36,6 +29,7 @@ void SwsContext::write(SdlWindow& window) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "RendererReadPixels error", SDL_GetError(), NULL); throw SwsContextRendererReadPixelsException(); } - //operacion lenta que va a tenre que estar en otro thread - videoOutput.writeFrame(dataBuffer.data(), ctx); + //operacion lenta que pasó a otro thread: + //videoOutput.writeFrame(dataBuffer.data(), ctx); + producedFrames.push(dataBuffer); } diff --git a/03-record-sdl/src/SwsContext.h b/03-record-sdl/src/SwsContext.h index e5f609f..24d6e6f 100644 --- a/03-record-sdl/src/SwsContext.h +++ b/03-record-sdl/src/SwsContext.h @@ -13,6 +13,8 @@ #include "FormatContext.h" #include "Output.h" #include "SdlWindow.h" +#include "BlockingQueue.h" +#include "Consumer.h" extern "C" { #include @@ -20,37 +22,24 @@ extern "C" { } -class SwsContextException : public std::exception { -protected: - std::string message; -public: - SwsContextException() = default; - virtual const char *what() const throw() { - return this->message.c_str(); - } -}; - -class SwsContextRendererReadPixelsException : public SwsContextException { -public: - SwsContextRendererReadPixelsException() { - message = RRP_SWS_CONTEXT_EXC; - } -}; - -// RAII wrapper for struct SwsContext class SwsContext { private: - FormatContext context; - Output videoOutput; - // You need it to perform scaling/conversion operations using. - SwsContext* ctx; std::vector dataBuffer; + BlockingQueue& producedFrames; public: - explicit SwsContext(std::string filename); + SwsContext(BlockingQueue& producedFrames); ~SwsContext(); void write(SdlWindow& window); }; +class SwsContextRendererReadPixelsException : public std::exception { +public: + SwsContextRendererReadPixelsException() = default; + virtual const char *what() const throw() { + return RRP_SWS_CONTEXT_EXC; + } +}; + #endif //FFMPEG_DEMO_SWSCONTEXT_H diff --git a/03-record-sdl/src/Thread.h b/03-record-sdl/src/Thread.h index 84e0bc0..431d34e 100644 --- a/03-record-sdl/src/Thread.h +++ b/03-record-sdl/src/Thread.h @@ -21,7 +21,6 @@ class Thread { Thread& operator=(const Thread&) = delete; virtual ~Thread(); Thread(Thread&& other); - Thread& operator=(Thread&& other); }; diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index a9a197c..190923b 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -11,6 +11,7 @@ extern "C" { #include "FormatContext.h" #include "Output.h" #include "SwsContext.h" +#include "Consumer.h" const int BUFFER_WIDTH = 352, BUFFER_HEIGHT = 288; @@ -22,8 +23,11 @@ int main(int argc, char** argv){ return -1; } try { + BlockingQueue queue; av_register_all(); - SwsContext ctx((std::string(argv[1]))); + SwsContext ctx(queue); + std::string filename = argv[1]; + Consumer consumer(queue, filename); SdlWindow window(800, 600); @@ -38,7 +42,8 @@ int main(int argc, char** argv){ SDL_Texture* videoTexture = SDL_CreateTexture(window.getRenderer(), SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, BUFFER_WIDTH, BUFFER_HEIGHT); - + consumer.run(); + std::cerr << "Ya puse a correr el thread consumidor" << std::endl; while (running) { // Muevo textura con flechas direccionales handleSDLEvent(x, y, running); @@ -57,6 +62,9 @@ int main(int argc, char** argv){ ctx.write(window); } + + queue.close(); + consumer.join(); } catch (std::exception& e) { std::cout << e.what() << std::endl; return 1; From 856ed5c0ec1dd1fc5bf4d7f54f1e7e61262b6a3a Mon Sep 17 00:00:00 2001 From: camiboj Date: Wed, 12 Jun 2019 12:18:58 -0300 Subject: [PATCH 10/12] you were fighting with yourself stupid girl. Of course you must call 'start'. 'run' is a sequential function. 'start' is the one that make possible to execute 'run' in a thread --- 03-record-sdl/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index 190923b..80ca8b0 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -42,7 +42,7 @@ int main(int argc, char** argv){ SDL_Texture* videoTexture = SDL_CreateTexture(window.getRenderer(), SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, BUFFER_WIDTH, BUFFER_HEIGHT); - consumer.run(); + consumer.start(); std::cerr << "Ya puse a correr el thread consumidor" << std::endl; while (running) { // Muevo textura con flechas direccionales From f31bbc3cca2ca8ac8d95b67509890a24adeed2c2 Mon Sep 17 00:00:00 2001 From: camiboj Date: Wed, 12 Jun 2019 12:46:48 -0300 Subject: [PATCH 11/12] deleted prints --- 03-record-sdl/src/BlockingQueue.cpp | 2 -- 03-record-sdl/src/main.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/03-record-sdl/src/BlockingQueue.cpp b/03-record-sdl/src/BlockingQueue.cpp index f1c37d8..d805001 100644 --- a/03-record-sdl/src/BlockingQueue.cpp +++ b/03-record-sdl/src/BlockingQueue.cpp @@ -16,7 +16,6 @@ void BlockingQueue::push(std::vector element) { std::unique_lock lock(this->m); this->queue.push(element); this->cond_variable.notify_all(); - std::cerr << "Notificanding: " << std::endl; } void BlockingQueue::close() { @@ -31,7 +30,6 @@ std::vector BlockingQueue::pop() { throw BlockingQueueDoneException(); int i = 0; while (this->queue.empty() && ! this->done) { - std::cerr << "Entro al while de pop por vez: " << i << std::endl; this->cond_variable.wait(lock); i++; } diff --git a/03-record-sdl/src/main.cpp b/03-record-sdl/src/main.cpp index 80ca8b0..20b815b 100644 --- a/03-record-sdl/src/main.cpp +++ b/03-record-sdl/src/main.cpp @@ -43,7 +43,6 @@ int main(int argc, char** argv){ SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, BUFFER_WIDTH, BUFFER_HEIGHT); consumer.start(); - std::cerr << "Ya puse a correr el thread consumidor" << std::endl; while (running) { // Muevo textura con flechas direccionales handleSDLEvent(x, y, running); From 0ceb6fa8cd315b6380a3f729e7cc3d29694faf55 Mon Sep 17 00:00:00 2001 From: camiboj Date: Thu, 13 Jun 2019 17:26:50 -0300 Subject: [PATCH 12/12] instructions to compile and execute the important one --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 64acccf..fb82fe9 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ mkdir build cd build cmake .. make -j +cd 03-record-sdl +./record-sdl out.mkv +mplayer out.mkv // Se recomienda `mplayer` + // para poder visualizar el mismo. ~~~ Con *N cores* como el número de procesos en paralelo para compilar.