diff --git a/Makefile.am b/Makefile.am index 67b3b78035..576c2c17f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -585,6 +585,8 @@ janus_pp_rec_SOURCES = \ postprocessing/pp-h264.h \ postprocessing/pp-av1.c \ postprocessing/pp-av1.h \ + postprocessing/pp-avformat.c \ + postprocessing/pp-avformat.h \ postprocessing/pp-h265.c \ postprocessing/pp-h265.h \ postprocessing/pp-opus.c \ diff --git a/postprocessing/pp-av1.c b/postprocessing/pp-av1.c index b2139c7b60..8ad29c5db6 100644 --- a/postprocessing/pp-av1.c +++ b/postprocessing/pp-av1.c @@ -19,42 +19,13 @@ #include #include -#include -#include - +#include "pp-avformat.h" #include "pp-av1.h" #include "../debug.h" - -#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ - (LIBAVCODEC_VERSION_MAJOR > major || \ - (LIBAVCODEC_VERSION_MAJOR == major && \ - LIBAVCODEC_VERSION_MINOR >= minor)) - -#if LIBAVCODEC_VER_AT_LEAST(51, 42) -#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P -#endif - -#if LIBAVCODEC_VER_AT_LEAST(56, 56) -#ifndef CODEC_FLAG_GLOBAL_HEADER -#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER -#endif -#ifndef FF_INPUT_BUFFER_PADDING_SIZE -#define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE -#endif -#endif - -#if LIBAVCODEC_VER_AT_LEAST(57, 14) -#define USE_CODECPAR -#endif - - /* MP4 output */ static AVFormatContext *fctx; static AVStream *vStream; -#ifdef USE_CODECPAR -static AVCodecContext *vEncoder; -#endif static uint16_t max_width = 0, max_height = 0; int fps = 0; @@ -65,98 +36,30 @@ int janus_pp_av1_create(char *destination, char *metadata, gboolean faststart) { #if !LIBAVCODEC_VER_AT_LEAST(57, 25) JANUS_LOG(LOG_ERR, "This version of libavcodec doesn't support AV1...\n"); return -1; -#endif - /* Setup FFmpeg */ -#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) - av_register_all(); -#endif - /* Adjust logging to match the postprocessor's */ - av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : - (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : - (janus_log_level == LOG_ERR ? AV_LOG_ERROR : - (janus_log_level == LOG_WARN ? AV_LOG_WARNING : - (janus_log_level == LOG_INFO ? AV_LOG_INFO : - (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); +#else /* MP4 output */ - fctx = avformat_alloc_context(); + fctx = janus_pp_create_avformatcontext("mp4", metadata, destination); if(fctx == NULL) { JANUS_LOG(LOG_ERR, "Error allocating context\n"); return -1; } - /* We save the metadata part as a comment (see #1189) */ - if(metadata) - av_dict_set(&fctx->metadata, "comment", metadata, 0); - fctx->oformat = av_guess_format("mp4", NULL, NULL); - if(fctx->oformat == NULL) { - JANUS_LOG(LOG_ERR, "Error guessing format\n"); - return -1; - } - char filename[1024]; - snprintf(filename, sizeof(filename), "%s", destination); -#ifdef USE_CODECPAR -#if LIBAVCODEC_VER_AT_LEAST(57, 25) - AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AV1); -#else - if(!codec) { - /* Error opening video codec */ - JANUS_LOG(LOG_ERR, "Encoder not available\n"); - return -1; - } -#endif - fctx->video_codec = codec; - fctx->oformat->video_codec = codec->id; - vStream = avformat_new_stream(fctx, codec); - vStream->id = fctx->nb_streams-1; - vEncoder = avcodec_alloc_context3(codec); - vEncoder->width = max_width; - vEncoder->height = max_height; - vEncoder->time_base = (AVRational){ 1, fps }; - vEncoder->pix_fmt = AV_PIX_FMT_YUV420P; - vEncoder->flags |= CODEC_FLAG_GLOBAL_HEADER; - vEncoder->strict_std_compliance = -2; - if(avcodec_open2(vEncoder, codec, NULL) < 0) { - /* Error opening video codec */ - JANUS_LOG(LOG_ERR, "Encoder error\n"); - return -1; - } - avcodec_parameters_from_context(vStream->codecpar, vEncoder); -#else - vStream = avformat_new_stream(fctx, 0); + + vStream = janus_pp_new_video_avstream(fctx, AV_CODEC_ID_AV1, max_width, max_height); if(vStream == NULL) { JANUS_LOG(LOG_ERR, "Error adding stream\n"); return -1; } -#if LIBAVCODEC_VER_AT_LEAST(53, 21) - avcodec_get_context_defaults3(vStream->codec, AVMEDIA_TYPE_VIDEO); -#else - avcodec_get_context_defaults2(vStream->codec, AVMEDIA_TYPE_VIDEO); -#endif -#if LIBAVCODEC_VER_AT_LEAST(57, 25) - vStream->codec->codec_id = AV_CODEC_ID_AV1; -#endif - vStream->codec->codec_type = AVMEDIA_TYPE_VIDEO; - vStream->codec->time_base = (AVRational){1, fps}; - vStream->time_base = (AVRational){1, 90000}; - vStream->codec->width = max_width; - vStream->codec->height = max_height; - vStream->codec->pix_fmt = PIX_FMT_YUV420P; - //~ if (fctx->flags & AVFMT_GLOBALHEADER) - vStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif + AVDictionary *options = NULL; if(faststart) av_dict_set(&options, "movflags", "+faststart", 0); - int res = avio_open2(&fctx->pb, filename, AVIO_FLAG_WRITE, NULL, &options); - if(res < 0) { - JANUS_LOG(LOG_ERR, "Error opening file for output (%d)\n", res); - return -1; - } if(avformat_write_header(fctx, &options) < 0) { JANUS_LOG(LOG_ERR, "Error writing header\n"); return -1; } return 0; +#endif } /* Helper to decode a leb128 integer */ @@ -422,6 +325,7 @@ int janus_pp_av1_process(FILE *file, janus_pp_frame_packet *list, int *working) int len = 0, frameLen = 0, total = 0, dataLen = 0; int keyFrame = 0; gboolean keyframe_found = FALSE; + AVPacket *packet = av_packet_alloc(); while(*working && tmp != NULL) { keyFrame = 0; @@ -542,21 +446,20 @@ int janus_pp_av1_process(FILE *file, janus_pp_frame_packet *list, int *working) total += frameLen; JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Saving frame: %d (tot=%d)\n", tmp->ts, frameLen, total); - AVPacket packet; - av_init_packet(&packet); - packet.stream_index = 0; - packet.data = received_frame; - packet.size = frameLen; + av_packet_unref(packet); + packet->stream_index = 0; + packet->data = received_frame; + packet->size = frameLen; if(keyFrame) - packet.flags |= AV_PKT_FLAG_KEY; + packet->flags |= AV_PKT_FLAG_KEY; /* First we save to the file... */ - packet.dts = tmp->ts-list->ts; - packet.pts = tmp->ts-list->ts; + packet->dts = tmp->ts-list->ts; + packet->pts = tmp->ts-list->ts; JANUS_LOG(LOG_HUGE, "%"SCNu64" - %"SCNu64" --> %"SCNu64"\n", - tmp->ts, list->ts, packet.pts); + tmp->ts, list->ts, packet->pts); if(fctx) { - int res = av_write_frame(fctx, &packet); + int res = av_write_frame(fctx, packet); if(res < 0) { JANUS_LOG(LOG_ERR, "Error writing video frame to file... (error %d)\n", res); } @@ -564,6 +467,7 @@ int janus_pp_av1_process(FILE *file, janus_pp_frame_packet *list, int *working) } tmp = tmp->next; } + av_packet_free(&packet); g_free(received_frame); g_free(obu_data); g_free(start); @@ -572,23 +476,9 @@ int janus_pp_av1_process(FILE *file, janus_pp_frame_packet *list, int *working) /* Close MP4 file */ void janus_pp_av1_close(void) { - if(fctx != NULL) - av_write_trailer(fctx); -#ifdef USE_CODECPAR - if(vEncoder != NULL) - avcodec_close(vEncoder); -#else - if(vStream != NULL && vStream->codec != NULL) - avcodec_close(vStream->codec); -#endif - if(fctx != NULL && fctx->streams[0] != NULL) { -#ifndef USE_CODECPAR - av_free(fctx->streams[0]->codec); -#endif - av_free(fctx->streams[0]); - } if(fctx != NULL) { + av_write_trailer(fctx); avio_close(fctx->pb); - av_free(fctx); + avformat_free_context(fctx); } } diff --git a/postprocessing/pp-avformat.c b/postprocessing/pp-avformat.c new file mode 100644 index 0000000000..6d13575a98 --- /dev/null +++ b/postprocessing/pp-avformat.c @@ -0,0 +1,92 @@ +/*! \file pp-avformat.c + * \copyright GNU General Public License v3 + * + * \ingroup postprocessing + * \ref postprocessing + */ + +#include "pp-avformat.h" + +void janus_pp_setup_avformat(void) { + /* Setup FFmpeg */ +#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) + av_register_all(); +#endif + /* Adjust logging to match the postprocessor's */ + av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : + (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : + (janus_log_level == LOG_ERR ? AV_LOG_ERROR : + (janus_log_level == LOG_WARN ? AV_LOG_WARNING : + (janus_log_level == LOG_INFO ? AV_LOG_INFO : + (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); + +} + +AVFormatContext *janus_pp_create_avformatcontext(const char *format, const char *metadata, const char *destination) { + janus_pp_setup_avformat(); + + AVFormatContext *ctx = avformat_alloc_context(); + if(!ctx) + return NULL; + + /* We save the metadata part as a comment (see #1189) */ + if(metadata) + av_dict_set(&ctx->metadata, "comment", metadata, 0); + + ctx->oformat = av_guess_format(format, NULL, NULL); + if(ctx->oformat == NULL) { + JANUS_LOG(LOG_ERR, "Error guessing format\n"); + avformat_free_context(ctx); + return NULL; + } + + int res = avio_open(&ctx->pb, destination, AVIO_FLAG_WRITE); + if(res < 0) { + JANUS_LOG(LOG_ERR, "Error opening file for output (%d)\n", res); + avformat_free_context(ctx); + return NULL; + } + + return ctx; +} + +AVStream *janus_pp_new_audio_avstream(AVFormatContext *fctx, int codec_id, int samplerate, int channels, const uint8_t *extradata, int size) { + AVStream *st = avformat_new_stream(fctx, NULL); + if(!st) + return NULL; + +#ifdef USE_CODECPAR + AVCodecParameters *c = st->codecpar; +#else + AVCodecContext *c = st->codec; +#endif + c->codec_id = codec_id; + c->codec_type = AVMEDIA_TYPE_AUDIO; + c->sample_rate = samplerate; + c->channels = channels; + if(extradata) { + c->extradata_size = size; + c->extradata = av_memdup(extradata, size); + } + + return st; +} + +AVStream *janus_pp_new_video_avstream(AVFormatContext *fctx, int codec_id, int width, int height) { + AVStream *st = avformat_new_stream(fctx, NULL); + if(!st) + return NULL; + +#ifdef USE_CODECPAR + AVCodecParameters *c = st->codecpar; +#else + AVCodecContext *c = st->codec; +#endif + c->codec_id = codec_id; + c->codec_type = AVMEDIA_TYPE_VIDEO; + c->width = width; + c->height = height; + + return st; +} + diff --git a/postprocessing/pp-avformat.h b/postprocessing/pp-avformat.h new file mode 100644 index 0000000000..3107a976a5 --- /dev/null +++ b/postprocessing/pp-avformat.h @@ -0,0 +1,46 @@ +/*! \file pp-avformat.h + * \copyright GNU General Public License v3 + * + * \ingroup postprocessing + * \ref postprocessing + */ + +#ifndef JANUS_PP_AVFORMAT +#define JANUS_PP_AVFORMAT + +#include "../debug.h" + +#include +#include + +#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ + (LIBAVCODEC_VERSION_MAJOR > major || \ + (LIBAVCODEC_VERSION_MAJOR == major && \ + LIBAVCODEC_VERSION_MINOR >= minor)) + +#if LIBAVCODEC_VER_AT_LEAST(51, 42) +#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P +#endif + +#if LIBAVCODEC_VER_AT_LEAST(56, 56) +#ifndef CODEC_FLAG_GLOBAL_HEADER +#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER +#endif +#ifndef FF_INPUT_BUFFER_PADDING_SIZE +#define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE +#endif +#endif + +#if LIBAVCODEC_VER_AT_LEAST(57, 14) +#define USE_CODECPAR +#endif + +void janus_pp_setup_avformat(void); + +AVFormatContext *janus_pp_create_avformatcontext(const char *format, const char *metadata, const char *destination); + +AVStream *janus_pp_new_video_avstream(AVFormatContext *fctx, int codec_id, int width, int height); +AVStream *janus_pp_new_audio_avstream(AVFormatContext *fctx, int codec_id, int samplerate, int channels, const uint8_t *extradata, int size); + + +#endif diff --git a/postprocessing/pp-g722.c b/postprocessing/pp-g722.c index d85506e8fc..566a0d85a8 100644 --- a/postprocessing/pp-g722.c +++ b/postprocessing/pp-g722.c @@ -19,23 +19,11 @@ #include #include -#include -#include +#include "pp-avformat.h" #include "pp-g722.h" #include "../debug.h" - -#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ - (LIBAVCODEC_VERSION_MAJOR > major || \ - (LIBAVCODEC_VERSION_MAJOR == major && \ - LIBAVCODEC_VERSION_MINOR >= minor)) - -#if LIBAVCODEC_VER_AT_LEAST(57, 14) -#define USE_CODECPAR -#endif - - /* G.722 decoder */ static AVCodec *dec_codec; /* FFmpeg decoding codec */ static AVCodecContext *dec_ctx; /* FFmpeg decoding context */ @@ -63,17 +51,7 @@ static FILE *wav_file = NULL; int janus_pp_g722_create(char *destination, char *metadata) { if(destination == NULL) return -1; - /* Setup FFmpeg */ -#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) - av_register_all(); -#endif - /* Adjust logging to match the postprocessor's */ - av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : - (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : - (janus_log_level == LOG_ERR ? AV_LOG_ERROR : - (janus_log_level == LOG_WARN ? AV_LOG_WARNING : - (janus_log_level == LOG_INFO ? AV_LOG_INFO : - (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); + janus_pp_setup_avformat(); /* Create decoding context */ #if LIBAVCODEC_VER_AT_LEAST(53, 21) int codec = AV_CODEC_ID_ADPCM_G722; @@ -192,10 +170,9 @@ int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working) JANUS_LOG(LOG_VERB, "Writing %d bytes out of %d (seq=%"SCNu16", step=%"SCNu16", ts=%"SCNu64", time=%"SCNu64"s)\n", bytes, tmp->len, tmp->seq, diff, tmp->ts, (tmp->ts-list->ts)/8000); /* Decode and save to wav */ - AVPacket avpacket; - av_init_packet(&avpacket); - avpacket.data = (uint8_t *)buffer; - avpacket.size = bytes; + AVPacket *avpacket = av_packet_alloc(); + avpacket->data = (uint8_t *)buffer; + avpacket->size = bytes; int err = 0; #if LIBAVCODEC_VER_AT_LEAST(55,28) AVFrame *frame = av_frame_alloc(); @@ -203,7 +180,7 @@ int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working) AVFrame *frame = avcodec_alloc_frame(); #endif #ifdef USE_CODECPAR - err = avcodec_send_packet(dec_ctx, &avpacket); + err = avcodec_send_packet(dec_ctx, avpacket); if(err < 0) { JANUS_LOG(LOG_ERR, "Error decoding audio frame... (%d)\n", err); } else { @@ -212,7 +189,7 @@ int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working) if(err > -1) { #else int got_frame = 0; - err = avcodec_decode_audio4(dec_ctx, frame, &got_frame, &avpacket); + err = avcodec_decode_audio4(dec_ctx, frame, &got_frame, avpacket); if(err < 0 || !got_frame) { JANUS_LOG(LOG_ERR, "Error decoding audio frame... (%d)\n", err); } else { @@ -233,6 +210,7 @@ int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working) #else avcodec_free_frame(&frame); #endif + av_packet_free(&avpacket); tmp = tmp->next; } g_free(buffer); diff --git a/postprocessing/pp-h264.c b/postprocessing/pp-h264.c index bd02cb32aa..b61454902d 100644 --- a/postprocessing/pp-h264.c +++ b/postprocessing/pp-h264.c @@ -19,36 +19,10 @@ #include #include -#include -#include - +#include "pp-avformat.h" #include "pp-h264.h" #include "../debug.h" - -#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ - (LIBAVCODEC_VERSION_MAJOR > major || \ - (LIBAVCODEC_VERSION_MAJOR == major && \ - LIBAVCODEC_VERSION_MINOR >= minor)) - -#if LIBAVCODEC_VER_AT_LEAST(51, 42) -#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P -#endif - -#if LIBAVCODEC_VER_AT_LEAST(56, 56) -#ifndef CODEC_FLAG_GLOBAL_HEADER -#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER -#endif -#ifndef FF_INPUT_BUFFER_PADDING_SIZE -#define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE -#endif -#endif - -#if LIBAVCODEC_VER_AT_LEAST(57, 14) -#define USE_CODECPAR -#endif - - /* MP4 output */ static AVFormatContext *fctx; static AVStream *vStream; @@ -61,17 +35,9 @@ static int max_width = 0, max_height = 0, fps = 0; int janus_pp_h264_create(char *destination, char *metadata, gboolean faststart) { if(destination == NULL) return -1; - /* Setup FFmpeg */ -#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) - av_register_all(); -#endif - /* Adjust logging to match the postprocessor's */ - av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : - (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : - (janus_log_level == LOG_ERR ? AV_LOG_ERROR : - (janus_log_level == LOG_WARN ? AV_LOG_WARNING : - (janus_log_level == LOG_INFO ? AV_LOG_INFO : - (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); + + janus_pp_setup_avformat(); + /* MP4 output */ fctx = avformat_alloc_context(); if(fctx == NULL) { @@ -365,6 +331,7 @@ int janus_pp_h264_process(FILE *file, janus_pp_frame_packet *list, int *working) int len = 0, frameLen = 0; int keyFrame = 0; gboolean keyframe_found = FALSE; + AVPacket *packet = av_packet_alloc(); while(*working && tmp != NULL) { keyFrame = 0; @@ -487,21 +454,20 @@ int janus_pp_h264_process(FILE *file, janus_pp_frame_packet *list, int *working) /* Save the frame */ memset(received_frame + frameLen, 0, FF_INPUT_BUFFER_PADDING_SIZE); - AVPacket packet; - av_init_packet(&packet); - packet.stream_index = 0; - packet.data = received_frame; - packet.size = frameLen; + av_packet_unref(packet); + packet->stream_index = 0; + packet->data = received_frame; + packet->size = frameLen; if(keyFrame) - packet.flags |= AV_PKT_FLAG_KEY; + packet->flags |= AV_PKT_FLAG_KEY; /* First we save to the file... */ - packet.dts = tmp->ts-list->ts; - packet.pts = tmp->ts-list->ts; + packet->dts = tmp->ts-list->ts; + packet->pts = tmp->ts-list->ts; JANUS_LOG(LOG_HUGE, "%"SCNu64" - %"SCNu64" --> %"SCNu64"\n", - tmp->ts, list->ts, packet.pts); + tmp->ts, list->ts, packet->pts); if(fctx) { - int res = av_write_frame(fctx, &packet); + int res = av_write_frame(fctx, packet); if(res < 0) { JANUS_LOG(LOG_ERR, "Error writing video frame to file... (error %d)\n", res); } @@ -509,6 +475,7 @@ int janus_pp_h264_process(FILE *file, janus_pp_frame_packet *list, int *working) } tmp = tmp->next; } + av_packet_free(&packet); g_free(received_frame); g_free(start); return 0; diff --git a/postprocessing/pp-h265.c b/postprocessing/pp-h265.c index 464949b042..1494dcf018 100644 --- a/postprocessing/pp-h265.c +++ b/postprocessing/pp-h265.c @@ -19,36 +19,10 @@ #include #include -#include -#include - +#include "pp-avformat.h" #include "pp-h265.h" #include "../debug.h" - -#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ - (LIBAVCODEC_VERSION_MAJOR > major || \ - (LIBAVCODEC_VERSION_MAJOR == major && \ - LIBAVCODEC_VERSION_MINOR >= minor)) - -#if LIBAVCODEC_VER_AT_LEAST(51, 42) -#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P -#endif - -#if LIBAVCODEC_VER_AT_LEAST(56, 56) -#ifndef CODEC_FLAG_GLOBAL_HEADER -#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER -#endif -#ifndef FF_INPUT_BUFFER_PADDING_SIZE -#define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE -#endif -#endif - -#if LIBAVCODEC_VER_AT_LEAST(57, 14) -#define USE_CODECPAR -#endif - - /* MP4 output */ static AVFormatContext *fctx; static AVStream *vStream; @@ -61,17 +35,9 @@ static int max_width = 0, max_height = 0, fps = 0; int janus_pp_h265_create(char *destination, char *metadata, gboolean faststart) { if(destination == NULL) return -1; - /* Setup FFmpeg */ -#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) - av_register_all(); -#endif - /* Adjust logging to match the postprocessor's */ - av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : - (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : - (janus_log_level == LOG_ERR ? AV_LOG_ERROR : - (janus_log_level == LOG_WARN ? AV_LOG_WARNING : - (janus_log_level == LOG_INFO ? AV_LOG_INFO : - (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); + + janus_pp_setup_avformat(); + /* MP4 output */ fctx = avformat_alloc_context(); if(fctx == NULL) { @@ -449,6 +415,7 @@ int janus_pp_h265_process(FILE *file, janus_pp_frame_packet *list, int *working) int len = 0, frameLen = 0; int keyFrame = 0; gboolean keyframe_found = FALSE; + AVPacket *packet = av_packet_alloc(); while(*working && tmp != NULL) { keyFrame = 0; @@ -577,21 +544,20 @@ int janus_pp_h265_process(FILE *file, janus_pp_frame_packet *list, int *working) /* Save the frame */ memset(received_frame + frameLen, 0, FF_INPUT_BUFFER_PADDING_SIZE); - AVPacket packet; - av_init_packet(&packet); - packet.stream_index = 0; - packet.data = received_frame; - packet.size = frameLen; + av_packet_unref(packet); + packet->stream_index = 0; + packet->data = received_frame; + packet->size = frameLen; if(keyFrame) - packet.flags |= AV_PKT_FLAG_KEY; + packet->flags |= AV_PKT_FLAG_KEY; /* First we save to the file... */ - packet.dts = tmp->ts-list->ts; - packet.pts = tmp->ts-list->ts; + packet->dts = tmp->ts-list->ts; + packet->pts = tmp->ts-list->ts; JANUS_LOG(LOG_HUGE, "%"SCNu64" - %"SCNu64" --> %"SCNu64"\n", - tmp->ts, list->ts, packet.pts); + tmp->ts, list->ts, packet->pts); if(fctx) { - int res = av_write_frame(fctx, &packet); + int res = av_write_frame(fctx, packet); if(res < 0) { JANUS_LOG(LOG_ERR, "Error writing video frame to file... (error %d)\n", res); } @@ -599,6 +565,7 @@ int janus_pp_h265_process(FILE *file, janus_pp_frame_packet *list, int *working) } tmp = tmp->next; } + av_packet_free(&packet); g_free(received_frame); g_free(start); return 0; diff --git a/postprocessing/pp-opus.c b/postprocessing/pp-opus.c index 4eef9952e1..edf3b5ace5 100644 --- a/postprocessing/pp-opus.c +++ b/postprocessing/pp-opus.c @@ -19,51 +19,48 @@ #include #include -#include - +#include "pp-avformat.h" #include "pp-opus.h" #include "pp-opus-silence.h" #include "../debug.h" #include "../version.h" +static AVFormatContext *fctx; +static AVStream *vStream; -/* OGG/Opus helpers */ -FILE *ogg_file = NULL; -ogg_stream_state *stream = NULL; +static const uint8_t opus_extradata[19] = { + 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', + 1, 2, 0, 0, 128, 187, + 0, 0, 0, 0, 0, +}; -void le32(unsigned char *p, int v); -void le16(unsigned char *p, int v); -ogg_packet *op_opushead(void); -ogg_packet *op_opustags(char *metadata); -ogg_packet *op_from_pkt(const unsigned char *pkt, int len); -void op_free(ogg_packet *op); -int ogg_write(void); -int ogg_flush(void); +int janus_pp_opus_create(char *destination, char *metadata) { + if(destination == NULL) + return -1; + /* WebM output */ + fctx = janus_pp_create_avformatcontext("ogg", metadata, destination); + if(fctx == NULL) { + JANUS_LOG(LOG_ERR, "Error allocating context\n"); + return -1; + } -int janus_pp_opus_create(char *destination, char *metadata) { - stream = g_malloc0(sizeof(ogg_stream_state)); - if(ogg_stream_init(stream, rand()) < 0) { - JANUS_LOG(LOG_ERR, "Couldn't initialize Ogg stream state\n"); + vStream = janus_pp_new_audio_avstream(fctx, AV_CODEC_ID_OPUS, 48000, 2, opus_extradata, sizeof(opus_extradata)); + if(vStream == NULL) { + JANUS_LOG(LOG_ERR, "Error adding stream\n"); return -1; } - ogg_file = fopen(destination, "wb"); - if(ogg_file == NULL) { - JANUS_LOG(LOG_ERR, "Couldn't open output file\n"); + + if(avformat_write_header(fctx, NULL) < 0) { + JANUS_LOG(LOG_ERR, "Error writing header\n"); return -1; } - JANUS_LOG(LOG_INFO, "Writing .opus file header\n"); - /* Write stream headers */ - ogg_packet *op = op_opushead(); - ogg_stream_packetin(stream, op); - op_free(op); - op = op_opustags(metadata); - ogg_stream_packetin(stream, op); - op_free(op); - ogg_flush(); return 0; } +// It assumes ALL the packets are of the 20ms kind +#define OPUS_PACKET_DURATION 48 * 20; + int janus_pp_opus_process(FILE *file, janus_pp_frame_packet *list, int *working) { if(!file || !list || !working) return -1; @@ -72,30 +69,38 @@ int janus_pp_opus_process(FILE *file, janus_pp_frame_packet *list, int *working) int bytes = 0, len = 0, steps = 0, last_seq = 0; uint64_t pos = 0, nextPos = 0; uint8_t *buffer = g_malloc0(1500); + AVPacket *pkt = av_packet_alloc(); + AVRational timebase = {1, 48000}; + while(*working && tmp != NULL) { if(tmp->prev != NULL && ((tmp->ts - tmp->prev->ts)/48/20 > 1)) { JANUS_LOG(LOG_WARN, "Lost a packet here? (got seq %"SCNu16" after %"SCNu16", time ~%"SCNu64"s)\n", tmp->seq, tmp->prev->seq, (tmp->ts-list->ts)/48000); - ogg_packet *op = op_from_pkt((const unsigned char *)opus_silence, sizeof(opus_silence)); /* use ts differ to insert silence packet */ int silence_count = (tmp->ts - tmp->prev->ts)/48/20 - 1; pos = (tmp->prev->ts - list->ts) / 48 / 20 + 1; JANUS_LOG(LOG_WARN, "[FILL] pos: %06"SCNu64", writing silences (count=%d)\n", pos, silence_count); int i=0; + pos = tmp->prev->ts - list->ts; for(i=0; iprev->ts - list->ts) / 48 / 20 + i + 1; + pos += OPUS_PACKET_DURATION; if(tmp->next != NULL) - nextPos = (tmp->next->ts - list->ts) / 48 / 20; + nextPos = tmp->next->ts - list->ts; if(pos >= nextPos) { - JANUS_LOG(LOG_WARN, "[SKIP] pos: %06" SCNu64 ", skipping remaining silence\n", pos); + JANUS_LOG(LOG_WARN, "[SKIP] pos: %06" SCNu64 ", skipping remaining silence\n", pos / 48 / 20 + 1); break; } - op->granulepos = 960*(pos); /* FIXME: get this from the toc byte */ - ogg_stream_packetin(stream, op); - ogg_write(); + av_packet_unref(pkt); + pkt->stream_index = 0; + pkt->data = opus_silence; + pkt->size = sizeof(opus_silence); + pkt->pts = pkt->dts = av_rescale_q(pos, timebase, fctx->streams[0]->time_base); + pkt->duration = OPUS_PACKET_DURATION; + + if(av_write_frame(fctx, pkt) < 0) { + JANUS_LOG(LOG_ERR, "Error writing audio frame to file...\n"); + } } - ogg_flush(); - g_free(op); } if(tmp->drop) { /* We marked this packet as one to drop, before */ @@ -128,176 +133,30 @@ int janus_pp_opus_process(FILE *file, janus_pp_frame_packet *list, int *working) last_seq = tmp->seq; steps++; } - ogg_packet *op = op_from_pkt((const unsigned char *)buffer, bytes); - pos = (tmp->ts - list->ts) / 48 / 20 + 1; JANUS_LOG(LOG_VERB, "pos: %06"SCNu64", writing %d bytes out of %d (seq=%"SCNu16", step=%"SCNu16", ts=%"SCNu64", time=%"SCNu64"s)\n", pos, bytes, tmp->len, tmp->seq, diff, tmp->ts, (tmp->ts-list->ts)/48000); - op->granulepos = 960*(pos); /* FIXME: get this from the toc byte */ - ogg_stream_packetin(stream, op); - g_free(op); - ogg_write(); - ogg_flush(); + av_packet_unref(pkt); + pkt->stream_index = 0; + pkt->data = buffer; + pkt->size = bytes; + pkt->pts = pkt->dts = av_rescale_q(tmp->ts - list->ts, timebase, fctx->streams[0]->time_base); + pkt->duration = OPUS_PACKET_DURATION; + + if(av_write_frame(fctx, pkt) < 0) { + JANUS_LOG(LOG_ERR, "Error writing audio frame to file...\n"); + } + tmp = tmp->next; } g_free(buffer); + av_packet_free(&pkt); return 0; } void janus_pp_opus_close(void) { - ogg_flush(); - if(ogg_file) - fclose(ogg_file); - ogg_file = NULL; - if(stream) - ogg_stream_destroy(stream); - stream = NULL; -} - - -/* OGG/Opus helpers */ -/* Write a little-endian 32 bit int to memory */ -void le32(unsigned char *p, int v) { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; -} - - -/* Write a little-endian 16 bit int to memory */ -void le16(unsigned char *p, int v) { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; -} - -/* Manufacture a generic OpusHead packet */ -ogg_packet *op_opushead(void) { - int size = 19; - unsigned char *data = g_malloc(size); - ogg_packet *op = g_malloc(sizeof(*op)); - - memcpy(data, "OpusHead", 8); /* identifier */ - data[8] = 1; /* version */ - data[9] = 2; /* channels */ - le16(data+10, 0); /* pre-skip */ - le32(data + 12, 48000); /* original sample rate */ - le16(data + 16, 0); /* gain */ - data[18] = 0; /* channel mapping family */ - - op->packet = data; - op->bytes = size; - op->b_o_s = 1; - op->e_o_s = 0; - op->granulepos = 0; - op->packetno = 0; - - return op; -} - -/* Manufacture a generic OpusTags packet */ -ogg_packet *op_opustags(char *metadata) { - const char *identifier = "OpusTags"; - const char *desc = "DESCRIPTION="; - char vendor[256]; - g_snprintf(vendor, sizeof(vendor), "Janus post-processor %s", janus_version_string); - int size = strlen(identifier) + 4 + strlen(vendor) + 4; - int dlen = strlen(desc), mlen = metadata ? strlen(metadata) : 0; - if(mlen > 0) - size += (4+dlen+mlen); - unsigned char *data = g_malloc(size); - ogg_packet *op = g_malloc(sizeof(*op)); - - /* Write down the tags */ - memcpy(data, identifier, 8); - le32(data + 8, strlen(vendor)); - memcpy(data + 12, vendor, strlen(vendor)); - le32(data + 12 + strlen(vendor), mlen > 0 ? 1 : 0); - /* Check if we have metadata to write down: we'll use the "DESCRIPTION" tag */ - if(metadata && strlen(metadata) > 0) { - /* Add a single comment */ - le32(data + 12 + strlen(vendor) + 4, dlen+mlen); - memcpy(data + 12 + strlen(vendor) + 8, desc, dlen); - memcpy(data + 12 + strlen(vendor) + 8 + dlen, metadata, mlen); - } - - op->packet = data; - op->bytes = size; - op->b_o_s = 0; - op->e_o_s = 0; - op->granulepos = 0; - op->packetno = 1; - - return op; -} - -/* Allocate an ogg_packet */ -ogg_packet *op_from_pkt(const unsigned char *pkt, int len) { - ogg_packet *op = g_malloc(sizeof(*op)); - - op->packet = (unsigned char *)pkt; - op->bytes = len; - op->b_o_s = 0; - op->e_o_s = 0; - op->granulepos = 0; - op->packetno = 0; - - return op; -} - -/* Free a packet and its contents */ -void op_free(ogg_packet *op) { - if(op) { - if(op->packet) { - g_free(op->packet); - } - g_free(op); - } -} - -/* Write out available ogg pages */ -int ogg_write(void) { - ogg_page page; - size_t written; - - if(!stream || !ogg_file) { - return -1; - } - - while (ogg_stream_pageout(stream, &page)) { - written = fwrite(page.header, 1, page.header_len, ogg_file); - if(written != (size_t)page.header_len) { - JANUS_LOG(LOG_ERR, "Error writing Ogg page header\n"); - return -2; - } - written = fwrite(page.body, 1, page.body_len, ogg_file); - if(written != (size_t)page.body_len) { - JANUS_LOG(LOG_ERR, "Error writing Ogg page body\n"); - return -3; - } - } - return 0; -} - -/* Flush remaining ogg data */ -int ogg_flush(void) { - ogg_page page; - size_t written; - - if(!stream || !ogg_file) { - return -1; + if(fctx != NULL) { + av_write_trailer(fctx); + avio_close(fctx->pb); + avformat_free_context(fctx); } - - while (ogg_stream_flush(stream, &page)) { - written = fwrite(page.header, 1, page.header_len, ogg_file); - if(written != (size_t)page.header_len) { - JANUS_LOG(LOG_ERR, "Error writing Ogg page header\n"); - return -2; - } - written = fwrite(page.body, 1, page.body_len, ogg_file); - if(written != (size_t)page.body_len) { - JANUS_LOG(LOG_ERR, "Error writing Ogg page body\n"); - return -3; - } - } - return 0; } diff --git a/postprocessing/pp-webm.c b/postprocessing/pp-webm.c index 17a394758f..9c4e0be486 100644 --- a/postprocessing/pp-webm.c +++ b/postprocessing/pp-webm.c @@ -19,13 +19,10 @@ #include #include -#include -#include - +#include "pp-avformat.h" #include "pp-webm.h" #include "../debug.h" - /* WebRTC stuff (VP8/VP9) */ #if defined(__ppc__) || defined(__ppc64__) # define swap2(d) \ @@ -35,35 +32,10 @@ # define swap2(d) d #endif -#define LIBAVCODEC_VER_AT_LEAST(major, minor) \ - (LIBAVCODEC_VERSION_MAJOR > major || \ - (LIBAVCODEC_VERSION_MAJOR == major && \ - LIBAVCODEC_VERSION_MINOR >= minor)) - -#if LIBAVCODEC_VER_AT_LEAST(51, 42) -#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P -#endif - -#if LIBAVCODEC_VER_AT_LEAST(56, 56) -#ifndef CODEC_FLAG_GLOBAL_HEADER -#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER -#endif -#ifndef FF_INPUT_BUFFER_PADDING_SIZE -#define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE -#endif -#endif - -#if LIBAVCODEC_VER_AT_LEAST(57, 14) -#define USE_CODECPAR -#endif - /* WebM output */ static AVFormatContext *fctx; static AVStream *vStream; -#ifdef USE_CODECPAR -static AVCodecContext *vEncoder; -#endif static int max_width = 0, max_height = 0, fps = 0; int janus_pp_webm_create(char *destination, char *metadata, gboolean vp8) { @@ -75,92 +47,30 @@ int janus_pp_webm_create(char *destination, char *metadata, gboolean vp8) { return -1; } #endif - /* Setup FFmpeg */ -#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ) - av_register_all(); -#endif - /* Adjust logging to match the postprocessor's */ - av_log_set_level(janus_log_level <= LOG_NONE ? AV_LOG_QUIET : - (janus_log_level == LOG_FATAL ? AV_LOG_FATAL : - (janus_log_level == LOG_ERR ? AV_LOG_ERROR : - (janus_log_level == LOG_WARN ? AV_LOG_WARNING : - (janus_log_level == LOG_INFO ? AV_LOG_INFO : - (janus_log_level == LOG_VERB ? AV_LOG_VERBOSE : AV_LOG_DEBUG)))))); /* WebM output */ - fctx = avformat_alloc_context(); + fctx = janus_pp_create_avformatcontext("webm", metadata, destination); if(fctx == NULL) { JANUS_LOG(LOG_ERR, "Error allocating context\n"); return -1; } - /* We save the metadata part as a comment (see #1189) */ - if(metadata) - av_dict_set(&fctx->metadata, "comment", metadata, 0); - fctx->oformat = av_guess_format("webm", NULL, NULL); - if(fctx->oformat == NULL) { - JANUS_LOG(LOG_ERR, "Error guessing format\n"); - return -1; - } - char filename[1024]; - snprintf(filename, sizeof(filename), "%s", destination); -#ifdef USE_CODECPAR - AVCodec *codec = avcodec_find_encoder(vp8 ? AV_CODEC_ID_VP8 : AV_CODEC_ID_VP9); - if(!codec) { - /* Error opening video codec */ - JANUS_LOG(LOG_ERR, "Encoder not available\n"); - return -1; - } - fctx->video_codec = codec; - fctx->oformat->video_codec = codec->id; - vStream = avformat_new_stream(fctx, codec); - vStream->id = fctx->nb_streams-1; - vEncoder = avcodec_alloc_context3(codec); - vEncoder->width = max_width; - vEncoder->height = max_height; - vEncoder->time_base = (AVRational){ 1, fps }; - vEncoder->pix_fmt = AV_PIX_FMT_YUV420P; - vEncoder->flags |= CODEC_FLAG_GLOBAL_HEADER; - if(avcodec_open2(vEncoder, codec, NULL) < 0) { - /* Error opening video codec */ - JANUS_LOG(LOG_ERR, "Encoder error\n"); - return -1; - } - avcodec_parameters_from_context(vStream->codecpar, vEncoder); -#else - //~ vStream = av_new_stream(fctx, 0); - vStream = avformat_new_stream(fctx, 0); - if(vStream == NULL) { - JANUS_LOG(LOG_ERR, "Error adding stream\n"); - return -1; - } - //~ avcodec_get_context_defaults2(vStream->codec, CODEC_TYPE_VIDEO); -#if LIBAVCODEC_VER_AT_LEAST(53, 21) - avcodec_get_context_defaults3(vStream->codec, AVMEDIA_TYPE_VIDEO); -#else - avcodec_get_context_defaults2(vStream->codec, AVMEDIA_TYPE_VIDEO); -#endif + + int codec_id; #if LIBAVCODEC_VER_AT_LEAST(54, 25) #if LIBAVCODEC_VERSION_MAJOR >= 55 - vStream->codec->codec_id = vp8 ? AV_CODEC_ID_VP8 : AV_CODEC_ID_VP9; + codec_id = vp8 ? AV_CODEC_ID_VP8 : AV_CODEC_ID_VP9; #else - vStream->codec->codec_id = AV_CODEC_ID_VP8; + codec_id = AV_CODEC_ID_VP8; #endif #else - vStream->codec->codec_id = CODEC_ID_VP8; + codec_id = CODEC_ID_VP8; #endif - //~ vStream->codec->codec_type = CODEC_TYPE_VIDEO; - vStream->codec->codec_type = AVMEDIA_TYPE_VIDEO; - vStream->codec->time_base = (AVRational){1, fps}; - vStream->codec->width = max_width; - vStream->codec->height = max_height; - vStream->codec->pix_fmt = PIX_FMT_YUV420P; - if (fctx->flags & AVFMT_GLOBALHEADER) - vStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif - int res = avio_open(&fctx->pb, filename, AVIO_FLAG_WRITE); - if(res < 0) { - JANUS_LOG(LOG_ERR, "Error opening file for output (%d)\n", res); + + vStream = janus_pp_new_video_avstream(fctx, codec_id, max_width, max_height); + if(vStream == NULL) { + JANUS_LOG(LOG_ERR, "Error adding stream\n"); return -1; } + if(avformat_write_header(fctx, NULL) < 0) { JANUS_LOG(LOG_ERR, "Error writing header\n"); return -1; @@ -374,6 +284,7 @@ int janus_pp_webm_process(FILE *file, janus_pp_frame_packet *list, gboolean vp8, int len = 0, frameLen = 0; int keyFrame = 0; gboolean keyframe_found = FALSE; + AVPacket *packet = av_packet_alloc(); while(*working && tmp != NULL) { keyFrame = 0; @@ -609,28 +520,28 @@ int janus_pp_webm_process(FILE *file, janus_pp_frame_packet *list, gboolean vp8, if(frameLen > 0) { memset(received_frame + frameLen, 0, FF_INPUT_BUFFER_PADDING_SIZE); - AVPacket packet; - av_init_packet(&packet); - packet.stream_index = 0; - packet.data = received_frame; - packet.size = frameLen; + av_packet_unref(packet); + packet->stream_index = 0; + packet->data = received_frame; + packet->size = frameLen; if(keyFrame) //~ packet.flags |= PKT_FLAG_KEY; - packet.flags |= AV_PKT_FLAG_KEY; + packet->flags |= AV_PKT_FLAG_KEY; /* First we save to the file... */ //~ packet.dts = AV_NOPTS_VALUE; //~ packet.pts = AV_NOPTS_VALUE; - packet.dts = (tmp->ts-list->ts)/90; - packet.pts = (tmp->ts-list->ts)/90; + packet->dts = (tmp->ts-list->ts)/90; + packet->pts = (tmp->ts-list->ts)/90; if(fctx) { - if(av_write_frame(fctx, &packet) < 0) { + if(av_write_frame(fctx, packet) < 0) { JANUS_LOG(LOG_ERR, "Error writing video frame to file...\n"); } } } tmp = tmp->next; } + av_packet_free(&packet); g_free(received_frame); g_free(start); return 0; @@ -638,24 +549,9 @@ int janus_pp_webm_process(FILE *file, janus_pp_frame_packet *list, gboolean vp8, /* Close WebM file */ void janus_pp_webm_close(void) { - if(fctx != NULL) - av_write_trailer(fctx); -#ifdef USE_CODECPAR - if(vEncoder != NULL) - avcodec_close(vEncoder); -#else - if(vStream != NULL && vStream->codec != NULL) - avcodec_close(vStream->codec); -#endif - if(fctx != NULL && fctx->streams[0] != NULL) { -#ifndef USE_CODECPAR - av_free(fctx->streams[0]->codec); -#endif - av_free(fctx->streams[0]); - } if(fctx != NULL) { - //~ url_fclose(fctx->pb); + av_write_trailer(fctx); avio_close(fctx->pb); - av_free(fctx); + avformat_free_context(fctx); } }