Skip to content
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
build
*.kdev4
.idea
cmake-build-debug/
26 changes: 17 additions & 9 deletions 03-record-sdl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@ 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
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
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/SwsContext.cpp
src/BlockingQueue.cpp
src/Consumer.cpp
src/Thread.cpp
src/main.cpp
)

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})
48 changes: 48 additions & 0 deletions 03-record-sdl/src/BlockingQueue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Created by camix on 12/06/19.
//

#include "BlockingQueue.h"
#include <vector>
#include <iostream>

BlockingQueue::BlockingQueue() {
this->done = false;
}

BlockingQueue::~BlockingQueue() = default;

void BlockingQueue::push(std::vector<char> element) {
std::unique_lock<std::mutex> lock(this->m);
this->queue.push(element);
this->cond_variable.notify_all();
}

void BlockingQueue::close() {
std::vector<char> s;
push(s);
}

std::vector<char> BlockingQueue::pop() {
std::unique_lock<std::mutex> lock(this->m);

if (this->done)
throw BlockingQueueDoneException();
int i = 0;
while (this->queue.empty() && ! this->done) {
this->cond_variable.wait(lock);
i++;
}

if (this->done)
throw BlockingQueueDoneException();

std::vector<char> data = this->queue.front();
this->queue.pop();
if (data.empty()) {
this->done = true;
throw BlockingQueueDoneException();
}
return data;
}

49 changes: 49 additions & 0 deletions 03-record-sdl/src/BlockingQueue.h
Original file line number Diff line number Diff line change
@@ -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 <queue>
#include <mutex>
#include <condition_variable>

class BlockingQueue {
private:
std::queue <std::vector<char>> 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<char>);

//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<char> 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
51 changes: 51 additions & 0 deletions 03-record-sdl/src/CodecContext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Created by camix on 10/06/19.
//

extern "C" {
#include <libavutil/opt.h>
}
#include "CodecContext.h"

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;
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;
}
51 changes: 51 additions & 0 deletions 03-record-sdl/src/CodecContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Created by camix on 10/06/19.
//

#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 <libavcodec/avcodec.h>
}

#include <string>

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:
AVCodecContext* codecContext;

public:
explicit CodecContext(AVCodec *codec);

~CodecContext();

AVCodecContext *get();

int getHeight();

int getWidth();

int getPixFmt();
};


#endif //FFMPEG_DEMO_CODECCONTEXT_H
12 changes: 12 additions & 0 deletions 03-record-sdl/src/Constants.h
Original file line number Diff line number Diff line change
@@ -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
33 changes: 33 additions & 0 deletions 03-record-sdl/src/Consumer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Created by camix on 12/06/19.
//
extern "C" {
#include <libswscale/swscale.h>
}
#include "Consumer.h"
#include "Constants.h"

Consumer::Consumer(BlockingQueue& producedFrames, std::string& filename) :
producedFrames(producedFrames),
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<char> frame = this->producedFrames.pop();
videoOutput.writeFrame(frame.data(), ctx);
}
}
catch (BlockingQueueDoneException& e) {
return;
}
}

Consumer::~Consumer() {
videoOutput.close();
sws_freeContext(ctx);
}
34 changes: 34 additions & 0 deletions 03-record-sdl/src/Consumer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// 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"
#include "FormatContext.h"

class Consumer : public Thread {
private:
BlockingQueue& producedFrames;
FormatContext context;
Output videoOutput;
// You need it to perform scaling/conversion operations using.
SwsContext* ctx;

public:
/// 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();

virtual void run() override;

};


#endif //FFMPEG_DEMO_CONSUMER_H
41 changes: 41 additions & 0 deletions 03-record-sdl/src/FormatOutput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Created by camix on 10/06/19.
//
extern "C" {
#include <libavutil/opt.h>
}

#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() {

}
Loading