[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