Skip to content

Commit

Permalink
Add support for L16 codec (raw samples) (#3116)
Browse files Browse the repository at this point in the history
  • Loading branch information
lminiero committed Dec 19, 2022
1 parent dc4e2b9 commit e139a1c
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 4 deletions.
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 <[email protected]>
* \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 <[email protected]>
* \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

0 comments on commit e139a1c

Please sign in to comment.