Add H.263 video support

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1131 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.0
Mark Spencer 22 years ago
parent 3b78918878
commit 9c9ee7c952

@ -183,6 +183,14 @@ static int record_exec(struct ast_channel *chan, void *data)
}
}
}
if (f->frametype == AST_FRAME_VIDEO) {
res = ast_writestream(s, f);
if (res) {
ast_log(LOG_WARNING, "Problem writing frame\n");
break;
}
}
if ((f->frametype == AST_FRAME_DTMF) &&
(f->subclass == '#')) {
ast_frfree(f);

157
file.c

@ -65,6 +65,11 @@ struct ast_format {
struct ast_filestream {
/* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
struct ast_format *fmt;
int flags;
mode_t mode;
char *filename;
/* Video file stream */
struct ast_filestream *vfs;
/* Transparently translate from another format -- just once */
struct ast_trans_pvt *trans;
struct ast_tranlator_pvt *tr;
@ -157,11 +162,13 @@ int ast_format_unregister(char *name)
int ast_stopstream(struct ast_channel *tmp)
{
/* Stop a running stream if there is one */
if (!tmp->stream)
return 0;
ast_closestream(tmp->stream);
if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
if (tmp->vstream)
ast_closestream(tmp->vstream);
if (tmp->stream) {
ast_closestream(tmp->stream);
if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
}
return 0;
}
@ -169,11 +176,29 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
{
struct ast_frame *trf;
int res = -1;
if (f->frametype != AST_FRAME_VOICE) {
int alt=0;
if (f->frametype == AST_FRAME_VIDEO) {
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 */
char *type = "h263";
fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
ast_log(LOG_DEBUG, "Opened video output file\n");
}
if (fs->vfs)
return ast_writestream(fs->vfs, f);
/* Ignore */
return 0;
} else {
/* Might / might not have mark set */
alt = 1;
}
} else if (f->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
return -1;
}
if ((fs->fmt->format & f->subclass) == f->subclass) {
if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
res = fs->fmt->write(fs, f);
if (res < 0)
ast_log(LOG_WARNING, "Natural write failed\n");
@ -335,7 +360,8 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
ast_log(LOG_WARNING, "Out of memory\n");
break;
case ACTION_OPEN:
if ((ret < 0) && ((chan->writeformat & f->format))) {
if ((ret < 0) && ((chan->writeformat & f->format) ||
((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
ret = open(fn, O_RDONLY);
if (ret >= 0) {
s = f->open(ret);
@ -343,7 +369,11 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
s->lasttimeout = -1;
s->fmt = f;
s->trans = NULL;
chan->stream = s;
s->filename = NULL;
if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
chan->stream = s;
else
chan->vstream = s;
} else {
close(ret);
ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
@ -397,11 +427,11 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename,
if (chan->generator)
ast_deactivate_generator(chan);
if (preflang && strlen(preflang)) {
snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
fmts = ast_fileexists(filename2, NULL, NULL);
if (fmts < 1) {
strncpy(lang2, preflang, sizeof(lang2)-1);
snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
fmts = ast_fileexists(filename2, NULL, NULL);
}
}
@ -423,6 +453,49 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename,
return NULL;
}
struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang)
{
/* This is a fairly complex routine. Essentially we should do
the following:
1) Find which file handlers produce our type of format.
2) Look for a filename which it can handle.
3) If we find one, then great.
4) If not, see what files are there
5) See what we can actually support
6) Choose the one with the least costly translator path and
set it up.
*/
int fd = -1;
int fmts = -1;
char filename2[256];
char lang2[MAX_LANGUAGE];
/* XXX H.263 only XXX */
char *fmt = "h263";
if (preflang && strlen(preflang)) {
snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
fmts = ast_fileexists(filename2, fmt, NULL);
if (fmts < 1) {
strncpy(lang2, preflang, sizeof(lang2)-1);
snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
fmts = ast_fileexists(filename2, fmt, NULL);
}
}
if (fmts < 1) {
strncpy(filename2, filename, sizeof(filename2)-1);
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;
}
static int ast_readaudio_callback(void *data)
{
struct ast_filestream *s = data;
@ -451,6 +524,34 @@ static int ast_readaudio_callback(void *data)
return 1;
}
static int ast_readvideo_callback(void *data)
{
struct ast_filestream *s = data;
struct ast_frame *fr;
int whennext = 0;
while(!whennext) {
fr = s->fmt->read(s, &whennext);
if (fr) {
if (ast_write(s->owner, fr)) {
ast_log(LOG_WARNING, "Failed to write frame\n");
s->owner->vstreamid = -1;
return 0;
}
} else {
/* Stream has finished */
s->owner->vstreamid = -1;
return 0;
}
}
if (whennext != s->lasttimeout) {
s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
s->lasttimeout = whennext;
return 0;
}
return 1;
}
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
{
s->owner = chan;
@ -459,7 +560,10 @@ int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
int ast_playstream(struct ast_filestream *s)
{
ast_readaudio_callback(s);
if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
ast_readaudio_callback(s);
else
ast_readvideo_callback(s);
return 0;
}
@ -498,10 +602,19 @@ int ast_closestream(struct ast_filestream *f)
/* Stop a running stream if there is one */
if (f->owner) {
f->owner->stream = NULL;
if (f->owner->streamid > -1)
ast_sched_del(f->owner->sched, f->owner->streamid);
f->owner->streamid = -1;
if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
if (f->owner->streamid > -1)
ast_sched_del(f->owner->sched, f->owner->streamid);
f->owner->streamid = -1;
} else {
if (f->owner->vstreamid > -1)
ast_sched_del(f->owner->sched, f->owner->vstreamid);
f->owner->vstreamid = -1;
}
}
if (f->filename)
free(f->filename);
f->filename = NULL;
f->fmt->close(f);
return 0;
}
@ -565,13 +678,23 @@ int ast_filecopy(char *filename, char *filename2, char *fmt)
int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
{
struct ast_filestream *fs;
struct ast_filestream *vfs;
fs = ast_openstream(chan, filename, preflang);
vfs = ast_openvstream(chan, filename, preflang);
if (vfs)
ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
else
ast_log(LOG_DEBUG, "Waaah, '%s' has no video stream :(\n", filename);
if(fs){
if(ast_applystream(chan, fs))
return -1;
if(vfs && ast_applystream(chan, vfs))
return -1;
if(ast_playstream(fs))
return -1;
if(vfs && ast_playstream(vfs))
return -1;
#if 1
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
@ -612,6 +735,10 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment,
if ((fs = f->rewrite(fd, comment))) {
fs->trans = NULL;
fs->fmt = f;
fs->flags = flags;
fs->mode = mode;
fs->filename = strdup(filename);
fs->vfs = NULL;
} else {
ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
close(fd);

@ -13,7 +13,7 @@
FORMAT_LIBS=format_gsm.so format_wav.so format_mp3.so \
format_wav_gsm.so format_vox.so format_pcm.so format_g729.so \
format_pcm_alaw.so
format_pcm_alaw.so format_h263.so
FORMAT_LIBS+=format_jpeg.so
#
# G723 simple frame is depricated

@ -0,0 +1,271 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Save to raw, headerless h263 data.
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#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 <arpa/inet.h>
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#ifdef __linux__
#include <endian.h>
#else
#include <machine/endian.h>
#endif
/* Some Ideas for this code came from makeh263e.c by Jeffery 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 */
int fd; /* 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 h263[4096]; /* Two Real h263 Frames */
};
static pthread_mutex_t h263_lock = AST_MUTEX_INITIALIZER;
static int glistcnt = 0;
static char *name = "h263";
static char *desc = "Raw h263 data";
static char *exts = "h263";
static struct ast_filestream *h263_open(int fd)
{
/* 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 = read(fd, &ts, sizeof(ts))) < 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_pthread_mutex_lock(&h263_lock)) {
ast_log(LOG_WARNING, "Unable to lock h263 list\n");
free(tmp);
return NULL;
}
tmp->fd = fd;
tmp->fr.data = tmp->h263;
tmp->fr.frametype = AST_FRAME_VIDEO;
tmp->fr.subclass = AST_FORMAT_H263;
/* datalen will vary for each frame */
tmp->fr.src = name;
tmp->fr.mallocd = 0;
glistcnt++;
ast_pthread_mutex_unlock(&h263_lock);
ast_update_use_count();
}
return tmp;
}
static struct ast_filestream *h263_rewrite(int fd, 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_pthread_mutex_lock(&h263_lock)) {
ast_log(LOG_WARNING, "Unable to lock h263 list\n");
free(tmp);
return NULL;
}
tmp->fd = fd;
glistcnt++;
ast_pthread_mutex_unlock(&h263_lock);
ast_update_use_count();
} else
ast_log(LOG_WARNING, "Out of memory\n");
return tmp;
}
static void h263_close(struct ast_filestream *s)
{
if (ast_pthread_mutex_lock(&h263_lock)) {
ast_log(LOG_WARNING, "Unable to lock h263 list\n");
return;
}
glistcnt--;
ast_pthread_mutex_unlock(&h263_lock);
ast_update_use_count();
close(s->fd);
free(s);
s = NULL;
}
static struct ast_frame *h263_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_H263;
s->fr.offset = AST_FRIENDLY_OFFSET;
s->fr.mallocd = 0;
s->fr.data = s->h263;
if ((res = read(s->fd, &len, sizeof(len))) < 1) {
return NULL;
}
len = ntohs(len);
if (len & 0x8000) {
mark = 1;
}
len &= 0x7fff;
if (len > sizeof(s->h263)) {
ast_log(LOG_WARNING, "Length %d is too long\n", len);
}
if ((res = read(s->fd, s->h263, len)) != 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;
if ((res = read(s->fd, &ts, sizeof(ts))) == sizeof(ts)) {
s->lastts = *whennext = ntohl(ts) * 4/45;
} else
*whennext = 0;
return &s->fr;
}
static int h263_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_H263) {
ast_log(LOG_WARNING, "Asked to write non-h263 frame (%d)!\n", f->subclass);
return -1;
}
ts = htonl(f->samples);
if ((res = write(fs->fd, &ts, sizeof(ts))) != sizeof(ts)) {
ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
return -1;
}
len = htons(f->datalen | mark);
if ((res = write(fs->fd, &len, sizeof(len))) != sizeof(len)) {
ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
return -1;
}
if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
return -1;
}
return 0;
}
static char *h263_getcomment(struct ast_filestream *s)
{
return NULL;
}
static int h263_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
/* No way Jose */
return -1;
}
static int h263_trunc(struct ast_filestream *fs)
{
/* Truncate file to current length */
if (ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)) < 0)
return -1;
return 0;
}
static long h263_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
return (offset/20)*160;
}
int load_module()
{
return ast_format_register(name, exts, AST_FORMAT_H263,
h263_open,
h263_rewrite,
h263_write,
h263_seek,
h263_trunc,
h263_tell,
h263_read,
h263_close,
h263_getcomment);
}
int unload_module()
{
return ast_format_unregister(name);
}
int usecount()
{
int res;
if (ast_pthread_mutex_lock(&h263_lock)) {
ast_log(LOG_WARNING, "Unable to lock h263 list\n");
return -1;
}
res = glistcnt;
ast_pthread_mutex_unlock(&h263_lock);
return res;
}
char *description()
{
return desc;
}
char *key()
{
return ASTERISK_GPL_KEY;
}

@ -116,6 +116,10 @@ struct ast_channel {
int streamid;
/*! Stream itself. */
struct ast_filestream *stream;
/*! For streaming playback, the schedule ID */
int vstreamid;
/*! Stream itself. */
struct ast_filestream *vstream;
/*! Original writer format */
int oldwriteformat;

@ -171,7 +171,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f);
*/
int ast_closestream(struct ast_filestream *f);
//! Opens stream for use in seeking, playing, and writing
//! Opens stream for use in seeking, playing
/*!
* \param chan channel to work with
* \param filename to use
@ -180,6 +180,15 @@ int ast_closestream(struct ast_filestream *f);
*/
struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang);
//! Opens stream for use in seeking, playing
/*!
* \param chan channel to work with
* \param filename to use
* \param preflang prefered language to use
* Returns a ast_filestream pointer if it opens the file, NULL on error
*/
struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang);
//! Applys a open stream to a channel.
/*!
* \param chan channel to work

@ -460,6 +460,8 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
}
} else {
/* Video -- samples is # of samples vs. 90000 */
if (!rtp->lastividtimestamp)
rtp->lastividtimestamp = timestamp;
rtp->f.samples = timestamp - rtp->lastividtimestamp;
rtp->lastividtimestamp = timestamp;
if (mark)

Loading…
Cancel
Save