Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for L16 codec (uncompressed audio) #3116

Merged
merged 1 commit into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ janus_pp_rec_SOURCES = \
postprocessing/pp-g711.h \
postprocessing/pp-g722.c \
postprocessing/pp-g722.h \
postprocessing/pp-l16.c \
postprocessing/pp-l16.h \
postprocessing/pp-h264.c \
postprocessing/pp-h264.h \
postprocessing/pp-av1.c \
Expand Down
30 changes: 29 additions & 1 deletion src/postprocessing/janus-pp-rec.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Usage: janus-pp-rec [OPTIONS] source.mjr
#include "pp-opus.h"
#include "pp-g711.h"
#include "pp-g722.h"
#include "pp-l16.h"
#include "pp-srt.h"
#include "pp-binary.h"

Expand Down Expand Up @@ -237,6 +238,7 @@ int main(int argc, char *argv[]) {
JANUS_LOG(LOG_INFO, " -- Opus: %s\n", janus_pp_extensions_string(janus_pp_opus_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- G.711: %s\n", janus_pp_extensions_string(janus_pp_g711_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- G.722: %s\n", janus_pp_extensions_string(janus_pp_g722_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- L16: %s\n", janus_pp_extensions_string(janus_pp_l16_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- VP8: %s\n", janus_pp_extensions_string(janus_pp_webm_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- VP9: %s\n", janus_pp_extensions_string(janus_pp_webm_get_extensions(), supported, sizeof(supported)));
JANUS_LOG(LOG_INFO, " -- H.264: %s\n", janus_pp_extensions_string(janus_pp_h264_get_extensions(), supported, sizeof(supported)));
Expand Down Expand Up @@ -374,7 +376,7 @@ int main(int argc, char *argv[]) {
gboolean has_timestamps = FALSE;
gboolean parsed_header = FALSE;
gboolean video = FALSE, data = FALSE, textdata = FALSE;
gboolean opus = FALSE, multiopus = FALSE, g711 = FALSE, g722 = FALSE,
gboolean opus = FALSE, multiopus = FALSE, g711 = FALSE, g722 = FALSE, l16 = FALSE, l16_48k = FALSE,
vp8 = FALSE, vp9 = FALSE, h264 = FALSE, av1 = FALSE, h265 = FALSE;
int opusred_pt = 0;
gboolean e2ee = FALSE;
Expand Down Expand Up @@ -618,6 +620,16 @@ int main(int argc, char *argv[]) {
janus_pprec_options_destroy();
exit(1);
}
} else if(!strcasecmp(c, "l16") || !strcasecmp(c, "l16-48")) {
l16 = TRUE;
l16_48k = !strcasecmp(c, "l16-48");
if(extension && !janus_pp_extension_check(extension, janus_pp_l16_get_extensions())) {
JANUS_LOG(LOG_ERR, "L16 RTP packets cannot be converted to this target file, at the moment (supported formats: %s)\n",
janus_pp_extensions_string(janus_pp_l16_get_extensions(), supported, sizeof(supported)));
json_decref(info);
janus_pprec_options_destroy();
exit(1);
}
} else {
JANUS_LOG(LOG_WARN, "The post-processor only supports Opus, G.711 and G.722 audio for now (was '%s')...\n", c);
json_decref(info);
Expand Down Expand Up @@ -1143,6 +1155,8 @@ int main(int argc, char *argv[]) {
int rate = video ? 90000 : 48000;
if(g711 || g722)
rate = 8000;
else if(l16 && !l16_48k)
rate = 16000;
double ts = 0.0, pts = 0.0;
while(tmp) {
count++;
Expand Down Expand Up @@ -1353,6 +1367,14 @@ int main(int argc, char *argv[]) {
janus_pprec_options_destroy();
exit(1);
}
} else if(l16) {
if(janus_pp_l16_create(destination, l16_48k ? 48000 : 16000, metadata) < 0) {
JANUS_LOG(LOG_ERR, "Error creating .wav file...\n");
g_free(metadata);
g_free(extension);
janus_pprec_options_destroy();
exit(1);
}
}
} else if(data) {
if(textdata) {
Expand Down Expand Up @@ -1422,6 +1444,10 @@ int main(int argc, char *argv[]) {
if(janus_pp_g722_process(file, list, &working) < 0) {
JANUS_LOG(LOG_ERR, "Error processing G.722 RTP frames...\n");
}
} else if(l16) {
if(janus_pp_l16_process(file, list, &working) < 0) {
JANUS_LOG(LOG_ERR, "Error processing L16 RTP frames...\n");
}
}
} else if(data) {
if(textdata) {
Expand Down Expand Up @@ -1477,6 +1503,8 @@ int main(int argc, char *argv[]) {
janus_pp_g711_close();
} else if(g722) {
janus_pp_g722_close();
} else if(l16) {
janus_pp_l16_close();
}
}
fclose(file);
Expand Down
188 changes: 188 additions & 0 deletions src/postprocessing/pp-l16.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*! \file pp-l16.c
* \author Lorenzo Miniero <lorenzo@meetecho.com>
* \copyright GNU General Public License v3
* \brief Post-processing to generate .wav files out of L16 frames (headers)
* \details Implementation of the post-processing code needed to
* generate raw .wav files out of L16 RTP frames.
*
* \ingroup postprocessing
* \ref postprocessing
*/

#include <arpa/inet.h>
#if defined(__MACH__) || defined(__FreeBSD__)
#include <machine/endian.h>
#else
#include <endian.h>
#endif
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>

#include "pp-l16.h"
#include "../debug.h"


/* WAV header */
typedef struct janus_pp_l16_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_l16_wav;
static FILE *wav_file = NULL;

/* Supported target formats */
static const char *janus_pp_l16_formats[] = {
"wav", NULL
};
const char **janus_pp_l16_get_extensions(void) {
return janus_pp_l16_formats;
}

/* Processing methods */
static int samplerate = 0;
int janus_pp_l16_create(char *destination, int rate, char *metadata) {
samplerate = rate;
if(samplerate != 16000 && samplerate != 48000) {
JANUS_LOG(LOG_ERR, "Unsupported sample rate %d (should be 16000 or 48000)\n", rate);
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_l16_wav header = {
{'R', 'I', 'F', 'F'},
0,
{'W', 'A', 'V', 'E'},
{'f', 'm', 't', ' '},
16,
1,
1,
samplerate,
samplerate * 2,
2,
16,
{'d', 'a', 't', 'a'},
0
};
/* Note: .wav files don't seem to support arbitrary comments
* so there's nothing we can do with the provided metadata*/
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_l16_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));
size_t num_samples = samplerate/100/2;
int sr = samplerate/1000;
while(*working && tmp != NULL) {
if(tmp->prev != NULL && ((tmp->ts - tmp->prev->ts)/sr/10 > 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)/samplerate);
int silence_count = (tmp->ts - tmp->prev->ts)/sr/10 - 1;
int i=0;
for(i=0; i<silence_count; i++) {
JANUS_LOG(LOG_WARN, "[FILL] Writing silence (seq=%d, index=%d)\n",
tmp->prev->seq+i+1, i+1);
/* Add silence */
memset(samples, 0, num_samples*2);
if(wav_file != NULL) {
if(fwrite(samples, sizeof(char), num_samples*2, 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)/8000);
tmp = tmp->next;
continue;
}
if(tmp->audiolevel != -1) {
JANUS_LOG(LOG_VERB, "Audio level: %d dB\n", tmp->audiolevel);
}
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;
if(len < 1) {
tmp = tmp->next;
continue;
}
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);
tmp = tmp->next;
continue;
}
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)/samplerate);
num_samples = bytes/2;
int i=0;
for(i=0; i<(int)num_samples; i++) {
memcpy(&samples[i], buffer + i*2, sizeof(int16_t));
samples[i] = ntohs(samples[i]);
}
if(wav_file != NULL) {
if(fwrite(samples, sizeof(int16_t), num_samples, wav_file) != num_samples) {
JANUS_LOG(LOG_ERR, "Couldn't write sample...\n");
}
fflush(wav_file);
}
tmp = tmp->next;
}
g_free(buffer);
return 0;
}

void janus_pp_l16_close(void) {
/* Flush and close file */
if(wav_file != NULL) {
/* Update the header */
fseek(wav_file, 0, SEEK_END);
uint32_t size = ftell(wav_file) - 8;
fseek(wav_file, 4, SEEK_SET);
fwrite(&size, sizeof(uint32_t), 1, wav_file);
size += 8;
fseek(wav_file, 40, SEEK_SET);
fwrite(&size, sizeof(uint32_t), 1, wav_file);
fflush(wav_file);
fclose(wav_file);
}
wav_file = NULL;
}
25 changes: 25 additions & 0 deletions src/postprocessing/pp-l16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*! \file pp-l16.h
* \author Lorenzo Miniero <lorenzo@meetecho.com>
* \copyright GNU General Public License v3
* \brief Post-processing to generate .wav files out of L16 frames (headers)
* \details Implementation of the post-processing code needed to
* generate raw .wav files out of L16 RTP frames.
*
* \ingroup postprocessing
* \ref postprocessing
*/

#ifndef JANUS_PP_L16
#define JANUS_PP_L16

#include <stdio.h>

#include "pp-rtp.h"

/* L16 stuff */
const char **janus_pp_l16_get_extensions(void);
int janus_pp_l16_create(char *destination, int samplerate, char *metadata);
int janus_pp_l16_process(FILE *file, janus_pp_frame_packet *list, int *working);
void janus_pp_l16_close(void);

#endif
2 changes: 1 addition & 1 deletion src/record.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ janus_recorder *janus_recorder_create_full(const char *dir, const char *codec, c
type = JANUS_RECORDER_VIDEO;
} else if(!strcasecmp(codec, "opus") || !strcasecmp(codec, "multiopus")
|| !strcasecmp(codec, "g711") || !strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma")
|| !strcasecmp(codec, "g722")) {
|| !strcasecmp(codec, "g722") || !strcasecmp(codec, "l16-48") || !strcasecmp(codec, "l16")) {
type = JANUS_RECORDER_AUDIO;
} else if(!strcasecmp(codec, "text") || !strcasecmp(codec, "binary")) {
/* Data channels may be text or binary, so that's what we can save too */
Expand Down
14 changes: 14 additions & 0 deletions src/rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ const char *janus_srtp_error_str(int error) {
#define PCMU_PT 0
#define PCMA_PT 8
#define G722_PT 9
#define L16_48_PT 105
#define L16_PT 106
#define VP8_PT 96
#define VP9_PT 101
#define H264_PT 107
Expand All @@ -898,6 +900,10 @@ const char *janus_audiocodec_name(janus_audiocodec acodec) {
return "isac32";
case JANUS_AUDIOCODEC_ISAC_16K:
return "isac16";
case JANUS_AUDIOCODEC_L16_48K:
return "l16-48";
case JANUS_AUDIOCODEC_L16_16K:
return "l16";
default:
/* Shouldn't happen */
return "opus";
Expand All @@ -922,6 +928,10 @@ janus_audiocodec janus_audiocodec_from_name(const char *name) {
return JANUS_AUDIOCODEC_PCMA;
else if(!strcasecmp(name, "g722"))
return JANUS_AUDIOCODEC_G722;
else if(!strcasecmp(name, "l16-48"))
return JANUS_AUDIOCODEC_L16_48K;
else if(!strcasecmp(name, "l16"))
return JANUS_AUDIOCODEC_L16_16K;
JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s'\n", name);
return JANUS_AUDIOCODEC_NONE;
}
Expand All @@ -945,6 +955,10 @@ int janus_audiocodec_pt(janus_audiocodec acodec) {
return PCMA_PT;
case JANUS_AUDIOCODEC_G722:
return G722_PT;
case JANUS_AUDIOCODEC_L16_48K:
return L16_48_PT;
case JANUS_AUDIOCODEC_L16_16K:
return L16_PT;
default:
/* Shouldn't happen */
return OPUS_PT;
Expand Down
4 changes: 3 additions & 1 deletion src/rtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ typedef enum janus_audiocodec {
JANUS_AUDIOCODEC_PCMA,
JANUS_AUDIOCODEC_G722,
JANUS_AUDIOCODEC_ISAC_32K,
JANUS_AUDIOCODEC_ISAC_16K
JANUS_AUDIOCODEC_ISAC_16K,
JANUS_AUDIOCODEC_L16_48K,
JANUS_AUDIOCODEC_L16_16K
} janus_audiocodec;
const char *janus_audiocodec_name(janus_audiocodec acodec);
janus_audiocodec janus_audiocodec_from_name(const char *name);
Expand Down
Loading