Add support for H.264 with SIP and recording
authorMark Spencer <markster@digium.com>
Sat, 7 Jan 2006 17:54:22 +0000 (17:54 +0000)
committerMark Spencer <markster@digium.com>
Sat, 7 Jan 2006 17:54:22 +0000 (17:54 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7855 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channel.c
channels/chan_sip.c
cli.c
file.c
formats/Makefile
formats/format_h264.c [new file with mode: 0644]
frame.c
include/asterisk/frame.h
rtp.c

index d35caf5..3062f6b 100644 (file)
--- a/channel.c
+++ b/channel.c
@@ -2305,6 +2305,9 @@ static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *fo
        int native;
        int res;
        
+       /* Make sure we only consider audio */
+       fmt &= AST_FORMAT_AUDIO_MASK;
+       
        native = chan->nativeformats;
        /* Find a translation path from the native format to one of the desired formats */
        if (!direction)
index 55419f1..027f100 100644 (file)
@@ -2790,7 +2790,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
                what = i->capability;
        else
                what = global_capability;
-       tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1);
+       tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
        fmt = ast_best_codec(tmp->nativeformats);
 
        if (title)
@@ -3011,9 +3011,9 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
        if (p->owner) {
                /* We already hold the channel lock */
                if (f->frametype == AST_FRAME_VOICE) {
-                       if (f->subclass != p->owner->nativeformats) {
+                       if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
                                ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
-                               p->owner->nativeformats = f->subclass;
+                               p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass;
                                ast_set_read_format(p->owner, p->owner->readformat);
                                ast_set_write_format(p->owner, p->owner->writeformat);
                        }
@@ -3679,13 +3679,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
        if (!p->owner)  /* There's no open channel owning us */
                return 0;
 
-       if (!(p->owner->nativeformats & p->jointcapability)) {
+       if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
                const unsigned slen=512;
                char s1[slen], s2[slen];
                ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %s and not %s\n", 
                                ast_getformatname_multiple(s1, slen, p->jointcapability),
                                ast_getformatname_multiple(s2, slen, p->owner->nativeformats));
-               p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1);
+               p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability);
                ast_set_read_format(p->owner, p->owner->readformat);
                ast_set_write_format(p->owner, p->owner->writeformat);
        }
diff --git a/cli.c b/cli.c
index 7ad4d5f..5c0f38f 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -738,6 +738,7 @@ static int handle_showchan(int fd, int argc, char *argv[])
        struct timeval now;
        char buf[2048];
        char cdrtime[256];
+       char nf[256], wf[256], rf[256];
        long elapsed_seconds=0;
        int hour=0, min=0, sec=0;
        
@@ -767,9 +768,9 @@ static int handle_showchan(int fd, int argc, char *argv[])
                "    DNID Digits: %s\n"
                "          State: %s (%d)\n"
                "          Rings: %d\n"
-               "   NativeFormat: %d\n"
-               "    WriteFormat: %d\n"
-               "     ReadFormat: %d\n"
+               "  NativeFormats: %s\n"
+               "    WriteFormat: %s\n"
+               "     ReadFormat: %s\n"
                "1st File Descriptor: %d\n"
                "      Frames in: %d%s\n"
                "     Frames out: %d%s\n"
@@ -789,7 +790,10 @@ static int handle_showchan(int fd, int argc, char *argv[])
                c->name, c->type, c->uniqueid,
                (c->cid.cid_num ? c->cid.cid_num : "(N/A)"),
                (c->cid.cid_name ? c->cid.cid_name : "(N/A)"),
