[Pkg-voip-commits] [janus] 93/163: Added G.722 support to Record&Play and janus-pp-rec
Jonas Smedegaard
dr at jones.dk
Sat Oct 28 01:22:13 UTC 2017
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag debian/0.2.5-1
in repository janus.
commit c7c94b0ba28b1d958810686373989915f5b3704e
Author: Lorenzo Miniero <lminiero at gmail.com>
Date: Wed Sep 6 13:19:12 2017 +0200
Added G.722 support to Record&Play and janus-pp-rec
---
Makefile.am | 2 +
plugins/janus_recordplay.c | 45 +++++++--
postprocessing/janus-pp-rec.c | 20 +++-
postprocessing/pp-g711.c | 6 +-
postprocessing/pp-g722.c | 216 ++++++++++++++++++++++++++++++++++++++++++
postprocessing/pp-g722.h | 23 +++++
record.c | 4 +-
7 files changed, 302 insertions(+), 14 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index c8baa3e..705ccfa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -402,6 +402,8 @@ man1_MANS += postprocessing/janus-pp-rec.1
janus_pp_rec_SOURCES = \
postprocessing/pp-g711.c \
postprocessing/pp-g711.h \
+ postprocessing/pp-g722.c \
+ postprocessing/pp-g722.h \
postprocessing/pp-h264.c \
postprocessing/pp-h264.h \
postprocessing/pp-opus.c \
diff --git a/plugins/janus_recordplay.c b/plugins/janus_recordplay.c
index 77a5921..966a42a 100644
--- a/plugins/janus_recordplay.c
+++ b/plugins/janus_recordplay.c
@@ -378,8 +378,10 @@ typedef struct janus_recordplay_recording {
char *date; /* Time of the recording */
char *arc_file; /* Audio file name */
const char *acodec; /* Codec used for audio, if available */
+ int audio_pt; /* Payload types to use for audio when playing recordings */
char *vrc_file; /* Video file name */
const char *vcodec; /* Codec used for video, if available */
+ int video_pt; /* Payload types to use for audio when playing recordings */
gboolean completed; /* Whether this recording was completed or still going on */
char *offer; /* The SDP offer that will be sent to watchers */
GList *viewers; /* List of users watching this recording */
@@ -393,7 +395,7 @@ typedef struct janus_recordplay_session {
janus_plugin_session *handle;
gboolean active;
gboolean recorder; /* Whether this session is used to record or to replay a WebRTC session */
- gboolean firefox; /* We send Firefox users a different kind of FIR */
+ gboolean firefox; /* We send Firefox users a different kind of FIR */
janus_recordplay_recording *recording;
janus_recorder *arc; /* Audio recorder */
janus_recorder *vrc; /* Video recorder */
@@ -422,7 +424,7 @@ static void *janus_recordplay_playout_thread(void *data);
/* Helper to send RTCP feedback back to recorders, if needed */
void janus_recordplay_send_rtcp_feedback(janus_plugin_session *handle, int video, char *buf, int len);
-/* To make things easier, we use static payload types for viewers */
+/* To make things easier, we use static payload types for viewers (unless it's for G.711 or G.722) */
#define AUDIO_PT 111
#define VIDEO_PT 100
@@ -583,11 +585,11 @@ static int janus_recordplay_generate_offer(janus_recordplay_recording *rec) {
s_name, "1.1.1.1",
JANUS_SDP_OA_AUDIO, offer_audio,
JANUS_SDP_OA_AUDIO_CODEC, rec->acodec,
- JANUS_SDP_OA_AUDIO_PT, AUDIO_PT,
+ JANUS_SDP_OA_AUDIO_PT, rec->audio_pt,
JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_VIDEO, offer_video,
JANUS_SDP_OA_VIDEO_CODEC, rec->vcodec,
- JANUS_SDP_OA_VIDEO_PT, VIDEO_PT,
+ JANUS_SDP_OA_VIDEO_PT, rec->video_pt,
JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_DATA, FALSE,
JANUS_SDP_OA_DONE);
@@ -1325,6 +1327,17 @@ static void *janus_recordplay_handler(void *data) {
}
temp = temp->next;
}
+ rec->audio_pt = AUDIO_PT;
+ if(rec->acodec) {
+ /* Some audio codecs have a fixed payload type that we can't mess with */
+ if(!strcasecmp(rec->acodec, "pcmu"))
+ rec->audio_pt = 0;
+ else if(!strcasecmp(rec->acodec, "pcma"))
+ rec->audio_pt = 8;
+ else if(!strcasecmp(rec->acodec, "g722"))
+ rec->audio_pt = 9;
+ }
+ rec->video_pt = VIDEO_PT;
/* Create a date string */
time_t t = time(NULL);
struct tm *tmv = localtime(&t);
@@ -1710,6 +1723,17 @@ void janus_recordplay_update_recordings_list(void) {
/* Check which codec is in this recording */
rec->vcodec = janus_recordplay_parse_codec(recordings_path, rec->vrc_file);
}
+ rec->audio_pt = AUDIO_PT;
+ if(rec->acodec) {
+ /* Some audio codecs have a fixed payload type that we can't mess with */
+ if(!strcasecmp(rec->acodec, "pcmu"))
+ rec->audio_pt = 0;
+ else if(!strcasecmp(rec->acodec, "pcma"))
+ rec->audio_pt = 8;
+ else if(!strcasecmp(rec->acodec, "g722"))
+ rec->audio_pt = 9;
+ }
+ rec->video_pt = VIDEO_PT;
rec->viewers = NULL;
rec->destroyed = 0;
rec->completed = TRUE;
@@ -2130,7 +2154,10 @@ static void *janus_recordplay_playout_thread(void *data) {
memset(buffer, 0, 1500);
int bytes = 0;
int64_t ts_diff = 0, passed = 0;
-
+
+ int audio_pt = session->recording->audio_pt;
+ int video_pt = session->recording->video_pt;
+
while(!session->destroyed && session->active && !session->recording->destroyed && (audio || video)) {
if(!asent && !vsent) {
/* We skipped the last round, so sleep a bit (5ms) */
@@ -2147,7 +2174,7 @@ static void *janus_recordplay_playout_thread(void *data) {
JANUS_LOG(LOG_WARN, "Didn't manage to read all the bytes we needed (%d < %d)...\n", bytes, audio->len);
/* Update payload type */
rtp_header *rtp = (rtp_header *)buffer;
- rtp->type = AUDIO_PT;
+ rtp->type = audio_pt;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 0, (char *)buffer, bytes);
gettimeofday(&now, NULL);
@@ -2188,7 +2215,7 @@ static void *janus_recordplay_playout_thread(void *data) {
JANUS_LOG(LOG_WARN, "Didn't manage to read all the bytes we needed (%d < %d)...\n", bytes, audio->len);
/* Update payload type */
rtp_header *rtp = (rtp_header *)buffer;
- rtp->type = AUDIO_PT;
+ rtp->type = audio_pt;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 0, (char *)buffer, bytes);
asent = TRUE;
@@ -2207,7 +2234,7 @@ static void *janus_recordplay_playout_thread(void *data) {
JANUS_LOG(LOG_WARN, "Didn't manage to read all the bytes we needed (%d < %d)...\n", bytes, video->len);
/* Update payload type */
rtp_header *rtp = (rtp_header *)buffer;
- rtp->type = VIDEO_PT;
+ rtp->type = video_pt;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 1, (char *)buffer, bytes);
video = video->next;
@@ -2252,7 +2279,7 @@ static void *janus_recordplay_playout_thread(void *data) {
JANUS_LOG(LOG_WARN, "Didn't manage to read all the bytes we needed (%d < %d)...\n", bytes, video->len);
/* Update payload type */
rtp_header *rtp = (rtp_header *)buffer;
- rtp->type = VIDEO_PT;
+ rtp->type = video_pt;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 1, (char *)buffer, bytes);
video = video->next;
diff --git a/postprocessing/janus-pp-rec.c b/postprocessing/janus-pp-rec.c
index ef8e703..a045b96 100644
--- a/postprocessing/janus-pp-rec.c
+++ b/postprocessing/janus-pp-rec.c
@@ -69,6 +69,7 @@
#include "pp-h264.h"
#include "pp-opus.h"
#include "pp-g711.h"
+#include "pp-g722.h"
#include "pp-srt.h"
#define htonll(x) ((1==htonl(1)) ? (x) : ((gint64)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
@@ -155,7 +156,7 @@ int main(int argc, char *argv[])
JANUS_LOG(LOG_INFO, "Pre-parsing file to generate ordered index...\n");
gboolean parsed_header = FALSE;
int video = 0, data = 0;
- int opus = 0, g711 = 0, vp8 = 0, vp9 = 0, h264 = 0;
+ int opus = 0, g711 = 0, g722 = 0, vp8 = 0, vp9 = 0, h264 = 0;
gint64 c_time = 0, w_time = 0;
int bytes = 0, skip = 0;
long offset = 0;
@@ -309,6 +310,12 @@ int main(int argc, char *argv[])
JANUS_LOG(LOG_ERR, "G.711 RTP packets can only be converted to a .wav file\n");
exit(1);
}
+ } else if(!strcasecmp(c, "g722")) {
+ g722 = 1;
+ if(extension && strcasecmp(extension, ".wav")) {
+ JANUS_LOG(LOG_ERR, "G.722 RTP packets can only be converted to a .wav file\n");
+ exit(1);
+ }
} else {
JANUS_LOG(LOG_WARN, "The post-processor only supports Opus and G.711 audio for now (was '%s')...\n", c);
exit(1);
@@ -623,6 +630,11 @@ int main(int argc, char *argv[])
JANUS_LOG(LOG_ERR, "Error creating .wav file...\n");
exit(1);
}
+ } else if(g722) {
+ if(janus_pp_g722_create(destination) < 0) {
+ JANUS_LOG(LOG_ERR, "Error creating .wav file...\n");
+ exit(1);
+ }
}
} else if(data) {
if(janus_pp_srt_create(destination) < 0) {
@@ -653,6 +665,10 @@ int main(int argc, char *argv[])
if(janus_pp_g711_process(file, list, &working) < 0) {
JANUS_LOG(LOG_ERR, "Error processing G.711 RTP frames...\n");
}
+ } else if(g722) {
+ if(janus_pp_g722_process(file, list, &working) < 0) {
+ JANUS_LOG(LOG_ERR, "Error processing G.722 RTP frames...\n");
+ }
}
} else if(data) {
if(janus_pp_srt_process(file, list, &working) < 0) {
@@ -684,6 +700,8 @@ int main(int argc, char *argv[])
janus_pp_opus_close();
} else if(g711) {
janus_pp_g711_close();
+ } else if(g722) {
+ janus_pp_g722_close();
}
}
fclose(file);
diff --git a/postprocessing/pp-g711.c b/postprocessing/pp-g711.c
index 3b0a6e8..ea7104c 100644
--- a/postprocessing/pp-g711.c
+++ b/postprocessing/pp-g711.c
@@ -39,7 +39,7 @@ typedef struct janus_pp_g711_wav {
char data[4];
uint32_t blocksize;
} janus_pp_g711_wav;
-FILE *wav_file = NULL;
+static FILE *wav_file = NULL;
/* mu-law decoding table */
@@ -167,7 +167,7 @@ int janus_pp_g711_process(FILE *file, janus_pp_frame_packet *list, int *working)
int i=0;
for(i=0; i<(tmp->seq-tmp->prev->seq-1); i++) {
/* FIXME We should actually also look at the timestamp differences */
- JANUS_LOG(LOG_WARN, "[FILL] writing silence (seq=%"SCNu16", index=%"SCNu16")\n",
+ JANUS_LOG(LOG_WARN, "[FILL] Writing silence (seq=%"SCNu16", index=%"SCNu16")\n",
tmp->prev->seq+i+1, i+1);
/* Add silence */
memset(samples, 0, num_samples*2);
@@ -200,7 +200,7 @@ int janus_pp_g711_process(FILE *file, janus_pp_frame_packet *list, int *working)
last_seq = tmp->seq;
steps++;
}
- JANUS_LOG(LOG_VERB, "writing %d bytes out of %d (seq=%"SCNu16", step=%"SCNu16", ts=%"SCNu64", time=%"SCNu64"s)\n",
+ 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 */
uint8_t *data = (uint8_t *)buffer;
diff --git a/postprocessing/pp-g722.c b/postprocessing/pp-g722.c
new file mode 100644
index 0000000..b42f6f6
--- /dev/null
+++ b/postprocessing/pp-g722.c
@@ -0,0 +1,216 @@
+/*! \file pp-g722.c
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>
+ * \copyright GNU General Public License v3
+ * \brief Post-processing to generate .wav files out of G.722 (headers)
+ * \details Implementation of the post-processing code needed to
+ * generate raw .wav files out of G.722 RTP frames.
+ *
+ * \ingroup postprocessing
+ * \ref postprocessing
+ */
+
+#include <arpa/inet.h>
+#ifdef __MACH__
+#include <machine/endian.h>
+#else
+#include <endian.h>
+#endif
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/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))
+
+
+/* G.722 decoder */
+static AVCodec *dec_codec; /* FFmpeg decoding codec */
+static AVCodecContext *dec_ctx; /* FFmpeg decoding context */
+
+/* WAV header */
+typedef struct janus_pp_g711_wav {
+ char riff[4];
+ uint32_t len;
+ char wave[4];
+ char fmt[4];
+ uint32_t formatsize;
+ uint16_t format;
+ uint16_t channels;
+ uint32_t samplerate;
+ uint32_t avgbyterate;
+ uint16_t samplebytes;
+ uint16_t channelbits;
+ char data[4];
+ uint32_t blocksize;
+} janus_pp_g711_wav;
+static FILE *wav_file = NULL;
+
+
+/* Processing methods */
+int janus_pp_g722_create(char *destination) {
+ if(destination == NULL)
+ return -1;
+ /* Setup FFmpeg */
+ av_register_all();
+ /* 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))))));
+ /* Create decoding context */
+#if LIBAVCODEC_VER_AT_LEAST(53, 21)
+ int codec = AV_CODEC_ID_ADPCM_G722;
+#else
+ int codec = CODEC_ID_ADPCM_G722;
+#endif
+ dec_codec = avcodec_find_decoder(codec);
+ if(!dec_codec) {
+ /* Error finding G.722 codec... */
+ JANUS_LOG(LOG_ERR, "Unsupported decoder (G.722)...\n");
+ return -1;
+ }
+ dec_ctx = avcodec_alloc_context3(dec_codec);
+ if(!dec_ctx) {
+ /* Error creating FFmpeg context... */
+ JANUS_LOG(LOG_ERR, "Error creating FFmpeg context...\n");
+ return -1;
+ }
+ if(avcodec_open2(dec_ctx, dec_codec, NULL) < 0) {
+ /* Error finding video codec... */
+ JANUS_LOG(LOG_ERR, "Error opening G.722 decoder...\n");
+ return -1;
+ }
+ /* Create wav file */
+ wav_file = fopen(destination, "wb");
+ if(wav_file == NULL) {
+ JANUS_LOG(LOG_ERR, "Couldn't open output file\n");
+ return -1;
+ }
+ /* Add header */
+ JANUS_LOG(LOG_INFO, "Writing .wav file header\n");
+ janus_pp_g711_wav header = {
+ {'R', 'I', 'F', 'F'},
+ 0,
+ {'W', 'A', 'V', 'E'},
+ {'f', 'm', 't', ' '},
+ 16,
+ 1,
+ 1,
+ 16000,
+ 16000,
+ 2,
+ 16,
+ {'d', 'a', 't', 'a'},
+ 0
+ };
+ if(fwrite(&header, 1, sizeof(header), wav_file) != sizeof(header)) {
+ JANUS_LOG(LOG_ERR, "Couldn't write WAV header, expect problems...\n");
+ }
+ fflush(wav_file);
+ return 0;
+}
+
+int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working) {
+ if(!file || !list || !working)
+ return -1;
+ janus_pp_frame_packet *tmp = list;
+ long int offset = 0;
+ int bytes = 0, len = 0, steps = 0, last_seq = 0;
+ uint8_t *buffer = g_malloc0(1500);
+ int16_t samples[1500];
+ memset(samples, 0, sizeof(samples));
+ while(*working && tmp != NULL) {
+ if(tmp->prev != NULL && (tmp->seq - tmp->prev->seq > 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);
+ /* FIXME Write the silence packet N times to fill in the gaps */
+ int i=0;
+ for(i=0; i<(tmp->seq-tmp->prev->seq-1); i++) {
+ /* FIXME We should actually also look at the timestamp differences */
+ JANUS_LOG(LOG_WARN, "[FILL] Writing silence (seq=%"SCNu16", index=%"SCNu16")\n",
+ tmp->prev->seq+i+1, i+1);
+ /* Add silence */
+ uint num_samples = 320;
+ memset(samples, 0, num_samples*2);
+ if(wav_file != NULL) {
+ if(fwrite(samples, sizeof(uint16_t), num_samples, wav_file) != num_samples) {
+ JANUS_LOG(LOG_ERR, "Couldn't write sample...\n");
+ }
+ fflush(wav_file);
+ }
+ }
+ }
+ if(tmp->drop) {
+ /* We marked this packet as one to drop, before */
+ JANUS_LOG(LOG_WARN, "Dropping previously marked audio packet (time ~%"SCNu64"s)\n", (tmp->ts-list->ts)/48000);
+ tmp = tmp->next;
+ continue;
+ }
+ guint16 diff = tmp->prev == NULL ? 1 : (tmp->seq - tmp->prev->seq);
+ len = 0;
+ /* RTP payload */
+ offset = tmp->offset+12+tmp->skip;
+ fseek(file, offset, SEEK_SET);
+ len = tmp->len-12-tmp->skip;
+ bytes = fread(buffer, sizeof(char), len, file);
+ if(bytes != len)
+ JANUS_LOG(LOG_WARN, "Didn't manage to read all the bytes we needed (%d < %d)...\n", bytes, len);
+ if(last_seq == 0)
+ last_seq = tmp->seq;
+ if(tmp->seq < last_seq) {
+ last_seq = tmp->seq;
+ steps++;
+ }
+ 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;
+ avpacket.data = (uint8_t *)buffer;
+ avpacket.size = bytes;
+ int err = 0, got_frame = 0;
+ AVFrame *frame = av_frame_alloc();
+ 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 {
+ if(wav_file != NULL) {
+ int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
+ int i=0, ch=0;
+ for(i=0; i<frame->nb_samples; i++) {
+ for(ch=0; ch<dec_ctx->channels; ch++) {
+ fwrite(frame->data[ch] + data_size*i, 1, data_size, wav_file);
+ }
+ }
+ fflush(wav_file);
+ }
+ }
+ av_frame_free(&frame);
+ tmp = tmp->next;
+ }
+ g_free(buffer);
+ return 0;
+}
+
+void janus_pp_g722_close(void) {
+ /* Close decoder */
+ avcodec_close(dec_ctx);
+ av_free(dec_ctx);
+ dec_ctx = NULL;
+ /* Flush and close file */
+ if(wav_file != NULL) {
+ fflush(wav_file);
+ fclose(wav_file);
+ }
+ wav_file = NULL;
+}
diff --git a/postprocessing/pp-g722.h b/postprocessing/pp-g722.h
new file mode 100644
index 0000000..670f79f
--- /dev/null
+++ b/postprocessing/pp-g722.h
@@ -0,0 +1,23 @@
+/*! \file pp-g722.h
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>
+ * \copyright GNU General Public License v3
+ * \brief Post-processing to generate .wav files from G.722 (headers)
+ * \details Implementation of the post-processing code needed to
+ * generate raw .wav files out of G.722 RTP frames.
+ *
+ * \ingroup postprocessing
+ * \ref postprocessing
+ */
+
+#ifndef _JANUS_PP_G722
+#define _JANUS_PP_G722
+
+#include <stdio.h>
+
+#include "pp-rtp.h"
+
+int janus_pp_g722_create(char *destination);
+int janus_pp_g722_process(FILE *file, janus_pp_frame_packet *list, int *working);
+void janus_pp_g722_close(void);
+
+#endif
diff --git a/record.c b/record.c
index fd2dbdc..080da71 100644
--- a/record.c
+++ b/record.c
@@ -69,7 +69,9 @@ janus_recorder *janus_recorder_create(const char *dir, const char *codec, const
}
if(!strcasecmp(codec, "vp8") || !strcasecmp(codec, "vp9") || !strcasecmp(codec, "h264")) {
type = JANUS_RECORDER_VIDEO;
- } else if(!strcasecmp(codec, "opus") || !strcasecmp(codec, "g711") || !strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma")) {
+ } else if(!strcasecmp(codec, "opus")
+ || !strcasecmp(codec, "g711") || !strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma")
+ || !strcasecmp(codec, "g722")) {
type = JANUS_RECORDER_AUDIO;
if(!strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma"))
codec = "g711";
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/janus.git
More information about the Pkg-voip-commits
mailing list