Add support for H.264 with SIP and recording

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7855 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Mark Spencer 20 years ago
parent 33323ac720
commit e2ef61814f

@ -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)

@ -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);
}

12
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>",

@ -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;
}

@ -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
#

@ -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;
}

@ -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" },

@ -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 */

@ -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:

Loading…
Cancel
Save