-               (c->cid.cid_dnid ? c->cid.cid_dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, c->nativeformats, c->writeformat, c->readformat,
+               (c->cid.cid_dnid ? c->cid.cid_dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, 
+               ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
+               ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
+               ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
                c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
                c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup,
                cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
diff --git a/file.c b/file.c
index 590fb71..bce283b 100644 (file)
--- a/file.c
+++ b/file.c
@@ -203,8 +203,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
                if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
                        /* This is the audio portion.  Call the video one... */
                        if (!fs->vfs && fs->filename) {
-                               /* XXX Support other video formats XXX */
-                               const char *type = "h263";
+                               const char *type = ast_getformatname(f->subclass & ~0x1);
                                fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
                                ast_log(LOG_DEBUG, "Opened video output file\n");
                        }
@@ -495,10 +494,14 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
                } else
                        snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
                fmts = ast_fileexists(filename2, NULL, NULL);
+               if (fmts > 0) 
+                       fmts &= AST_FORMAT_AUDIO_MASK;
        }
        if (fmts < 1) {
                ast_copy_string(filename2, filename, sizeof(filename2));
                fmts = ast_fileexists(filename2, NULL, NULL);
+               if (fmts > 0)
+                       fmts &= AST_FORMAT_AUDIO_MASK;
        }
        if (fmts < 1) {
                ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
@@ -530,30 +533,35 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil
        */
        int fd = -1;
        int fmts = -1;
+       unsigned int format;
        char filename2[256];
        char lang2[MAX_LANGUAGE];
-       /* XXX H.263 only XXX */
-       char *fmt = "h263";
-       if (!ast_strlen_zero(preflang)) {
-               snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
-               fmts = ast_fileexists(filename2, fmt, NULL);
+       const char *fmt;
+       for (format = AST_FORMAT_MAX_AUDIO << 1; format <= AST_FORMAT_MAX_VIDEO; format = format << 1) {
+               if (!(chan->nativeformats & format))
+                       continue;
+               fmt = ast_getformatname(format);
+               if (!ast_strlen_zero(preflang)) {
+                       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
+                       fmts = ast_fileexists(filename2, fmt, NULL);
+                       if (fmts < 1) {
+                               ast_copy_string(lang2, preflang, sizeof(lang2));
+                               snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
+                               fmts = ast_fileexists(filename2, fmt, NULL);
+                       }
+               }
                if (fmts < 1) {
-                       ast_copy_string(lang2, preflang, sizeof(lang2));
-                       snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
+                       ast_copy_string(filename2, filename, sizeof(filename2));
                        fmts = ast_fileexists(filename2, fmt, NULL);
                }
+               if (fmts < 1) {
+                       continue;
+               }
+               fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
+               if (fd >= 0)
+                       return chan->vstream;
+               ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
        }
-       if (fmts < 1) {
-               ast_copy_string(filename2, filename, sizeof(filename2));
-               fmts = ast_fileexists(filename2, fmt, NULL);
-       }
-       if (fmts < 1) {
-               return NULL;
-       }
-       fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
-       if (fd >= 0)
-               return chan->vstream;
-       ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
        return NULL;
 }
 
@@ -792,12 +800,14 @@ int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
 {
        struct ast_filestream *fs;
-       struct ast_filestream *vfs;
+       struct ast_filestream *vfs=NULL;
+       char fmt[256];
 
        fs = ast_openstream(chan, filename, preflang);
-       vfs = ast_openvstream(chan, filename, preflang);
+       if (fs)
+               vfs = ast_openvstream(chan, filename, preflang);
        if (vfs)
-               ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
+               ast_log(LOG_DEBUG, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format));
        if (fs){
                if (ast_applystream(chan, fs))
                        return -1;
@@ -813,7 +823,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
 #endif
                return 0;
        }
-       ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
+       ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), chan->nativeformats), strerror(errno));
        return -1;
 }
 
index 31f9733..4225824 100644 (file)
@@ -14,7 +14,7 @@
 FORMAT_LIBS=format_gsm.so format_wav.so \
        format_wav_gsm.so format_vox.so format_pcm.so format_g729.so \
        format_pcm_alaw.so format_h263.so format_g726.so format_ilbc.so \
-       format_sln.so format_au.so
+       format_sln.so format_au.so format_h264.so
 FORMAT_LIBS+=format_jpeg.so
 
 #
