aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2006-01-07 17:54:22 +0000
committermarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2006-01-07 17:54:22 +0000
commitd1d90f47fd87ec50db7aa5badcc996854d93c0bf (patch)
tree34e09d3fba69a40757b42b46d8656a74744e6c7b
parentcde8c70439d39f407bf73a892ef09caf81bb1899 (diff)
Add support for H.264 with SIP and recording
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@7855 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channel.c3
-rw-r--r--channels/chan_sip.c10
-rw-r--r--cli.c12
-rw-r--r--file.c58
-rw-r--r--formats/Makefile2
-rw-r--r--formats/format_h264.c281
-rw-r--r--frame.c2
-rw-r--r--include/asterisk/frame.h5
-rw-r--r--rtp.c3
9 files changed, 341 insertions, 35 deletions
diff --git a/channel.c b/channel.c
index d35caf53d..3062f6bcc 100644
--- 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)
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 55419f163..027f10081 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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 7ad4d5ff6..5c0f38fb4 100644
--- 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 590fb71f0..bce283b83 100644
--- 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;
}
diff --git a/formats/Makefile b/formats/Makefile
index 31f973390..42258242e 100644
--- a/formats/Makefile
+++ b/formats/Makefile
@@ -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
index 000000000..e132e0fe0
--- /dev/null
+++ b/formats/format_h264.c
@@ -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 2558ead99..8c86a67f4 100644
--- 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" },
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index c0815732b..ca8397e0f 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -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 b9361ac86..b88dfacbd 100644
--- 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: