Skip to content

Commit

Permalink
Now successfully remuxes input into a file
Browse files Browse the repository at this point in the history
  • Loading branch information
edman007 committed May 23, 2020
1 parent 4122f39 commit 59a96b1
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 17 deletions.
5 changes: 4 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ AM_CXXFLAGS = -std=gnu++11 -Wall
PRGM = chiton

bin_PROGRAMS = $(PRGM)
chiton_SOURCES = main.cpp config.cpp config_parser.cpp database.cpp mariadb.cpp util.cpp mariadb_result.cpp camera.cpp stream_unwrap.cpp file_manager.cpp
chiton_SOURCES = main.cpp config.cpp config_parser.cpp database.cpp mariadb.cpp \
util.cpp mariadb_result.cpp camera.cpp stream_unwrap.cpp file_manager.cpp \
chiton_ffmpeg.cpp stream_writer.cpp

chiton_CXXFLAGS = $(AM_CXXFLAGS)

13 changes: 13 additions & 0 deletions camera.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "camera.hpp"
#include "util.hpp"
#include "stream_writer.hpp"

Camera::Camera(int camera, Database& db) : id(camera), db(db), stream(cfg), fm(db, cfg) {
//load the config
Expand All @@ -25,6 +26,18 @@ void Camera::run(void){
struct timeval start;
Util::get_videotime(start);
std::string new_output = fm.get_next_path(file_id, id, start);

StreamWriter out = StreamWriter(cfg, new_output, stream);
out.open();

AVPacket pkt;
while (stream.get_next_frame(pkt)){
out.write(pkt);//log it

stream.unref_frame(pkt);
}
out.close();

}

void Camera::stop(void){
Expand Down
6 changes: 6 additions & 0 deletions chiton_ffmpeg.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "chiton_ffmpeg.hpp"

void load_ffmpeg(void){
av_log_set_level(AV_LOG_DEBUG);
//probably should call av_log_set_callback ( void(*)(void *, int, const char *, va_list) callback )
}
9 changes: 9 additions & 0 deletions chiton_ffmpeg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,13 @@ av_always_inline char* av_err2str(int errnum)
}
#endif


//make AVRounding valid in bitwise operations
inline AVRounding operator|(AVRounding a, AVRounding b)
{
return static_cast<AVRounding>(static_cast<int>(a) | static_cast<int>(b));
}

void load_ffmpeg(void);

#endif
3 changes: 3 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <unistd.h>
#include <thread>
#include <stdlib.h>
#include "chiton_ffmpeg.hpp"

static char timezone_env[256];//if timezone is changed from default, we need to store it in memory for putenv()

Expand Down Expand Up @@ -43,6 +44,8 @@ int main (int argc, char **argv){

//load system config
load_sys_cfg(cfg);

load_ffmpeg();

//Launch all cameras
res = db.query("SELECT camera FROM config WHERE camera IS NOT NULL AND name = 'active' AND value = '1' GROUP BY camera");
Expand Down
78 changes: 65 additions & 13 deletions stream_unwrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,98 @@


StreamUnwrap::StreamUnwrap(Config& cfg) : cfg(cfg) {

input_format_context = NULL;

}

StreamUnwrap::~StreamUnwrap(){
avformat_close_input(&input_format_context);

}

bool StreamUnwrap::connect(void) {
const std::string& url = cfg.get_value("video-url");
if (!url.compare("")){
Util::log_msg(LOG_ERROR, "Camera was not supplied with a URL" + url);
return false;
}
load_avformat();

AVCodecContext *avctx;
AVCodec *input_codec;
AVFormatContext *input_format_context = NULL;

int error;
/* Open the input file to read from it. */
if ((error = avformat_open_input(&input_format_context, url.c_str(), NULL, NULL)) < 0) {
Util::log_msg(LOG_ERROR, "Could not open camera url '" + url + "' (error '" + std::string(av_err2str(error)) +")n");
Util::log_msg(LOG_ERROR, "Could not open camera url '" + url + "' (error '" + std::string(av_err2str(error)) +")");
input_format_context = NULL;
return false;
}

/* Get information on the input file (number of streams etc.). */
if ((error = avformat_find_stream_info(input_format_context, NULL)) < 0) {
Util::log_msg(LOG_ERROR, "Could not open find stream info (error '" + std::string(av_err2str(error)) + "')n");
Util::log_msg(LOG_ERROR, "Could not open find stream info (error '" + std::string(av_err2str(error)) + "')");
avformat_close_input(&input_format_context);
return error;
return false;
}
Util::log_msg(LOG_INFO, "Video Stream has " + std::to_string(input_format_context->nb_streams) + " streams");
LINFO("Video Stream has " + std::to_string(input_format_context->nb_streams) + " streams");

/*
if (!(input_codec = avcodec_find_decoder(input_format_context->streams[0]->codecpar->codec_id))) {
LERROR("Could not find input codec\n");
avformat_close_input(input_format_context);
return false;
}
*/

return true;
}

bool StreamUnwrap::open_output(void){
AVFormatContext* StreamUnwrap::get_format_context(void){
return input_format_context;
}

unsigned int StreamUnwrap::get_stream_count(void){
return input_format_context->nb_streams;
}

AVCodecContext* StreamUnwrap::alloc_decode_context(unsigned int stream){
AVCodecContext *avctx;
AVCodec *input_codec;
int error;

if (stream >= get_stream_count()){
LERROR("Attempted to access stream that doesn't exist");
return NULL;
}

/* Allocate a new decoding context. */
avctx = avcodec_alloc_context3(input_codec);
if (!avctx) {
fprintf(stderr, "Could not allocate a decoding context");
return NULL;
}

/* Initialize the stream parameters with demuxer information. */
error = avcodec_parameters_to_context(avctx, input_format_context->streams[stream]->codecpar);
if (error < 0) {
avcodec_free_context(&avctx);
return NULL;
}

/* Open the decoder. */
if ((error = avcodec_open2(avctx, input_codec, NULL)) < 0) {
LERROR("Could not open input codec (error '" + std::string(av_err2str(error)) + "')");
avcodec_free_context(&avctx);
return NULL;
}

/* Save the decoder context for easier access later. */
return avctx;

}
void StreamUnwrap::load_avformat(void){
av_log_set_level(AV_LOG_DEBUG);
//probably should call av_log_set_callback ( void(*)(void *, int, const char *, va_list) callback )

bool StreamUnwrap::get_next_frame(AVPacket &packet){
return 0 == av_read_frame(input_format_context, &packet);
}

void StreamUnwrap::unref_frame(AVPacket &packet){
av_packet_unref(&packet);
}
14 changes: 11 additions & 3 deletions stream_unwrap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define __STREAM_UNWRAP_HPP__

#include "chiton_config.hpp"
#include "chiton_ffmpeg.hpp"

class StreamUnwrap {
/*
Expand All @@ -10,16 +11,23 @@ class StreamUnwrap {
*/
public:
StreamUnwrap(Config& cfg);
~StreamUnwrap();

bool connect(void);//returns true on success
AVCodecContext* get_codec_context(void);
AVFormatContext* get_format_context(void);
unsigned int get_stream_count(void);
AVCodecContext* alloc_decode_context(unsigned int stream);//alloc and return the codec context for the stream, caller must free it
bool get_next_frame(AVPacket &packet);//writes the next frame out to packet, returns true on success, false on error (end of file)
void unref_frame(AVPacket &packet);//free resources from frame
private:
const std::string url;//the URL of the camera we are connecting to
Config& cfg;

bool open_output(void);

void load_avformat(void);


AVFormatContext *input_format_context;
AVPacket pkt;
};

#endif
140 changes: 140 additions & 0 deletions stream_writer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "stream_writer.hpp"
#include "util.hpp"
#include "chiton_ffmpeg.hpp"

bool StreamWriter::open(void){
//need to free input_ctx
/*
AVCodecContext *input_ctx = unwrap.alloc_decode_context(0);
if (!input_ctx){
return false;
}
*/

int error;

avformat_alloc_output_context2(&output_format_context, NULL, NULL, path.c_str());
if (!output_format_context) {
LERROR("Could not create output context");
error = AVERROR_UNKNOWN;
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}

stream_mapping_size = unwrap.get_stream_count();
stream_mapping = NULL;
stream_mapping = (int*)av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
error = AVERROR(ENOMEM);
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}


AVOutputFormat *ofmt = output_format_context->oformat;
int stream_index = 0;
for (unsigned int i = 0; i < unwrap.get_stream_count(); i++) {
AVStream *out_stream;
AVStream *in_stream = unwrap.get_format_context()->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;

if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}

stream_mapping[i] = stream_index++;

out_stream = avformat_new_stream(output_format_context, NULL);
if (!out_stream) {
LERROR("Failed allocating output stream");
error = AVERROR_UNKNOWN;
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}

error = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (error < 0) {
LERROR("Failed to copy codec parameters\n");
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}
out_stream->codecpar->codec_tag = 0;
}
av_dump_format(output_format_context, 0, path.c_str(), 1);

if (!(ofmt->flags & AVFMT_NOFILE)) {
error = avio_open(&output_format_context->pb, path.c_str(), AVIO_FLAG_WRITE);
if (error < 0) {
LERROR("Could not open output file '" + path + "'");
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}
}

error = avformat_write_header(output_format_context, NULL);
if (error < 0) {
LERROR("Error occurred when opening output file");
LERROR("Error occurred: " + std::string(av_err2str(error)));
return false;
}
return true;

}


StreamWriter::~StreamWriter(){
/* close output */
if (output_format_context && !(output_format_context->flags & AVFMT_NOFILE))
avio_closep(&output_format_context->pb);
avformat_free_context(output_format_context);

av_freep(&stream_mapping);
}

void StreamWriter::close(void){
//flush it...
if (0 > av_interleaved_write_frame(output_format_context, NULL)){
LERROR("Error flushing muxing output");
}

av_write_trailer(output_format_context);
}

bool StreamWriter::write(const AVPacket &packet){
AVStream *in_stream, *out_stream;
AVPacket out_pkt;
if (av_packet_ref(&out_pkt, &packet)){
LERROR("Could not allocate new output packet for writing");
return false;
}

in_stream = unwrap.get_format_context()->streams[out_pkt.stream_index];
if (out_pkt.stream_index >= stream_mapping_size ||
stream_mapping[out_pkt.stream_index] < 0) {
av_packet_unref(&out_pkt);
return true;//we processed the stream we don't care about
}

out_pkt.stream_index = stream_mapping[out_pkt.stream_index];
out_stream = output_format_context->streams[out_pkt.stream_index];


/* copy packet */
out_pkt.pts = av_rescale_q_rnd(out_pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
out_pkt.dts = av_rescale_q_rnd(out_pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
out_pkt.duration = av_rescale_q(out_pkt.duration, in_stream->time_base, out_stream->time_base);
out_pkt.pos = -1;


int ret = av_interleaved_write_frame(output_format_context, &out_pkt);
if (ret < 0) {
LERROR("Error muxing packet");
return false;
}

av_packet_unref(&out_pkt);
return true;
}
24 changes: 24 additions & 0 deletions stream_writer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef __STREAM_WRITER_HPP__
#define __STREAM_WRITER_HPP__
#include "chiton_config.hpp"
#include "stream_unwrap.hpp"

class StreamWriter {
public:
StreamWriter(Config cfg, std::string path, StreamUnwrap &unwrap) : cfg(cfg), path(path), unwrap(unwrap) {};
~StreamWriter();

bool open();//open the file for writing, returns true on success
void close(void);
bool write(const AVPacket &pkt);//write the packet to the file
private:
Config &cfg;
std::string path;
StreamUnwrap &unwrap;

AVFormatContext *output_format_context = NULL;
int stream_mapping_size = 0;
int *stream_mapping = NULL;

};
#endif

0 comments on commit 59a96b1

Please sign in to comment.