diff --git a/formats/format_h264.c b/formats/format_h264.c
new file mode 100644 (file)
index 0000000..e132e0f
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Save to raw, headerless h264 data.
+ * \arg File name extension: h264
+ * \ingroup formats
+ */
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/sched.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makeh264e.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+struct ast_filestream {
+       void *reserved[AST_RESERVED_POINTERS];
+       /* Believe it or not, we must decode/recode to account for the
+          weird MS format */
+       /* This is what a filestream means to us */
+       FILE *f; /* Descriptor */
+       unsigned int lastts;
+       struct ast_frame fr;                            /* Frame information */
+       char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
+       char empty;                                                     /* Empty character */
+       unsigned char h264[4096];                               /* Two Real h264 Frames */
+};
+
+
+AST_MUTEX_DEFINE_STATIC(h264_lock);
+static int glistcnt = 0;
+
+static char *name = "h264";
+static char *desc = "Raw h264 data";
+static char *exts = "h264";
+
+static struct ast_filestream *h264_open(FILE *f)
+{
+       /* We don't have any header to read or anything really, but
+          if we did, it would go here.  We also might want to check
+          and be sure it's a valid file.  */
+       struct ast_filestream *tmp;
+       unsigned int ts;
+       int res;
+       if ((res = fread(&ts, 1, sizeof(ts), f)) < sizeof(ts)) {
+               ast_log(LOG_WARNING, "Empty file!\n");
+               return NULL;
+       }
+               
+       if ((tmp = malloc(sizeof(struct ast_filestream)))) {
+               memset(tmp, 0, sizeof(struct ast_filestream));
+               if (ast_mutex_lock(&h264_lock)) {
+                       ast_log(LOG_WARNING, "Unable to lock h264 list\n");
+                       free(tmp);
+                       return NULL;
+               }
+               tmp->f = f;
+               tmp->fr.data = tmp->h264;
+               tmp->fr.frametype = AST_FRAME_VIDEO;
+               tmp->fr.subclass = AST_FORMAT_H264;
+               /* datalen will vary for each frame */
+               tmp->fr.src = name;
+               tmp->fr.mallocd = 0;
+               glistcnt++;
+               ast_mutex_unlock(&h264_lock);
+               ast_update_use_count();
+       }
+       return tmp;
+}
+
+static struct ast_filestream *h264_rewrite(FILE *f, const char *comment)
+{
+       /* We don't have any header to read or anything really, but
+          if we did, it would go here.  We also might want to check
+          and be sure it's a valid file.  */
+       struct ast_filestream *tmp;
+       if ((tmp = malloc(sizeof(struct ast_filestream)))) {
+               memset(tmp, 0, sizeof(struct ast_filestream));
+               if (ast_mutex_lock(&h264_lock)) {
+                       ast_log(LOG_WARNING, "Unable to lock h264 list\n");
+                       free(tmp);
+                       return NULL;
+               }
+               tmp->f = f;
+               glistcnt++;
+               ast_mutex_unlock(&h264_lock);
+               ast_update_use_count();
+       } else
+               ast_log(LOG_WARNING, "Out of memory\n");
+       return tmp;
+}
+
+static void h264_close(struct ast_filestream *s)
+{
+       if (ast_mutex_lock(&h264_lock)) {
+               ast_log(LOG_WARNING, "Unable to lock h264 list\n");
+               return;
+       }
+       glistcnt--;
+       ast_mutex_unlock(&h264_lock);
+       ast_update_use_count();
+       fclose(s->f);
+       free(s);
+       s = NULL;
+}
+
+static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext)
+{
+       int res;
+       int mark=0;
+       unsigned short len;
+       unsigned int ts;
+       /* Send a frame from the file to the appropriate channel */
+       s->fr.frametype = AST_FRAME_VIDEO;
+       s->fr.subclass = AST_FORMAT_H264;
+       s->fr.offset = AST_FRIENDLY_OFFSET;
+       s->fr.mallocd = 0;
+       s->fr.data = s->h264;
+       if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) {
+               return NULL;
+       }
+       len = ntohs(len);
+       if (len & 0x8000) {
+               mark = 1;
+       }
+       len &= 0x7fff;
+       if (len > sizeof(s->h264)) {
+               ast_log(LOG_WARNING, "Length %d is too long\n", len);
+       }
+       if ((res = fread(s->h264, 1, len, s->f)) != len) {
+               if (res)
+                       ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+               return NULL;
+       }
+       s->fr.samples = s->lastts;
+       s->fr.datalen = len;
+       s->fr.subclass |= mark;
+       s->fr.delivery.tv_sec = 0;
+       s->fr.delivery.tv_usec = 0;
+       if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) {
+               s->lastts = ntohl(ts);
+               *whennext = s->lastts * 4/45;
+       } else
+               *whennext = 0;
+       return &s->fr;
+}
+
+static int h264_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+       int res;
+       unsigned int ts;
+       unsigned short len;
+       int subclass;
+       int mark=0;
+       if (f->frametype != AST_FRAME_VIDEO) {
+               ast_log(LOG_WARNING, "Asked to write non-video frame!\n");
+               return -1;
+       }
+       subclass = f->subclass;
+       if (subclass & 0x1)
+               mark=0x8000;
+       subclass &= ~0x1;
+       if (subclass != AST_FORMAT_H264) {
+               ast_log(LOG_WARNING, "Asked to write non-h264 frame (%d)!\n", f->subclass);
+               return -1;
+       }
+       ts = htonl(f->samples);
+       if ((res = fwrite(&ts, 1, sizeof(ts), fs->f)) != sizeof(ts)) {
+                       ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
+                       return -1;
+       }
+       len = htons(f->datalen | mark);
+       if ((res = fwrite(&len, 1, sizeof(len), fs->f)) != sizeof(len)) {
+                       ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
+                       return -1;
+       }
+       if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+                       ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+                       return -1;
+       }
+       return 0;
+}
+
+static char *h264_getcomment(struct ast_filestream *s)
+{
+       return NULL;
+}
+
+static int h264_seek(struct ast_filestream *fs, long sample_offset, int whence)
+{
+       /* No way Jose */
+       return -1;
+}
+
+static int h264_trunc(struct ast_filestream *fs)
+{
+       /* Truncate file to current length */
+       if (ftruncate(fileno(fs->f), ftell(fs->f)) < 0)
+               return -1;
+       return 0;
+}
+
+static long h264_tell(struct ast_filestream *fs)
+{
+       /* XXX This is totally bogus XXX */
+       off_t offset;
+       offset = ftell(fs->f);
+       return (offset/20)*160;
+}
+
+int load_module()
+{
+       return ast_format_register(name, exts, AST_FORMAT_H264,
+                                                               h264_open,
+                                                               h264_rewrite,
+                                                               h264_write,
+                                                               h264_seek,
+                                                               h264_trunc,
+                                                               h264_tell,
+                                                               h264_read,
+                                                               h264_close,
+                                                               h264_getcomment);
+                                                               
+                                                               
+}
+
+int unload_module()
+{
+       return ast_format_unregister(name);
+}      
+
+int usecount()
+{
+       return glistcnt;
+}
+
+char *description()
+{
+       return desc;
+}
+
+
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}
diff --git a/frame.c b/frame.c
index 2558ead..8c86a67 100644 (file)
--- a/frame.c
+++ b/frame.c
@@ -101,7 +101,7 @@ static struct ast_format_list AST_FORMAT_LIST[] = {
        { 1, AST_FORMAT_H261, "h261", "H.261 Video" },  /*!< Passthrough */
        { 1, AST_FORMAT_H263, "h263", "H.263 Video" },  /*!< Passthrough support, see format_h263.c */
        { 1, AST_FORMAT_H263_PLUS, "h263p", "H.263+ Video" },   /*!< See format_h263.c */
-       { 0, 0, "nothing", "undefined" },
+       { 1, AST_FORMAT_H264, "h264", "H.264 Video" },  /*!< Passthrough support, see format_h263.c */
        { 0, 0, "nothing", "undefined" },
        { 0, 0, "nothing", "undefined" },
        { 0, 0, "nothing", "undefined" },
index c081573..ca8397e 100644 (file)
@@ -193,6 +193,8 @@ struct ast_frame {
 #define AST_FORMAT_ILBC                (1 << 10)
 /*! Maximum audio format */
 #define AST_FORMAT_MAX_AUDIO   (1 << 15)
+/*! Maximum audio mask */
+#define AST_FORMAT_AUDIO_MASK   ((1 << 16)-1)
 /*! JPEG Images */
 #define AST_FORMAT_JPEG                (1 << 16)
 /*! PNG Images */
@@ -203,8 +205,11 @@ struct ast_frame {
 #define AST_FORMAT_H263                (1 << 19)
 /*! H.263+ Video */
 #define AST_FORMAT_H263_PLUS   (1 << 20)
+/*! H.264 Video */
+#define AST_FORMAT_H264                (1 << 21)
 /*! Maximum video format */
 #define AST_FORMAT_MAX_VIDEO   (1 << 24)
+#define AST_FORMAT_VIDEO_MASK   (((1 << 25)-1) & ~(AST_FORMAT_AUDIO_MASK))
 
 /* Control frame types */
 /*! Other end has hungup */
diff --git a/rtp.c b/rtp.c
index b9361ac..b88dfac 100644 (file)
--- a/rtp.c
+++ b/rtp.c
@@ -655,6 +655,7 @@ static struct {
        {{1, AST_FORMAT_H261}, "video", "H261"},
        {{1, AST_FORMAT_H263}, "video", "H263"},
        {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"},
+       {{1, AST_FORMAT_H264}, "video", "H264"},
 };
 
 /* Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s:
@@ -683,6 +684,7 @@ static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = {
        [34] = {1, AST_FORMAT_H263},
        [103] = {1, AST_FORMAT_H263_PLUS},
        [97] = {1, AST_FORMAT_ILBC},
+       [99] = {1, AST_FORMAT_H264},
        [101] = {0, AST_RTP_DTMF},
        [110] = {1, AST_FORMAT_SPEEX},
        [111] = {1, AST_FORMAT_G726},
@@ -1515,6 +1517,7 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
        case AST_FORMAT_H261:
        case AST_FORMAT_H263:
        case AST_FORMAT_H263_PLUS:
+       case AST_FORMAT_H264:
        case AST_FORMAT_G723_1:
        case AST_FORMAT_LPC10:
        case AST_FORMAT_SPEEX: