[svn-commits] phsultan: branch phsultan/rtmp-support r278874 - in /team/phsultan/rtmp-suppo...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jul 23 08:28:53 CDT 2010


Author: phsultan
Date: Fri Jul 23 08:28:49 2010
New Revision: 278874

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=278874
Log:
Fixes to FFPlayback, semi implementation of RFC 4629 in our RTP engine. 

RFC 4629 (H.263 RTP Packetization) is now implemented in our default RTP
engine (res/res_rtp_asterisk.c).

We used to rely on FFMPEG to send the video RTP packets, this is not needed
anymore since we now have an implementation of RFC 4629 (H.263 RTP
packetization) to send our video frames. Video frames are now handled just like
audio frames, that is :
- take an encoded video frame from the file
- decode it with FFMPEG
- encode it to H.263 with FFMPEG
- copy this content to a new Asterisk frame
- pass the frame to the remote channel using ast_write

Also fixed various compile warnings in app_ffplayback.c.

Video successfully tested with X-Lite, audio successfully tested with Zoiper.
(X-lite on MAC is buggy, no sound, and Zoiper can't do video).

Modified:
    team/phsultan/rtmp-support/apps/app_ffplayback.c
    team/phsultan/rtmp-support/include/asterisk/rtp_engine.h
    team/phsultan/rtmp-support/res/res_rtp_asterisk.c

Modified: team/phsultan/rtmp-support/apps/app_ffplayback.c
URL: http://svnview.digium.com/svn/asterisk/team/phsultan/rtmp-support/apps/app_ffplayback.c?view=diff&rev=278874&r1=278873&r2=278874
==============================================================================
--- team/phsultan/rtmp-support/apps/app_ffplayback.c (original)
+++ team/phsultan/rtmp-support/apps/app_ffplayback.c Fri Jul 23 08:28:49 2010
@@ -52,10 +52,8 @@
 static void *video_thread(void *data);
 static void *audio_thread(void *data);
 static int open_decoding_context(AVCodecContext *cctx, int codecid);
-static int open_encoding_context(AVCodecContext *cctx, int codecid);
 static int open_context(AVCodecContext *cctx, int codecid, int decoding);
-static AVStream *add_video_stream(AVFormatContext *oc, enum CodecID codec_id, int width, int height);
-static struct SwsContext *init_img_converter(AVCodecContext *in_vcctx, AVStream *video_st);
+static struct SwsContext *init_img_converter(AVCodecContext *in_vcctx, AVCodecContext *out_vcctx);
 
 /*** DOCUMENTATION
 	<application name="FFPlayback" language="en_US">
@@ -99,8 +97,6 @@
 	struct ast_channel *chan;
 	AVCodecContext *in_cctx;
 	AVFormatContext *fctx;
-	AVFormatContext *rtp_fctx;
-	AVOutputFormat *rtp_fmt;
 	int stream_index;
 };
 
@@ -150,88 +146,13 @@
 	pthread_t vthread = AST_PTHREADT_NULL;
 	pthread_t athread = AST_PTHREADT_NULL;
 
-	/* rtp related stuff */
-	AVOutputFormat *rtp_fmt;
-	AVFormatContext *rtp_fctx;
-	AVStream *video_st;
-	URLContext *h;
-	struct sockaddr_in a_rtpaddr;
-	struct sockaddr_in v_rtpaddr;
-	struct sockaddr_in local_v_rtpaddr;
-	struct ast_rtp_glue *glue;
-	static struct ast_rtp_instance *artp;
-	static struct ast_rtp_instance *vrtp;
-	char rtp_vurl[200];
-
 	struct stream_info ainfo;
 	struct stream_info vinfo;
 
 	in_vcctx = NULL;
 	in_acctx = NULL;
-	rtp_fctx = NULL;
-	rtp_fmt = NULL;
-	video_st = NULL;
-	h = NULL;
 	memset(&ainfo, 0, sizeof(ainfo));
 	memset(&vinfo, 0, sizeof(vinfo));
-
-	glue = ast_rtp_instance_get_glue(chan->tech->type);
-	if (!glue) {
-		ast_log(LOG_WARNING, "Channel is not RTP capable, can't playback.\n");
-		return res;
-	}
-
-	if (glue->get_rtp_info(chan, &artp) == AST_RTP_GLUE_RESULT_FORBID || !artp) {
-		ast_log(LOG_WARNING, "RTP audio forbidden on this channel.\n");
-		return res;
-	}
-
-	if (glue->get_vrtp_info(chan, &vrtp) == AST_RTP_GLUE_RESULT_FORBID || !vrtp) {
-		ast_log(LOG_WARNING, "RTP video forbidden on this channel.\n");
-		return res;
-	}
-
-
-	ast_rtp_instance_get_remote_address(artp, &a_rtpaddr);
-	ast_rtp_instance_get_remote_address(vrtp, &v_rtpaddr);
-
-	ast_log(LOG_NOTICE, "Audio RTP address : %s - port %d\n", ast_inet_ntoa(a_rtpaddr.sin_addr), ntohs(a_rtpaddr.sin_port));
-	ast_log(LOG_NOTICE, "Video RTP address : %s - port %d\n", ast_inet_ntoa(v_rtpaddr.sin_addr), ntohs(v_rtpaddr.sin_port));
-
-	rtp_fmt = av_guess_format("rtp", NULL, NULL);
-	rtp_fmt->video_codec = CODEC_ID_H263;
-
-	rtp_fctx = avformat_alloc_context();
-	if (!rtp_fctx) {
-		ast_log(LOG_WARNING, "Memory error\n");
-		return res;
-	}
-
-	rtp_fctx->oformat = rtp_fmt;
-
-	/* Now, we will replace Asterisk's video RTP socket with the one that 
-	 * has been created by FFMPEG's. Why? Well FFMPEG implements video RTP
-	 * packetization (RFC 4629), and Asterisk does not, yet */
-	ast_rtp_instance_get_local_address(vrtp, &local_v_rtpaddr);
-	ast_log(LOG_NOTICE, "Local video RTP address : %s - port %d\n", ast_inet_ntoa(local_v_rtpaddr.sin_addr), ntohs(local_v_rtpaddr.sin_port));
-	snprintf(rtp_vurl, sizeof(rtp_vurl), "rtp://%s:%d?localrtpport=%d", ast_inet_ntoa(a_rtpaddr.sin_addr), ntohs(v_rtpaddr.sin_port), ntohs(local_v_rtpaddr.sin_port));
-
-	/* Stop Asterisk's video RTP structure */
-	ao2_ref(vrtp, -1);
-	ast_rtp_instance_destroy(vrtp);
-	glue->set_vrtp_null(chan);
-	usleep(1000000);
-
-	/* Connect to the video RTP peer */
-	snprintf(rtp_fctx->filename, sizeof(rtp_fctx->filename), "%s", rtp_vurl);
-	if (url_open(&h, rtp_fctx->filename, URL_WRONLY) < 0) {
-		ast_log(LOG_WARNING, "Could not open URL : %s\n", rtp_fctx->filename);
-		goto safeout;
-	}
-	if (url_fdopen(&rtp_fctx->pb, h) < 0) {
-		ast_log(LOG_WARNING, "Could not open URL : %s\n", rtp_fctx->filename);
-		goto safeout;	
-	}
 
 	/* Set our format context based on the stream file */
 	if (av_open_input_file(&afctx, url, NULL, 0, NULL) != 0) {
@@ -309,14 +230,10 @@
 	ainfo.chan = chan;
 	ainfo.in_cctx = in_acctx;
 	ainfo.fctx = afctx;
-	ainfo.rtp_fctx = NULL;
-	ainfo.rtp_fmt = NULL;
 
 	vinfo.chan = chan;
 	vinfo.in_cctx = in_vcctx;
 	vinfo.fctx = vfctx;
-	vinfo.rtp_fctx = rtp_fctx;
-	vinfo.rtp_fmt = rtp_fmt;
 
 	if (ast_pthread_create(&vthread, NULL, video_thread, &vinfo) < 0) {
 		ast_log(LOG_WARNING, "Could not create video thread\n");
@@ -339,15 +256,6 @@
 	if (in_acctx) {
 		avcodec_close(in_acctx);
 	}
-	if (video_st && video_st->codec) {
-		avcodec_close(video_st->codec);
-	}
-	if (video_st) {
-		av_freep(video_st);
-	}
-	if (h) {
-		url_close(h);
-	}
 
 	return res;
 }
@@ -356,11 +264,10 @@
 {
 	struct ast_channel *chan = NULL;
 	AVCodecContext *in_vcctx = NULL;
+	AVCodec *out_vcodec = NULL;
+	AVCodecContext *out_vcctx = NULL;
 	AVFormatContext *fctx = NULL;
-	AVFormatContext *rtp_fctx = NULL;
-	AVOutputFormat *rtp_fmt = NULL;
 	struct SwsContext *img_convert_ctx = NULL;
-	AVStream *video_st = NULL;
 	AVFrame *frame; 
 	AVFrame *frame_rgb;
 	AVPacket packet;
@@ -370,41 +277,46 @@
 	int out_bufsize;	/* Size reserved for the decoded video frame */
 	int out_size;		/* Actual size of the encoded video frame */
 	uint8_t *out_buffer;	/* Encoded frame */
-	struct ast_frame outf;
 	uint64_t start_time;	/* The time when the app was called */
 	uint64_t pts;		/* Presentation timestamp from the input stream */	
 	uint64_t now;		/* The elapsed time from start_time */
 	int pps;		/* The number of frames per second in the input stream */
 	int vstream = -1;
 	struct stream_info *aux = data;
+	struct ast_frame aframe;
 
 	chan = aux->chan;
 	fctx = aux->fctx;
-	rtp_fctx = aux->rtp_fctx;
 	in_vcctx = aux->in_cctx;
-	rtp_fmt = aux->rtp_fmt;
 	vstream = aux->stream_index;
-
-	/* Get the video RTP stream structure */
-	video_st = add_video_stream(rtp_fctx, rtp_fmt->video_codec, 176, 144);
-	if (!video_st) {
-		ast_log(LOG_WARNING, "Could not create video RTP stream.\n");
+	frame = NULL;
+	frame_rgb = NULL;
+
+	out_vcodec = avcodec_find_encoder(CODEC_ID_H263);
+	if (!out_vcodec) {
+		ast_log(LOG_WARNING, "Could not find any codec to encode video.\n");
 		return NULL;
 	}
 
-	/* Set the output parameters (must be done even if no parameters). */
-	if (av_set_parameters(rtp_fctx, NULL) < 0) {
-		ast_log(LOG_WARNING, "Invalid output format parameters.\n");
+	out_vcctx = avcodec_alloc_context();
+	out_vcctx->codec_id = CODEC_ID_H263;
+	out_vcctx->codec_type = AVMEDIA_TYPE_VIDEO;
+	out_vcctx->bit_rate = 90000;
+	out_vcctx->width = 176;
+	out_vcctx->height = 144;
+	out_vcctx->time_base = (AVRational){1,25};
+	out_vcctx->pix_fmt = PIX_FMT_YUV420P;
+
+	if (avcodec_open(out_vcctx, out_vcodec) < 0) {
+		ast_log(LOG_WARNING, "Could not open output video codec context.\n");
 		return NULL;
 	}
 
-	if (!open_encoding_context(video_st->codec, CODEC_ID_H263)) {
-		ast_log(LOG_WARNING, "Could not open encoding context.\n");
-		return NULL;
-	}
-
 	/* Zero out our outgoing frame */
-	memset(&outf, 0, sizeof(outf));
+	memset(&aframe, 0, sizeof(aframe));
+	aframe.frametype = AST_FRAME_VIDEO;
+	aframe.subclass.codec = AST_FORMAT_H263_PLUS;
+	aframe.src = "FFPlayback";
 
 	/* Allocate space for out_buffer, which must be larger than 
 	 * FF_MIN_BUFFER_SIZE (16384) */ 
@@ -419,7 +331,6 @@
 	}
 
 	frame_rgb = avcodec_alloc_frame();
-
 	if (!frame_rgb) {
 		ast_log(LOG_WARNING, "Memory allocation problem\n");
 		goto safeout;
@@ -433,14 +344,11 @@
 	avpicture_fill((AVPicture *)frame_rgb, in_buffer, PIX_FMT_YUV420P, in_vcctx->width, in_vcctx->height);
 
 	/* Allocate image converter context */
-	img_convert_ctx = init_img_converter(in_vcctx, video_st);
+	img_convert_ctx = init_img_converter(in_vcctx, out_vcctx);
 	if(img_convert_ctx == NULL) {
 		ast_log(LOG_WARNING, "Could not initialize the conversion context\n");
 		goto safeout;
 	}
-
-	/* Write header */
-	av_write_header(rtp_fctx);
 
 	/* Mark the start time */
 	start_time = av_gettime();
@@ -451,7 +359,7 @@
 			continue;
 		}
 		int ret;
-		ret = avcodec_decode_video(in_vcctx, frame, &frameFinished, packet.data, packet.size);
+		ret = avcodec_decode_video2(in_vcctx, frame, &frameFinished, &packet);
 
 		/* Did we get a complete video frame ? */
 		if(!frameFinished) {
@@ -463,7 +371,6 @@
 		 * So we have to feed him with frames coming at a pace that equals
 		 * the one that's marked in the input stream.
 		 * This is similar to the -re (rate emulation) option in ffmpeg. */
-		//pps = fctx->streams[vstream]->r_frame_rate.num ? fctx->streams[vstream]->r_frame_rate.num : 25;
 		pps = fctx->streams[vstream]->time_base.den;
 		now = av_gettime() - start_time;
 		pts = av_rescale(packet.pts, 1000000, pps);
@@ -472,29 +379,23 @@
 		}
 
 		/* Rescale image */
-		ret = sws_scale(img_convert_ctx, frame->data, frame->linesize, 0, in_vcctx->height, frame_rgb->data, frame_rgb->linesize);
+		ret = sws_scale(img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize, 0, in_vcctx->height, frame_rgb->data, frame_rgb->linesize);
 
 		/* Encode the frame */
-		out_size = avcodec_encode_video(video_st->codec, out_buffer, out_bufsize, frame_rgb);
+		out_size = avcodec_encode_video(out_vcctx, out_buffer, out_bufsize, frame_rgb);
 		if (out_size) {
-			AVPacket pkt;
-			av_init_packet(&pkt);
-
-			pkt.stream_index= video_st->index;
-			pkt.data= out_buffer;
-			pkt.size= out_size;
-			ret = av_interleaved_write_frame(rtp_fctx, &pkt);
-		}
+			aframe.datalen = out_size;
+			aframe.data.ptr = out_buffer;
+			aframe.delivery.tv_sec = 0;
+			aframe.delivery.tv_usec = pts;
+			ast_write(chan, &aframe);
+		}
+
 		/* Free the packet that was allocated by av_read_frame */
 		av_free_packet(&packet);
 	}
 
-	/* write the trailer, if any.  the trailer must be written
-	 * before you close the CodecContexts open when you wrote the
-	 * header; otherwise write_trailer may try to use memory that
-	 * was freed on av_codec_close() */
-	av_write_trailer(rtp_fctx);
-
+	ast_free(out_buffer);
 	ast_log(LOG_NOTICE, "Ended reading video.\n");
 
 safeout:
@@ -503,6 +404,8 @@
 	}
 	av_free(frame);
 	av_free(frame_rgb);
+	avcodec_close(out_vcctx);
+	av_free(out_vcctx);
 
 	pthread_exit(NULL);
 
@@ -546,11 +449,11 @@
 	/* Mark the start time */
 	start_time = av_gettime();
 
-	ast_log(LOG_WARNING, "Now reading audio input...\n");
+	ast_log(LOG_NOTICE, "Now reading audio input...\n");
 	while(!ast_check_hangup(chan) && av_read_frame(fctx, &packet)>=0) {
 		int ret;
-		uint16_t *rawsamples;
-		uint16_t samples[1024];
+		int16_t *rawsamples;
+		int16_t samples[1024];
 		int rawsampleslen = AVCODEC_MAX_AUDIO_FRAME_SIZE;
 		struct ast_frame *list;
 		struct ast_frame *cur;
@@ -565,7 +468,6 @@
 		 * So we have to feed him with frames coming at a pace that equals
 		 * the one that's marked in the input stream.
 		 * This is similar to the -re (rate emulation) option in ffmpeg. */
-		//pps = fctx->streams[vstream]->r_frame_rate.num ? fctx->streams[vstream]->r_frame_rate.num : 25;
 		pps = fctx->streams[astream]->time_base.den;
 		now = av_gettime() - start_time;
 		pts = av_rescale(packet.pts, 1000000, pps);
@@ -607,12 +509,12 @@
 	return NULL;
 }
 
-static struct SwsContext *init_img_converter(AVCodecContext *in_vcctx, AVStream *video_st)
+static struct SwsContext *init_img_converter(AVCodecContext *in_vcctx, AVCodecContext *out_vcctx)
 {
 	int in_width = in_vcctx->width;
 	int in_height = in_vcctx->height;
-	int out_width = video_st->codec->width;
-	int out_height = video_st->codec->height;
+	int out_width = out_vcctx->width;
+	int out_height = out_vcctx->height;
 
 	return  sws_getContext(in_width, in_height, 
 			in_vcctx->pix_fmt, 
@@ -640,39 +542,9 @@
 	return 1;
 }
 
-static int open_encoding_context(AVCodecContext *cctx, int codecid)
-{
-	return open_context(cctx, codecid, 0);
-}
-
 static int open_decoding_context(AVCodecContext *cctx, int codecid)
 {
 	return open_context(cctx, codecid, 1);
-}
-
-/* add a video output stream */
-static AVStream *add_video_stream(AVFormatContext *oc, enum CodecID codec_id, int width, int height)
-{
-	AVCodecContext *c;
-	AVStream *st;
-
-	st = av_new_stream(oc, 0);
-	if (!st) {
-		ast_log(LOG_WARNING, "Memory error\n");
-		return NULL;
-	}
-
-	c = st->codec;
-	c->codec_id = codec_id;
-	c->codec_type = AVMEDIA_TYPE_VIDEO;
-	c->bit_rate = 90000;
-	c->width = width;
-	c->height = height;
-	c->time_base = (AVRational){1,25};
-	c->pix_fmt = PIX_FMT_YUV420P;
-
-	st->time_base = c->time_base;
-	return st;
 }
 
 static int unload_module(void)

Modified: team/phsultan/rtmp-support/include/asterisk/rtp_engine.h
URL: http://svnview.digium.com/svn/asterisk/team/phsultan/rtmp-support/include/asterisk/rtp_engine.h?view=diff&rev=278874&r1=278873&r2=278874
==============================================================================
--- team/phsultan/rtmp-support/include/asterisk/rtp_engine.h (original)
+++ team/phsultan/rtmp-support/include/asterisk/rtp_engine.h Fri Jul 23 08:28:49 2010
@@ -80,6 +80,9 @@
 
 /* Maximum number of generations */
 #define AST_RED_MAX_GENERATION 5
+
+/* Maximum RTP packet size, to avoid fragmentation when transmitting video */
+#define AST_MAX_RTP_PAYLOAD_SIZE 1460
 
 struct ast_rtp_instance;
 struct ast_rtp_glue;

Modified: team/phsultan/rtmp-support/res/res_rtp_asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/phsultan/rtmp-support/res/res_rtp_asterisk.c?view=diff&rev=278874&r1=278873&r2=278874
==============================================================================
--- team/phsultan/rtmp-support/res/res_rtp_asterisk.c (original)
+++ team/phsultan/rtmp-support/res/res_rtp_asterisk.c Fri Jul 23 08:28:49 2010
@@ -301,6 +301,9 @@
 		return 0;
 	}
 
+	if (rtpdebug)
+		return 1;
+		
 	return ast_sockaddr_cmp(&rtpdebugaddr, addr) == 0;
 }
 
@@ -362,6 +365,74 @@
 static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa)
 {
 	return __rtp_sendto(instance, buf, size, flags, sa, 0);
+}
+
+static int rtp_h263_sendto(struct ast_rtp_instance *instance, uint8_t *buf, size_t size, int flags, struct ast_sockaddr *sa, int codec)
+{
+	int hdrlen = 12;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	uint8_t aux[hdrlen + AST_MAX_RTP_PAYLOAD_SIZE];
+	uint8_t *tmp = aux;
+	int len;
+	int mark = 0;
+	int res = 0;
+
+	memset(aux, 0, sizeof(aux));
+
+	while (size > 0) {
+		tmp = aux + hdrlen;
+		len = size;
+
+		if (size >= 2 && (buf[0] == 0) && (buf[1] == 0)) {
+			/* Video frame start, set P bit */
+			*tmp++ = 0x04;
+			buf += 2;
+			size -= 2;
+		} else {
+			*tmp++ = 0;
+		}
+		*tmp++ = 0;
+
+		if (size > AST_MAX_RTP_PAYLOAD_SIZE - 2) {
+			len = AST_MAX_RTP_PAYLOAD_SIZE - 2;
+		} else {
+			/* Last frame fragment */
+			mark = 1;
+			len = size;
+		}
+
+		put_unaligned_uint32(aux, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23)));
+		put_unaligned_uint32(aux + 4, htonl(rtp->lastts));
+		put_unaligned_uint32(aux + 8, htonl(rtp->ssrc));
+
+		/*
+		   if (len < size) {
+		   const uint8_t *end = find_resync_marker_reverse(buf1, buf1 + len);
+		   len = end - buf1;
+		   }
+		   */
+		memcpy(aux + hdrlen + 2, buf, len);
+		tmp += len;
+
+		res = ast_sendto(rtp->s, aux, tmp - aux, flags, sa);
+		if (res >= 0) {
+			rtp->txcount++;
+			rtp->txoctetcount += res;
+		}
+
+		if (rtp_debug_test_addr(sa)) {
+			ast_verbose("Sent RTP packet to      %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+				    ast_sockaddr_stringify(sa),
+				    codec, rtp->seqno, rtp->lastts, res);
+		}
+
+		buf += len;
+		size -= len;
+		rtp->seqno++;
+		res += len;
+	}
+
+	return res;
 }
 
 static int rtp_get_rate(format_t subclass)
@@ -1074,8 +1145,9 @@
 		}
 	}
 
-	/* If we have been explicitly told to set the marker bit then do so */
-	if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) {
+	/* If we have been explicitly told to set the marker bit then do so.
+	 * Do it as well if we have a video frame */
+	if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT) || frame->frametype == AST_FRAME_VIDEO) {
 		mark = 1;
 		ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT);
 	}
@@ -1100,6 +1172,16 @@
 		put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
 		put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
 
+		if (frame->frametype == AST_FRAME_VIDEO) {
+			switch (frame->subclass.codec) {
+				case AST_FORMAT_H263_PLUS:
+					res = rtp_h263_sendto(instance, (void *)frame->data.ptr, frame->datalen, 0, &remote_address, codec);
+					return 0;
+				default :
+					break;
+			}
+		}
+		
 		if ((res = rtp_sendto(instance, (void *)rtpheader, frame->datalen + hdrlen, 0, &remote_address)) < 0) {
 			if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
 				ast_debug(1, "RTP Transmission error of packet %d to %s: %s\n",




More information about the svn-commits mailing list