[Pkg-voip-commits] [janus] 88/163: Made Record&Play plugin codec-agnostic (was opus/vp8 only before)
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 f0ae6d18c2995f1daa72c996e341d6f87f7bcbcb
Author: Lorenzo Miniero <lminiero at gmail.com>
Date: Tue Sep 5 13:14:55 2017 +0200
Made Record&Play plugin codec-agnostic (was opus/vp8 only before)
---
plugins/janus_recordplay.c | 252 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 218 insertions(+), 34 deletions(-)
diff --git a/plugins/janus_recordplay.c b/plugins/janus_recordplay.c
index 186b228..77a5921 100644
--- a/plugins/janus_recordplay.c
+++ b/plugins/janus_recordplay.c
@@ -377,7 +377,9 @@ typedef struct janus_recordplay_recording {
char *name; /* Name of the recording */
char *date; /* Time of the recording */
char *arc_file; /* Audio file name */
+ const char *acodec; /* Codec used for audio, if available */
char *vrc_file; /* Video file name */
+ const char *vcodec; /* Codec used for video, if available */
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 */
@@ -421,27 +423,171 @@ static void *janus_recordplay_playout_thread(void *data);
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 */
-#define OPUS_PT 111
-#define VP8_PT 100
+#define AUDIO_PT 111
+#define VIDEO_PT 100
+
+/* Preferred codecs when negotiating audio and video to record */
+static const char *preferred_audio_codecs[] = {
+ "opus", "pcmu", "pcma", "g722", "isac16", "isac32"
+};
+static uint audio_codecs = sizeof(preferred_audio_codecs)/sizeof(*preferred_audio_codecs);
+static const char *preferred_video_codecs[] = {
+ "vp8", "vp9", "h264"
+};
+static uint video_codecs = sizeof(preferred_video_codecs)/sizeof(*preferred_video_codecs);
+
+/* Helper method to check which codec was used in a specific recording */
+static const char *janus_recordplay_parse_codec(const char *dir, const char *filename) {
+ if(dir == NULL || filename == NULL)
+ return NULL;
+ char source[1024];
+ if(strstr(filename, ".mjr"))
+ g_snprintf(source, 1024, "%s/%s", dir, filename);
+ else
+ g_snprintf(source, 1024, "%s/%s.mjr", dir, filename);
+ FILE *file = fopen(source, "rb");
+ if(file == NULL) {
+ JANUS_LOG(LOG_ERR, "Could not open file %s\n", source);
+ return NULL;
+ }
+ fseek(file, 0L, SEEK_END);
+ long fsize = ftell(file);
+ fseek(file, 0L, SEEK_SET);
+
+ /* Pre-parse */
+ JANUS_LOG(LOG_VERB, "Pre-parsing file %s to generate ordered index...\n", source);
+ gboolean parsed_header = FALSE;
+ int bytes = 0;
+ long offset = 0;
+ uint16_t len = 0;
+ char prebuffer[1500];
+ memset(prebuffer, 0, 1500);
+ /* Let's look for timestamp resets first */
+ while(offset < fsize) {
+ /* Read frame header */
+ fseek(file, offset, SEEK_SET);
+ bytes = fread(prebuffer, sizeof(char), 8, file);
+ if(bytes != 8 || prebuffer[0] != 'M') {
+ JANUS_LOG(LOG_ERR, "Invalid header...\n");
+ fclose(file);
+ return NULL;
+ }
+ if(prebuffer[1] == 'E') {
+ /* Either the old .mjr format header ('MEETECHO' header followed by 'audio' or 'video'), or a frame */
+ offset += 8;
+ bytes = fread(&len, sizeof(uint16_t), 1, file);
+ len = ntohs(len);
+ offset += 2;
+ if(len == 5 && !parsed_header) {
+ /* This is the main header */
+ parsed_header = TRUE;
+ bytes = fread(prebuffer, sizeof(char), 5, file);
+ if(prebuffer[0] == 'v') {
+ JANUS_LOG(LOG_VERB, "This is an old video recording, assuming VP8\n");
+ return preferred_video_codecs[0];
+ } else if(prebuffer[0] == 'a') {
+ JANUS_LOG(LOG_VERB, "This is an old audio recording, assuming Opus\n");
+ return preferred_audio_codecs[0];
+ }
+ }
+ JANUS_LOG(LOG_WARN, "Unsupported recording media type...\n");
+ fclose(file);
+ return NULL;
+ } else if(prebuffer[1] == 'J') {
+ /* New .mjr format */
+ offset += 8;
+ bytes = fread(&len, sizeof(uint16_t), 1, file);
+ len = ntohs(len);
+ offset += 2;
+ if(len > 0 && !parsed_header) {
+ /* This is the info header */
+ bytes = fread(prebuffer, sizeof(char), len, file);
+ if(bytes < 0) {
+ JANUS_LOG(LOG_ERR, "Error reading from file... %s\n", strerror(errno));
+ fclose(file);
+ return NULL;
+ }
+ parsed_header = TRUE;
+ prebuffer[len] = '\0';
+ json_error_t error;
+ json_t *info = json_loads(prebuffer, 0, &error);
+ if(!info) {
+ JANUS_LOG(LOG_ERR, "JSON error: on line %d: %s\n", error.line, error.text);
+ JANUS_LOG(LOG_WARN, "Error parsing info header...\n");
+ fclose(file);
+ return NULL;
+ }
+ /* Is it audio or video? */
+ json_t *type = json_object_get(info, "t");
+ if(!type || !json_is_string(type)) {
+ JANUS_LOG(LOG_WARN, "Missing/invalid recording type in info header...\n");
+ json_decref(info);
+ fclose(file);
+ return NULL;
+ }
+ const char *t = json_string_value(type);
+ gboolean video = FALSE;
+ if(!strcasecmp(t, "v")) {
+ video = TRUE;
+ } else if(!strcasecmp(t, "a")) {
+ video = FALSE;
+ } else {
+ JANUS_LOG(LOG_WARN, "Unsupported recording type '%s' in info header...\n", t);
+ json_decref(info);
+ fclose(file);
+ return NULL;
+ }
+ /* What codec was used? */
+ json_t *codec = json_object_get(info, "c");
+ if(!codec || !json_is_string(codec)) {
+ JANUS_LOG(LOG_WARN, "Missing recording codec in info header...\n");
+ json_decref(info);
+ fclose(file);
+ return NULL;
+ }
+ const char *c = json_string_value(codec);
+ uint i=0;
+ for(i=0; i<(video ? video_codecs : audio_codecs); i++) {
+ if(!strcasecmp(c, (video ? preferred_video_codecs[i] : preferred_audio_codecs[i]))) {
+ /* Found! */
+ json_decref(info);
+ fclose(file);
+ return video ? preferred_video_codecs[i] : preferred_audio_codecs[i];
+ }
+ }
+ json_decref(info);
+ }
+ JANUS_LOG(LOG_WARN, "No codec found...\n");
+ fclose(file);
+ return NULL;
+ } else {
+ JANUS_LOG(LOG_ERR, "Invalid header...\n");
+ fclose(file);
+ return NULL;
+ }
+ }
+ fclose(file);
+ return NULL;
+}
/* Helper method to prepare an SDP offer when a recording is available */
static int janus_recordplay_generate_offer(janus_recordplay_recording *rec) {
if(rec == NULL)
return -1;
/* Prepare an SDP offer we'll send to playout viewers */
- gboolean offer_audio = (rec->arc_file != NULL),
- offer_video = (rec->vrc_file != NULL);
+ gboolean offer_audio = (rec->arc_file != NULL && rec->acodec != NULL),
+ offer_video = (rec->vrc_file != NULL && rec->vcodec != NULL);
char s_name[100];
g_snprintf(s_name, sizeof(s_name), "Recording %"SCNu64, rec->id);
janus_sdp *offer = janus_sdp_generate_offer(
s_name, "1.1.1.1",
JANUS_SDP_OA_AUDIO, offer_audio,
- JANUS_SDP_OA_AUDIO_CODEC, "opus",
- JANUS_SDP_OA_AUDIO_PT, OPUS_PT,
+ JANUS_SDP_OA_AUDIO_CODEC, rec->acodec,
+ JANUS_SDP_OA_AUDIO_PT, AUDIO_PT,
JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_VIDEO, offer_video,
- JANUS_SDP_OA_VIDEO_CODEC, "vp8",
- JANUS_SDP_OA_VIDEO_PT, VP8_PT,
+ JANUS_SDP_OA_VIDEO_CODEC, rec->vcodec,
+ JANUS_SDP_OA_VIDEO_PT, VIDEO_PT,
JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_DATA, FALSE,
JANUS_SDP_OA_DONE);
@@ -795,7 +941,11 @@ struct janus_plugin_result *janus_recordplay_handle_message(janus_plugin_session
json_object_set_new(ml, "name", json_string(rec->name));
json_object_set_new(ml, "date", json_string(rec->date));
json_object_set_new(ml, "audio", rec->arc_file ? json_true() : json_false());
+ if(rec->acodec)
+ json_object_set_new(ml, "audio_codec", json_string(rec->acodec));
json_object_set_new(ml, "video", rec->vrc_file ? json_true() : json_false());
+ if(rec->vcodec)
+ json_object_set_new(ml, "video_codec", json_string(rec->vcodec));
json_array_append_new(list, ml);
}
janus_mutex_unlock(&recordings_mutex);
@@ -1134,13 +1284,54 @@ static void *janus_recordplay_handler(void *data) {
rec->completed = FALSE;
rec->offer = NULL;
janus_mutex_init(&rec->mutex);
+ /* Check which codec we should record for audio and/or video */
+ gboolean audio = FALSE, video = FALSE;
+ GList *temp = offer->m_lines;
+ while(temp) {
+ /* Which media are available? */
+ janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
+ if(m->type == JANUS_SDP_AUDIO && m->port > 0 &&
+ m->direction != JANUS_SDP_RECVONLY && m->direction != JANUS_SDP_INACTIVE) {
+ audio = TRUE;
+ if(rec->acodec == NULL) {
+ uint i=0;
+ for(i=0; i<audio_codecs; i++) {
+ if(janus_sdp_get_codec_pt(offer, preferred_audio_codecs[i]) > 0) {
+ rec->acodec = preferred_audio_codecs[i];
+ break;
+ }
+ }
+ if(rec->acodec == NULL) {
+ JANUS_LOG(LOG_WARN, "No supported audio codec found..?\n");
+ audio = FALSE;
+ }
+ }
+ } else if(m->type == JANUS_SDP_VIDEO && m->port > 0 &&
+ m->direction != JANUS_SDP_RECVONLY && m->direction != JANUS_SDP_INACTIVE) {
+ video = TRUE;
+ if(rec->vcodec == NULL) {
+ uint i=0;
+ for(i=0; i<video_codecs; i++) {
+ if(janus_sdp_get_codec_pt(offer, preferred_video_codecs[i]) > 0) {
+ rec->vcodec = preferred_video_codecs[i];
+ break;
+ }
+ }
+ if(rec->vcodec == NULL) {
+ JANUS_LOG(LOG_WARN, "No supported video codec found..?\n");
+ video = FALSE;
+ }
+ }
+ }
+ temp = temp->next;
+ }
/* Create a date string */
time_t t = time(NULL);
struct tm *tmv = localtime(&t);
char outstr[200];
strftime(outstr, sizeof(outstr), "%Y-%m-%d %H:%M:%S", tmv);
rec->date = g_strdup(outstr);
- if(strstr(msg_sdp, "m=audio")) {
+ if(audio) {
char filename[256];
if(filename_text != NULL) {
g_snprintf(filename, 256, "%s-audio", filename_text);
@@ -1148,10 +1339,9 @@ static void *janus_recordplay_handler(void *data) {
g_snprintf(filename, 256, "rec-%"SCNu64"-audio", id);
}
rec->arc_file = g_strdup(filename);
- /* FIXME Assuming Opus */
- session->arc = janus_recorder_create(recordings_path, "opus", rec->arc_file);
+ session->arc = janus_recorder_create(recordings_path, rec->acodec, rec->arc_file);
}
- if(strstr(msg_sdp, "m=video")) {
+ if(video) {
char filename[256];
if(filename_text != NULL) {
g_snprintf(filename, 256, "%s-video", filename_text);
@@ -1159,8 +1349,7 @@ static void *janus_recordplay_handler(void *data) {
g_snprintf(filename, 256, "rec-%"SCNu64"-video", id);
}
rec->vrc_file = g_strdup(filename);
- /* FIXME Assuming VP8 */
- session->vrc = janus_recorder_create(recordings_path, "vp8", rec->vrc_file);
+ session->vrc = janus_recorder_create(recordings_path, rec->vcodec, rec->vrc_file);
}
session->recorder = TRUE;
session->recording = rec;
@@ -1169,9 +1358,11 @@ static void *janus_recordplay_handler(void *data) {
janus_mutex_unlock(&recordings_mutex);
/* We need to prepare an answer */
janus_sdp *answer = janus_sdp_generate_answer(offer,
- JANUS_SDP_OA_AUDIO_CODEC, "opus",
+ JANUS_SDP_OA_AUDIO, audio,
+ JANUS_SDP_OA_AUDIO_CODEC, rec->acodec,
JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
- JANUS_SDP_OA_VIDEO_CODEC, "vp8",
+ JANUS_SDP_OA_VIDEO, video,
+ JANUS_SDP_OA_VIDEO_CODEC, rec->vcodec,
JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_RECVONLY,
JANUS_SDP_OA_DATA, FALSE,
JANUS_SDP_OA_DONE);
@@ -1508,12 +1699,16 @@ void janus_recordplay_update_recordings_list(void) {
char *ext = strstr(rec->arc_file, ".mjr");
if(ext != NULL)
*ext = '\0';
+ /* Check which codec is in this recording */
+ rec->acodec = janus_recordplay_parse_codec(recordings_path, rec->arc_file);
}
if(video && video->value) {
rec->vrc_file = g_strdup(video->value);
char *ext = strstr(rec->vrc_file, ".mjr");
if(ext != NULL)
*ext = '\0';
+ /* Check which codec is in this recording */
+ rec->vcodec = janus_recordplay_parse_codec(recordings_path, rec->vrc_file);
}
rec->viewers = NULL;
rec->destroyed = 0;
@@ -1612,9 +1807,9 @@ janus_recordplay_frame_packet *janus_recordplay_get_frames(const char *dir, cons
JANUS_LOG(LOG_VERB, "Old .mjr header format\n");
bytes = fread(prebuffer, sizeof(char), 5, file);
if(prebuffer[0] == 'v') {
- JANUS_LOG(LOG_INFO, "This is a video recording, assuming VP8\n");
+ JANUS_LOG(LOG_INFO, "This is an old video recording, assuming VP8\n");
} else if(prebuffer[0] == 'a') {
- JANUS_LOG(LOG_INFO, "This is an audio recording, assuming Opus\n");
+ JANUS_LOG(LOG_INFO, "This is an old audio recording, assuming Opus\n");
} else {
JANUS_LOG(LOG_WARN, "Unsupported recording media type...\n");
fclose(file);
@@ -1683,17 +1878,6 @@ janus_recordplay_frame_packet *janus_recordplay_get_frames(const char *dir, cons
return NULL;
}
const char *c = json_string_value(codec);
- if(video && strcasecmp(c, "vp8")) {
- JANUS_LOG(LOG_WARN, "The Record&Play plugin only supports VP8 video for now (was '%s')...\n", c);
- json_decref(info);
- fclose(file);
- return NULL;
- } else if(!video && strcasecmp(c, "opus")) {
- JANUS_LOG(LOG_WARN, "The Record&Play plugin only supports Opus audio for now (was '%s')...\n", c);
- json_decref(info);
- fclose(file);
- return NULL;
- }
/* When was the file created? */
json_t *created = json_object_get(info, "s");
if(!created || !json_is_integer(created)) {
@@ -1963,7 +2147,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 = OPUS_PT; /* FIXME We assume it's Opus */
+ rtp->type = AUDIO_PT;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 0, (char *)buffer, bytes);
gettimeofday(&now, NULL);
@@ -1974,7 +2158,7 @@ static void *janus_recordplay_playout_thread(void *data) {
} else {
/* What's the timestamp skip from the previous packet? */
ts_diff = audio->ts - audio->prev->ts;
- ts_diff = (ts_diff*1000)/48; /* FIXME Again, we're assuming Opus and it's 48khz */
+ ts_diff = (ts_diff*1000)/48; /* FIXME We're assuming Opus and 48khz */
/* Check if it's time to send */
gettimeofday(&now, NULL);
d_s = now.tv_sec - abefore.tv_sec;
@@ -2004,7 +2188,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 = OPUS_PT; /* FIXME We assume it's Opus */
+ rtp->type = AUDIO_PT;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 0, (char *)buffer, bytes);
asent = TRUE;
@@ -2023,7 +2207,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 = VP8_PT; /* FIXME We assume it's VP8 */
+ rtp->type = VIDEO_PT;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 1, (char *)buffer, bytes);
video = video->next;
@@ -2068,7 +2252,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 = VP8_PT; /* FIXME We assume it's VP8 */
+ rtp->type = VIDEO_PT;
if(gateway != NULL)
gateway->relay_rtp(session->handle, 1, (char *)buffer, bytes);
video = video->next;
--
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