Commit 3426ac87 authored by Simon Morlat's avatar Simon Morlat

initial commit of g726 support (untested)

parent 1b8cb290
......@@ -541,7 +541,31 @@ fi
fi
dnl check for spandsp support to bring up g726 codec
AC_ARG_ENABLE(spandsp,
[ --disable-spandsp Disable spandsp support],
[case "${enableval}" in
yes) spandsp=true ;;
no) spandsp=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-spandsp) ;;
esac],[spandsp=true])
if test x$spandsp = xtrue; then
dnl check for installed version of speex
PKG_CHECK_MODULES(SPANDSP, spandsp >= 0.0.6,
[ AC_DEFINE(HAVE_SPANDSP,1,[tells whether spandsp can be used])
have_spandsp=true ],
[have_spandsp=false]
)
AC_SUBST(SPANDSP_CFLAGS)
AC_SUBST(SPANDSP_LIBS)
fi
AM_CONDITIONAL(BUILD_GSM, test x$build_gsm = xyes )
AM_CONDITIONAL(BUILD_G726, test "$have_spandsp" = "true" )
MS_CHECK_VIDEO
AM_CONDITIONAL(BUILD_VIDEO, test "$video" = "true")
......
......@@ -109,7 +109,23 @@ typedef enum MSFilterId{
MS_ANDROID_VIDEO_WRITE_ID,
MS_TONE_DETECTOR_ID,
MS_VP8_ENC_ID,
MS_VP8_DEC_ID
MS_VP8_DEC_ID,
MS_G726_40_ENC_ID,
MS_G726_32_ENC_ID,
MS_G726_24_ENC_ID,
MS_G726_16_ENC_ID,
MS_AAL2_G726_40_ENC_ID,
MS_AAL2_G726_32_ENC_ID,
MS_AAL2_G726_24_ENC_ID,
MS_AAL2_G726_16_ENC_ID,
MS_G726_40_DEC_ID,
MS_G726_32_DEC_ID,
MS_G726_24_DEC_ID,
MS_G726_16_DEC_ID,
MS_AAL2_G726_40_DEC_ID,
MS_AAL2_G726_32_DEC_ID,
MS_AAL2_G726_24_DEC_ID,
MS_AAL2_G726_16_DEC_ID
} MSFilterId;
......
......@@ -66,6 +66,10 @@ if BUILD_GSM
libmediastreamer_la_SOURCES+=gsm.c
endif
if BUILD_G726
libmediastreamer_la_SOURCES+=g726.c
endif
if BUILD_WIN32
libmediastreamer_la_SOURCES+= winsnd3.c \
msfileplayer_win.c msfilerec_win.c
......@@ -185,7 +189,8 @@ libmediastreamer_la_LIBADD= $(ORTP_LIBS) \
$(SPEEX_LIBS) \
$(GSM_LIBS) \
$(LIBV4L1_LIBS) \
$(LIBV4L2_LIBS)
$(LIBV4L2_LIBS) \
$(SPANDSP_LIBS)
libmediastreamer_la_LDFLAGS= -no-undefined -version-info $(LIBMEDIASTREAMER_SO_VERSION)
......@@ -215,7 +220,8 @@ AM_CFLAGS= $(ORTP_CFLAGS) \
$(SPEEX_CFLAGS) \
$(GSM_CFLAGS) \
$(STRICT_OPTIONS) \
$(LIBPULSE_CFLAGS)
$(LIBPULSE_CFLAGS) \
$(SPANDSP_CFLAGS)
AM_CXXFLAGS= $(ORTP_CFLAGS) \
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef __APPLE__
#include "mediastreamer-config.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/msv4l.h"
#include "nowebcam.h"
#include "mediastreamer2/mswebcam.h"
// build for carbon
#define TARGET_API_MAC_CARBON 1
#if __APPLE_CC__
#include <Carbon/Carbon.h>
#include <QuicKTime/QuickTime.h>
#include <AppKit/AppKit.h>
#else
#include <ConditionalMacros.h>
#include <QuickTimeComponents.h>
#include <TextUtils.h>
#include <AppKit/AppKit.h>
#include <stdio.h>
#endif
typedef struct v4mState{
char * name;
char * id;
SeqGrabComponent seqgrab;
SGChannel sgchanvideo;
GWorldPtr pgworld;
ImageSequence decomseq;
MSVideoSize vsize;
int pix_fmt;
mblk_t *mire;
queue_t rq;
ms_mutex_t mutex;
int frame_ind;
int frame_max;
float fps;
float start_time;
int frame_count;
bool_t run;
bool_t usemire;
}v4mState;
static void v4m_init(MSFilter *f){
v4mState *s=ms_new0(v4mState,1);
s->seqgrab=NULL;
s->sgchanvideo=NULL;
s->pgworld=NULL;
s->decomseq=0;
s->run=FALSE;
s->vsize.width=MS_VIDEO_SIZE_CIF_W;
s->vsize.height=MS_VIDEO_SIZE_CIF_H;
s->pix_fmt=MS_YUY2;
qinit(&s->rq);
s->mire=NULL;
ms_mutex_init(&s->mutex,NULL);
s->start_time=0;
s->frame_count=-1;
s->fps=15;
s->usemire=(getenv("DEBUG")!=NULL);
f->data=s;
}
#define BailErr(x) {err = x; if(err != noErr) goto bail;}
pascal OSErr sgdata_callback(SGChannel c, Ptr p, long len, long *offset, long chRefCon, TimeValue time, short writeType, long refCon);
pascal OSErr sgdata_callback(SGChannel c, Ptr p, long len, long *offset, long chRefCon, TimeValue time, short writeType, long refCon)
{
#pragma unused(offset,chRefCon,time,writeType)
CodecFlags ignore;
v4mState *s=(v4mState *)refCon;
ComponentResult err = noErr;
if (!s) goto bail;
Rect boundsRect = {0, 0, s->vsize.height, s->vsize.width};
if (s->pgworld) {
if (s->decomseq == 0) {
Rect sourceRect = { 0, 0, 0, 0 };
MatrixRecord scaleMatrix;
ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
err = SGGetChannelSampleDescription(c,(Handle)imageDesc);
BailErr(err);
ms_message("raw image format: format=%c%c%c%c",
((char*)&((**imageDesc).cType))[0],
((char*)&((**imageDesc).cType))[1],
((char*)&((**imageDesc).cType))[2],
((char*)&((**imageDesc).cType))[3]);
// make a scaling matrix for the sequence
sourceRect.right = (**imageDesc).width;
sourceRect.bottom = (**imageDesc).height;
RectMatrix(&scaleMatrix, &sourceRect, &(*GetPortPixMap(s->pgworld))->bounds);
err = DecompressSequenceBegin(&s->decomseq, // pointer to field to receive unique ID for sequence
imageDesc, // handle to image description structure
s->pgworld, // port for the DESTINATION image
NULL, // graphics device handle, if port is set, set to NULL
NULL, // source rectangle defining the portion of the image to decompress
&scaleMatrix, // transformation matrix
srcCopy, // transfer mode specifier
NULL, // clipping region in dest. coordinate system to use as a mask
0, // flags
codecHighQuality, //codecNormalQuality, // accuracy in decompression
bestSpeedCodec); // compressor identifier or special identifiers ie. bestSpeedCodec
BailErr(err);
DisposeHandle((Handle)imageDesc);
imageDesc = NULL;
}
// decompress a frame into the GWorld - can queue a frame for async decompression when passed in a completion proc
// once the image is in the GWorld it can be manipulated at will
err = DecompressSequenceFrameS(s->decomseq, // sequence ID returned by DecompressSequenceBegin
p, // pointer to compressed image data
len, // size of the buffer
0, // in flags
&ignore, // out flags
NULL); // async completion proc
BailErr(err);
PixMap * pixmap = *GetGWorldPixMap(s->pgworld);
uint8_t * data = (uint8_t *) GetPixBaseAddr(GetGWorldPixMap(s->pgworld));
if (s->pix_fmt==MS_YUY2)
{
int size = (s->vsize.width * s->vsize.height * 2);
mblk_t *buf;
buf=allocb(size,0);
memcpy(buf->b_wptr, data, size);
buf->b_wptr+=size;
putq(&s->rq, buf);
}
if (s->pix_fmt==MS_RGBA32)
{
mblk_t *buf;
int size = s->vsize.width * s->vsize.height * 4;
buf=allocb(size,0);
memcpy(buf->b_wptr, data, size);
buf->b_wptr+=size;
putq(&s->rq, buf);
}
if (s->pix_fmt==MS_RGB24)
{
unsigned line;
mblk_t *buf;
int size = s->vsize.width * s->vsize.height * 3;
buf=allocb(size,0);
unsigned rowBytes = pixmap->rowBytes & (((unsigned short) 0xFFFF) >> 2);
unsigned pixelSize = pixmap->pixelSize / 8; // Pixel size in bytes
unsigned lineOffset = rowBytes - s->vsize.width * pixelSize;
for (line = 0 ; line < s->vsize.height ; line++) {
unsigned offset = line * (s->vsize.width * pixelSize + lineOffset);
memcpy(buf->b_wptr + ((line * s->vsize.width) * pixelSize), data + offset, (rowBytes - lineOffset));
}
if (s->pix_fmt==MS_RGB24)
{
/* Conversion from top down bottom up (BGR to RGB and flip) */
unsigned long Index,nPixels;
unsigned char *blue;
unsigned char tmp;
short iPixelSize;
blue=buf->b_wptr;
nPixels=s->vsize.width*s->vsize.height;
iPixelSize=24/8;
for(Index=0;Index!=nPixels;Index++) // For each pixel
{
tmp=*blue;
*blue=*(blue+2);
*(blue+2)=tmp;
blue+=iPixelSize;
}
}
buf->b_wptr+=size;
putq(&s->rq, buf);
}
}
bail:
return err;
}
static int v4m_close(v4mState *s)
{
if(s->seqgrab)
CloseComponent(s->seqgrab);
s->seqgrab=NULL;
if (s->decomseq)
CDSequenceEnd(s->decomseq);
s->decomseq=0;
if (s->pgworld!=NULL)
DisposeGWorld(s->pgworld);
s->pgworld=NULL;
return 0;
}
unsigned char *stdToPascalString(char *buffer, char * str) {
if (strlen(str) <= 255) {
buffer[0] = strlen(str);
memcpy(buffer + 1, str, strlen(str));
return (unsigned char*)buffer;
} else {
return NULL;
}
}
static int sequence_grabber_start(v4mState *s)
{
OSErr err = noErr;
char *camName;
ms_warning("Opening component");
s->seqgrab = OpenDefaultComponent(SeqGrabComponentType, 0);
if (s->seqgrab == NULL) {
ms_warning("can't get default sequence grabber component");
return -1;
}
ms_warning("Initializing component");
err = SGInitialize(s->seqgrab);
if (err != noErr) {
ms_warning("can't initialize sequence grabber component");
return -1;
}
ms_warning("SetDataRef");
err = SGSetDataRef(s->seqgrab, 0, 0, seqGrabDontMakeMovie);
if (err != noErr) {
ms_warning("can't set the destination data reference");
return -1;
}
ms_warning("Creating new channel");
err = SGNewChannel(s->seqgrab, VideoMediaType, &s->sgchanvideo);
if (err != noErr) {
ms_warning("can't create a video channel");
return -1;
}
camName = alloca(strlen(s->name) + 1);
err = SGSetChannelDevice(s->sgchanvideo, stdToPascalString(camName, s->name));
if (err != noErr) {
ms_warning("can't set channel device");
return -1;
}
short input = atoi(s->id);
err = SGSetChannelDeviceInput(s->sgchanvideo,input);
if (err != noErr) {
ms_warning("can't set channel device input");
return -1;
}
ms_warning("createGWorld");
Rect theRect = {0, 0, s->vsize.height, s->vsize.width};
//err = QTNewGWorld(&(s->pgworld), // returned GWorld
// kYUVSPixelFormat, /* YUY2 */
// &theRect, // bounding rectangle
// 0, // color table
// NULL, // graphic device handle
// 0); // flags
VDCompressionListHandle h = (VDCompressionListHandle)NewHandle(0);
VideoDigitizerComponent vdig = SGGetVideoDigitizerComponent(s->sgchanvideo);
VDGetCompressionTypes(vdig,h);
unsigned long max = sizeof(**h) / sizeof(VDCompressionList);
for (int i=0; i<max;i++) {
VDCompressionList cl = (VDCompressionList)*h[i];
ms_message("codec format supported %c%c%c%c",
((char*)&(cl.cType))[0],
((char*)&(cl.cType))[1],
((char*)&(cl.cType))[2],
((char*)&(cl.cType))[3]);
}
if (s->pix_fmt==MS_YUY2)
{
err = QTNewGWorld(&(s->pgworld), // returned GWorld
kYUVSPixelFormat, /* YUY2 */
&theRect, // bounding rectangle
0, // color table
NULL, // graphic device handle
0);
if (err!=noErr)
{
ms_message("Failed to use YUY2");
}
}
if (s->pix_fmt==MS_RGBA32)
{
err = QTNewGWorld(&(s->pgworld), // returned GWorld
k32RGBAPixelFormat, //k24BGRPixelFormat,
&theRect, // bounding rectangle
0, // color table
NULL, // graphic device handle
0); // flags
if (err!=noErr)
{
ms_message("Failed to use RGBA32");
}
}
if (err!=noErr)
{
return -1;
}
if(!LockPixels(GetPortPixMap(s->pgworld)))
{
v4m_close(s);
return -1;
}
err = SGSetGWorld(s->seqgrab, s->pgworld, GetMainDevice());
if (err != noErr) {
ms_warning("can't set GWorld");
return -1;
}
ms_warning("SGSetDataProc");
err = SGSetDataProc(s->seqgrab,NewSGDataUPP(sgdata_callback),(long)s);
if (err != noErr) {
ms_warning("can't set data proc");
return -1;
}
ms_warning("SGSetChannelUsage");
err = SGSetChannelUsage(s->sgchanvideo, seqGrabRecord);
if (err != noErr) {
ms_warning("can't set channel usage");
return -1;
}
ms_warning("SGPrepare");
err = SGPrepare(s->seqgrab, false, true);
if (err != noErr) {
ms_warning("can't prepare sequence grabber component");
return -1;
}
err = SGSetChannelBounds(s->sgchanvideo, &theRect);
if (err!=noErr)
{
v4m_close(s);
return -1;
}
err = SGStartRecord(s->seqgrab);
if (err!=noErr)
{
v4m_close(s);
return -1;
}
return 0;
}
static int v4m_start(MSFilter *f, void *arg)
{
v4mState *s=(v4mState*)f->data;
int err=0;
err = sequence_grabber_start(s);
if (err!=0)
{
s->pix_fmt=MS_YUV420P;
s->vsize.width=MS_VIDEO_SIZE_CIF_W;
s->vsize.height=MS_VIDEO_SIZE_CIF_H;
return 0;
}
ms_message("v4m video device opened.");
return 0;
}
static void v4m_start_capture(v4mState *s){
if (s->seqgrab!=NULL){
s->run=TRUE;
}
}
static int v4m_stop(MSFilter *f, void *arg){
v4mState *s=(v4mState*)f->data;
if (s->seqgrab!=NULL){
ms_mutex_lock(&s->mutex);
SGStop(s->seqgrab);
v4m_close(s);
flushq(&s->rq,0);
ms_mutex_unlock(&s->mutex);
}
return 0;
}
static void v4m_stop_capture(v4mState *s){
if (s->run){
s->run=FALSE;
ms_message("v4m capture stopped.");
}
}
static void v4m_uninit(MSFilter *f){
v4mState *s=(v4mState*)f->data;
if (s->seqgrab!=NULL) v4m_stop(f,NULL);
//ms_free(s->dev);
flushq(&s->rq,0);
ms_mutex_destroy(&s->mutex);
freemsg(s->mire);
ms_free(s);
}
static mblk_t * v4m_make_mire(v4mState *s){
unsigned char *data;
int i,j,line,pos;
int patternw=s->vsize.width/6;
int patternh=s->vsize.height/6;
int red,green=0,blue=0;
if (s->mire==NULL){
s->mire=allocb(s->vsize.width*s->vsize.height*3,0);
s->mire->b_wptr=s->mire->b_datap->db_lim;
}
data=s->mire->b_rptr;
for (i=0;i<s->vsize.height;++i){
line=i*s->vsize.width*3;
if ( ((i+s->frame_ind)/patternh) & 0x1) red=255;
else red= 0;
for (j=0;j<s->vsize.width;++j){
pos=line+(j*3);
if ( ((j+s->frame_ind)/patternw) & 0x1) blue=255;
else blue= 0;
data[pos]=red;
data[pos+1]=green;
data[pos+2]=blue;
}
}
s->frame_ind++;
return s->mire;
}
static mblk_t * v4m_make_nowebcam(v4mState *s){
if (s->mire==NULL && s->frame_ind==0){
s->mire=ms_load_nowebcam(&s->vsize, -1);
}
s->frame_ind++;
return s->mire;
}
static void v4m_process(MSFilter * obj){
v4mState *s=(v4mState*)obj->data;
uint32_t timestamp;
int cur_frame;
if (s->frame_count==-1){
s->start_time=obj->ticker->time;
s->frame_count=0;
}
ms_mutex_lock(&s->mutex);
if (s->seqgrab!=NULL)
{
SGIdle(s->seqgrab);
}
cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
if (cur_frame>=s->frame_count){
mblk_t *om=NULL;
/*keep the most recent frame if several frames have been captured */
if (s->seqgrab!=NULL){
om=getq(&s->rq);
}else{
if (s->pix_fmt==MS_YUV420P
&& s->vsize.width==MS_VIDEO