From 304d25a2828076188afbd32757f5a67009ed866b Mon Sep 17 00:00:00 2001 From: "N.N." Date: Sun, 2 Jan 2005 06:02:50 +0000 Subject: PiDiP 0.12.18 svn path=/trunk/externals/pidip/; revision=2440 --- modules/Makefile | 1 + modules/Makefile.in | 1 + modules/pdp_ascii.c | 1 + modules/pdp_binary.c | 2 +- modules/pdp_ffmpeg~.c | 11 +- modules/pdp_i.c | 2 +- modules/pdp_icedthe~.c | 1369 +++++++++++++++++++++++++++++++++++++++++++++++ modules/pdp_live~.c | 82 ++- modules/pdp_mapper.c | 280 ++++++++++ modules/pdp_theonice~.c | 1333 +++++++++++++++++++++++++++++++++++++++++++++ modules/pdp_theorin~.c | 188 ++++--- modules/pdp_theorout~.c | 89 ++- 12 files changed, 3247 insertions(+), 112 deletions(-) create mode 100644 modules/pdp_icedthe~.c create mode 100644 modules/pdp_mapper.c create mode 100644 modules/pdp_theonice~.c (limited to 'modules') diff --git a/modules/Makefile b/modules/Makefile index 66847ff..c9bd447 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -19,6 +19,7 @@ OBJECTS = pdp_intrusion.o pdp_yqt.o pdp_simura.o pdp_underwatch.o \ pdp_binary.o pdp_erode.o pdp_dilate.o pdp_hitandmiss.o \ pdp_disintegration.o pdp_distance.o pdp_theorin~.o \ pdp_theorout~.o pdp_cropper.o pdp_background.o \ + pdp_mapper.o pdp_theonice~.o pdp_icedthe~.o\ pdp_live~.o pdp_ffmpeg~.o # pdp_xcanvas.o pdp_aa.o all_modules: $(OBJECTS) diff --git a/modules/Makefile.in b/modules/Makefile.in index 56fe02b..88c8bc0 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -19,6 +19,7 @@ OBJECTS = pdp_intrusion.o pdp_yqt.o pdp_simura.o pdp_underwatch.o \ pdp_binary.o pdp_erode.o pdp_dilate.o pdp_hitandmiss.o \ pdp_disintegration.o pdp_distance.o pdp_theorin~.o \ pdp_theorout~.o pdp_cropper.o pdp_background.o \ + pdp_mapper.o pdp_theonice~.o pdp_icedthe~.o\ @PDP_STREAMING_OBJECTS@ # pdp_xcanvas.o pdp_aa.o all_modules: $(OBJECTS) diff --git a/modules/pdp_ascii.c b/modules/pdp_ascii.c index e81d3f2..34f9e96 100644 --- a/modules/pdp_ascii.c +++ b/modules/pdp_ascii.c @@ -315,6 +315,7 @@ static void pdp_ascii_load(t_pdp_ascii *x, t_symbol *sfile) { case '0': case '1': + // post( "pdp_ascii : read %c", charread ); *pdata++ = ( charread == '0' )?0:1; nbexpdata--; break; diff --git a/modules/pdp_binary.c b/modules/pdp_binary.c index 5ce943c..c0ddb10 100644 --- a/modules/pdp_binary.c +++ b/modules/pdp_binary.c @@ -198,7 +198,7 @@ static void pdp_binary_process_yv12(t_pdp_binary *x) diff = 0; if ( x->x_colorY >= 0 ) { - diff += abs(y-x->x_colorY ); + diff = abs(y-x->x_colorY ); } if ( x->x_colorV >= 0 ) { diff --git a/modules/pdp_ffmpeg~.c b/modules/pdp_ffmpeg~.c index 3e229af..fec1c35 100644 --- a/modules/pdp_ffmpeg~.c +++ b/modules/pdp_ffmpeg~.c @@ -322,6 +322,7 @@ static void pdp_ffmpeg_process_yv12(t_pdp_ffmpeg *x) t_int framebytes; t_int owidth, oheight; short *pencbuf; + t_int framerate, atime, ttime; /* allocate all ressources */ if ( ((int)header->info.image.width != x->x_vwidth) || @@ -391,12 +392,12 @@ static void pdp_ffmpeg_process_yv12(t_pdp_ffmpeg *x) x->x_secondcount[ j ] = 0; } } - if ( x->x_secondcount[ svideoindex ] >= (x->x_avcontext->streams[i]->codec.frame_rate/10000) ) + framerate = x->x_avcontext->streams[i]->codec.frame_rate/10000; + ttime = ( ( x->x_nbframes + 1 ) % framerate ) * ( 1000 / framerate ); + atime = ( etime.tv_usec / 1000 ); + // post("pdp_theonice~ : actual : %d, theoretical : %d", atime, ttime ); + if ( atime < ttime ) { - // post("pdp_ffmpeg : index=%d actual : %d, nominal : %d", - // svideoindex, - // x->x_secondcount[ svideoindex ], - // (x->x_avcontext->streams[i]->codec.frame_rate/10000) ); x->x_nbframes_dropped++; continue; } diff --git a/modules/pdp_i.c b/modules/pdp_i.c index a89a56c..f1a47c1 100644 --- a/modules/pdp_i.c +++ b/modules/pdp_i.c @@ -155,7 +155,7 @@ static void pdp_i_free_ressources(t_pdp_i *x) { if ( x->x_ddata ) freebytes( x->x_ddata, x->x_psize ); if ( x->x_hdata ) freebytes( x->x_hdata, x->x_hsize ); - if ( x->x_bdata ) freebytes( x->x_hdata, x->x_bsize ); + if ( x->x_bdata ) freebytes( x->x_bdata, x->x_bsize ); } static void pdp_i_allocate(t_pdp_i *x) diff --git a/modules/pdp_icedthe~.c b/modules/pdp_icedthe~.c new file mode 100644 index 0000000..2a07f96 --- /dev/null +++ b/modules/pdp_icedthe~.c @@ -0,0 +1,1369 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a theora stream reader from icecast2 + * It uses libtheora and some of it code samples ( copyright xiph.org ) + * Written by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include "yuv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* theora stuff */ +#include /* vorbis stuff */ + +#define STRBUF_SIZE 4096 +#define NET_BUFFER_SIZE (4*1024) +#define VIDEO_BUFFER_SIZE (1024*1024) +#define MAX_AUDIO_PACKET_SIZE (64 * 1024) +#define MIN_AUDIO_SIZE (64*1024) + +#define DEFAULT_CHANNELS 1 +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 +#define DEFAULT_FRAME_RATE 25 +#define END_OF_STREAM 20 +#define MIN_PRIORITY 0 +#define DEFAULT_PRIORITY 1 +#define MAX_PRIORITY 20 +#define MAX_NO_STREAM 50 +#define THEORA_NUM_HEADER_PACKETS 3 +#define MAX_WRONG_PACKETS 10 + +static char *pdp_icedthe_version = "pdp_icedthe~: version 0.1, a theora stream reader ( ydegoyon@free.fr)."; + +typedef struct pdp_icedthe_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_pdp *x_header; + unsigned char *x_data; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_outlet *x_pdp_out; // output decoded pdp packets + t_outlet *x_outlet_left; // left audio output + t_outlet *x_outlet_right; // right audio output + t_outlet *x_outlet_state; // for informing of the connection state + t_outlet *x_outlet_nbframes; // number of frames emitted + t_outlet *x_outlet_framerate; // real framerate + t_outlet *x_outlet_endofstream;// for signaling the end of the stream + t_outlet *x_outlet_time; // outputing the video/audio delay + + pthread_t x_decodechild; // stream decoding thread + pthread_t x_connectchild; // connecting thread + pthread_mutex_t x_audiolock; // audio mutex + pthread_mutex_t x_videolock; // video mutex + t_int x_priority; // priority of decoding thread + + char *x_url; // url to connect to + char *x_hostname; // hostname of the server ( or IP ) + char *x_mountpoint; // mountpoint requested + t_int x_bitrate; // bitrate of stream read at connection time + char *x_name; // name of stream + char *x_genre; // genre of stream + t_int x_portnum; // port number + t_int x_insock; // socket file descriptor + t_int x_decoding; // decoding flag + t_int x_theorainit; // flag for indicating that theora is initialized + t_int x_videoready; // video ready flag + t_int x_newpicture; // new picture flag + t_int x_notpackets; // number of theora packets decoded + t_int x_novpackets; // number of vorbis packets decoded + t_int x_nbnostream; // number of cycles without a video stream + t_int x_endofstream; // end of the stream reached + t_int x_nbframes; // number of frames emitted + t_float x_framerate; // framerate + t_int x_samplerate; // audio sample rate + t_int x_audiochannels; // audio channels + t_int x_blocksize; // audio block size + t_int x_audioon; // audio buffer filling flag + t_int x_connected; // connection flag + t_int x_pconnected; // previous state + t_int x_cursec; // current second + t_int x_secondcount; // number of frames received in the current second + struct timeval x_starttime; // reading starting time + char x_request[STRBUF_SIZE]; // string to be send to server + + /* vorbis/theora structures */ + ogg_sync_state x_sync_state; // ogg sync state + ogg_page x_ogg_page; // ogg page + ogg_packet x_ogg_packet; // ogg packet + ogg_stream_state x_statev; // vorbis stream state + ogg_stream_state x_statet; // theora stream state + theora_info x_theora_info; // theora info + theora_comment x_theora_comment; // theora comment + theora_state x_theora_state; // theora state + vorbis_info x_vorbis_info; // vorbis info + vorbis_dsp_state x_dsp_state; // vorbis dsp state + vorbis_block x_vorbis_block; // vorbis block + vorbis_comment x_vorbis_comment; // vorbis comment + yuv_buffer x_yuvbuffer; // yuv buffer + + double x_videotime; // video logical time of last packet + double x_audiotime; // audio logical time of last packet + double x_ptime; // previous state + + /* audio structures */ + t_int x_audio; // flag to activate the decoding of audio + t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_int x_audioin_position; // writing position for incoming audio + +} t_pdp_icedthe; + +static void pdp_icedthe_priority(t_pdp_icedthe *x, t_floatarg fpriority ) +{ + if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) ) + { + x->x_priority = (int)fpriority; + } +} + +static void pdp_icedthe_audio(t_pdp_icedthe *x, t_floatarg faudio ) +{ + if ( ( faudio == 0. ) || ( faudio == 1. ) ) + { + x->x_audio = (int)faudio; + } +} + +static int strip_ice_header(char *head, int n) +{ + int i; + for (i = 0; i < (n - 2); i++) + { + if ((head[i] == '\n') && (head[i + 1] == '\n')) + break; + } + head[i + 1] = '\0'; + return n - (i + 1); +} + +static void pdp_icedthe_disconnect(t_pdp_icedthe *x) +{ + t_int ret, i, count=0; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 100000000; // 100 ms + + if ( x->x_insock == -1 ) + { + post("pdp_icedthe~ : close request but no stream is played ... ignored" ); + return; + } + + if ( x->x_connected ) + { + x->x_connected = 0; + // post("pdp_icedthe~ : waiting end of decoding..." ); + // while ( x->x_decoding ) nanosleep( &twait, NULL ); + + if ( close( x->x_insock ) < 0 ) + { + post( "pdp_icedthe~ : could not close input stream" ); + perror( "fclose" ); + } + x->x_insock = -1; + + if ( x->x_notpackets > 0 ) + { + ogg_stream_clear(&x->x_statet); + theora_clear(&x->x_theora_state); + theora_comment_clear(&x->x_theora_comment); + theora_info_clear(&x->x_theora_info); + } + + if ( x->x_novpackets > 0 ) + { + ogg_stream_clear(&x->x_statev); + vorbis_block_clear(&x->x_vorbis_block); + vorbis_dsp_clear(&x->x_dsp_state); + vorbis_comment_clear(&x->x_vorbis_comment); + vorbis_info_clear(&x->x_vorbis_info); + } + + } + + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_nbnostream = 0; + x->x_nbframes = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + x->x_videoready = 0; + x->x_newpicture = 0; + + x->x_nbframes = 0; + x->x_framerate = 0.; + x->x_videotime = 0.; + x->x_audiotime = 0.; + x->x_endofstream = 1; +} + +static t_int pdp_icedthe_get_buffer_from_network(t_int socket, ogg_sync_state *oy) +{ + char *buffer; + t_int bytes; + + buffer=ogg_sync_buffer(oy, NET_BUFFER_SIZE); + if ( ( bytes = read( socket, buffer, NET_BUFFER_SIZE ) ) < 0 ) + { + post( "pdp_icedthe~ : could not read data from the server" ); + perror( "read" ); + return -1; + } + ogg_sync_wrote(oy,bytes); + return(bytes); +} + +static t_int pdp_icedthe_queue_page(t_pdp_icedthe *x) +{ + + if(x->x_notpackets) + { + if ( x->x_connected && ( ogg_page_serialno(&x->x_ogg_page) == x->x_statet.serialno ) ) + { + // post( "pdp_icedthe~ : got a video page (#=%ld)", x->x_statet.pageno ); + x->x_videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&x->x_ogg_page)); + } + ogg_stream_pagein(&x->x_statet, &x->x_ogg_page); + } + if(x->x_novpackets) + { + if ( x->x_connected && ( ogg_page_serialno(&x->x_ogg_page) == x->x_statev.serialno ) ) + { + // post( "pdp_icedthe~ : got an audio page (#=%ld)", x->x_statev.pageno ); + x->x_audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&x->x_ogg_page)); + } + ogg_stream_pagein(&x->x_statev, &x->x_ogg_page); + } + return 0; +} + +static t_int pdp_icedthe_decode_stream(t_pdp_icedthe *x) +{ + int ret, count, maxsamples, samples, si=0, sj=0; + float **pcm; + struct timespec mwait; + struct timeval ctime; + long long tplaying; + long long ttheoretical; + unsigned char *pY, *pU, *pV; + unsigned char *psY, *psU, *psV; + t_int px, py; + + // post( "pdp_icedthe~ : decode packet" ); + + while ( x->x_novpackets && !x->x_audioon && x->x_connected ) + { + /* if there's pending, decoded audio, grab it */ + if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &pcm))>0) + { + if ( x->x_audio && x->x_connected ) + { + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } + maxsamples=(4*MAX_AUDIO_PACKET_SIZE-x->x_audioin_position); + samples=(retx_audio_inl[x->x_audioin_position], pcm[0], samples*sizeof(t_float) ); + memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], pcm[1], samples*sizeof(t_float) ); + x->x_audioin_position = ( x->x_audioin_position + samples ) % (4*MAX_AUDIO_PACKET_SIZE); + + if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) ) + { + x->x_audioon = 1; + // post( "pdp_icedthe~ : audio on (audioin=%d)", x->x_audioin_position ); + } + // tell vorbis how many samples were read + // post( "pdp_icedthe~ : got %d audio samples (audioin=%d)", samples, x->x_audioin_position ); + vorbis_synthesis_read(&x->x_dsp_state, samples); + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to audio unlock mutex" ); + perror( "pthread_mutex_unlock" ); + } + } + else + { + break; + } + } + else + { + // no pending audio: is there a pending packet to decode? + if( ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet)>0 ) + { + if(vorbis_synthesis(&x->x_vorbis_block, &x->x_ogg_packet)==0) + { + vorbis_synthesis_blockin(&x->x_dsp_state, &x->x_vorbis_block); + } + } + else /* we need more data; suck in another page */ + { + break; + } + } + } + + if ( !x->x_newpicture && x->x_connected ) + { + while(x->x_notpackets && !x->x_videoready && x->x_connected ) + { + // theora is one in, one out... + if(ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet)>0) + { + theora_decode_packetin(&x->x_theora_state, &x->x_ogg_packet); + // post( "pdp_icedthe~ : got one video frame" ); + x->x_videoready=1; + x->x_nbnostream=0; + } + else + { + if ( x->x_nbframes > 0 ) + { + x->x_nbnostream++; + } + + if ( x->x_nbnostream > MAX_NO_STREAM ) + { + post ( "pdp_icedthe~ : receiving too few frames... disconnecting." ); + x->x_endofstream = 1; + x->x_nbframes = 0; + x->x_audioin_position = 0; // reset audio + x->x_audioon = 0; + x->x_connected = 0; + x->x_notpackets = 0; + x->x_novpackets = 0; + } + break; + } + } + + if ( x->x_videoready ) + { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } + theora_decode_YUVout(&x->x_theora_state, &x->x_yuvbuffer); + + // create a new pdp packet from PIX_FMT_YUV420P image format + x->x_vwidth = x->x_yuvbuffer.y_width; + x->x_vheight = x->x_yuvbuffer.y_height; + x->x_vsize = x->x_vwidth*x->x_vheight; + x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_vwidth, x->x_vheight ); + // post( "pdp_icedthe~ : allocated packet %d", x->x_packet0 ); + x->x_header = pdp_packet_header(x->x_packet0); + x->x_data = (unsigned char*) pdp_packet_data(x->x_packet0); + + x->x_header->info.image.encoding = PDP_BITMAP_YV12; + x->x_header->info.image.width = x->x_vwidth; + x->x_header->info.image.height = x->x_vheight; + + pY = x->x_data; + pV = x->x_data+x->x_vsize; + pU = x->x_data+x->x_vsize+(x->x_vsize>>2); + + psY = x->x_yuvbuffer.y; + psU = x->x_yuvbuffer.u; + psV = x->x_yuvbuffer.v; + + for ( py=0; pyx_vheight; py++) + { + memcpy( (void*)pY, (void*)psY, x->x_vwidth ); + pY += x->x_vwidth; + psY += x->x_yuvbuffer.y_stride; + if ( py%2==0 ) + { + memcpy( (void*)pU, (void*)psU, (x->x_vwidth>>1) ); + memcpy( (void*)pV, (void*)psV, (x->x_vwidth>>1) ); + pU += (x->x_vwidth>>1); + pV += (x->x_vwidth>>1); + psU += x->x_yuvbuffer.uv_stride; + psV += x->x_yuvbuffer.uv_stride; + } + } + x->x_newpicture = 1; + // post( "pdp_icedthe~ : new picture decoded" ); + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } + } + } + + // read more data in + if( ( x->x_audioin_position < MIN_AUDIO_SIZE ) || ( !x->x_newpicture ) ) + { + if ( ( ret=pdp_icedthe_get_buffer_from_network(x->x_insock, &x->x_sync_state) ) < 0 ) + { + pdp_icedthe_disconnect(x); + return -1; + } + + // post( "pdp_icedthe~ : read %d bytes from network", ret ); + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0 ) + { + pdp_icedthe_queue_page(x); + } + } + + x->x_videoready = 0; + + return 0; + +} + +static void *pdp_icedthe_decode(void *tdata) +{ + t_pdp_icedthe *x = (t_pdp_icedthe*)tdata; + struct sched_param schedprio; + t_int pmin, pmax, p1; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 25000000; // 25 ms + + schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority; + if ( sched_setscheduler(0, SCHED_FIFO, &schedprio) == -1) + { + post("pdp_icedthe~ : couldn't set priority for decoding thread."); + } + + while ( x->x_decodechild ) + { + if ( x->x_connected ) + { + if ( x->x_decoding == 0 ) + { + post( "pdp_icedthe~ : child started decoding" ); + x->x_decoding = 1; + } + // decode incoming packets + pdp_icedthe_decode_stream( x ); + nanosleep( &twait, NULL ); + } + else + { + if ( x->x_decoding == 1 ) + { + post( "pdp_icedthe~ : child stopped decoding" ); + x->x_decoding = 0; + } + nanosleep( &twait, NULL ); // nothing to do, just wait + } + } + + x->x_decodechild = 0; + post("pdp_icedthe~ : decoding child exiting." ); + return NULL; +} + +static void pdp_icedthe_connect(t_pdp_icedthe *x, t_symbol *s); + +static void *pdp_icedthe_do_connect(void *tdata) +{ + pthread_attr_t decode_child_attr; + ogg_stream_state o_tempstate; + t_pdp_icedthe *x = (t_pdp_icedthe*)tdata; + t_int sockfd; + struct sockaddr_in server; + struct hostent *hp; + fd_set fdset; + struct timeval tv; + t_int numrelocs = 0; + t_int i, ret, rest, nanswers=0; + char *cpoint = NULL; + t_int offset = 0, endofheaders = 0, wpackets = 0; + char *sptr = NULL; + char *url; /* used for relocation */ + + if ( x->x_insock != -1 ) + { + post("pdp_icedthe~ : connect request but a stream is open ... disconnecting" ); + pdp_icedthe_disconnect(x); + } + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if ( ( sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ) <= 0 ) + { + error("pdp_icedthe~: couldn't obtain a socket"); + x->x_connectchild = 0; + return NULL; + } + + server.sin_family = AF_INET; + hp = gethostbyname(x->x_hostname); + if (hp == 0) + { + post("pdp_icedthe~: your server is unresolved here."); + if ( close(sockfd) < 0 ) post("pdp_icedthe~: could not close socket" ); + x->x_connectchild = 0; + return NULL; + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((unsigned short)x->x_portnum); + + /* try to connect. */ + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + error("pdp_icedthe~: connection failed!\n"); + if ( close(sockfd) < 0 ) post("pdp_icedthe~: could not close socket" ); + x->x_connectchild = 0; + return NULL; + } + post("pdp_icedthe~: connected : socket opened" ); + + /* check if we can read from socket */ + FD_ZERO( &fdset); + FD_SET( sockfd, &fdset); + tv.tv_sec = 0; /* seconds */ + tv.tv_usec = 500; /* microseconds */ + + ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); + if(ret != 0) + { + error("pdp_icedthe~: can not read from socket"); + if ( close(sockfd) < 0 ) post("pdp_icedthe~: could not close socket" ); + x->x_connectchild = 0; + return NULL; + } + + /* build up stuff we need to send to server */ + sprintf(x->x_request, "GET /%s HTTP/1.0 \r\nHost: %s\r\nUser-Agent: pdp_icedthe~ 0.1\r\nAccept: */*\r\n\r\n", + x->x_mountpoint, x->x_hostname); + + if ( send(sockfd, x->x_request, strlen(x->x_request), 0) < 0 ) /* say hello to server */ + { + post( "pdp_icedthe~: could not contact server... " ); + perror( "send" ); + x->x_connectchild = 0; + return NULL; + } + post("pdp_icedthe~: send done" ); + + memset( x->x_request, 0x00, STRBUF_SIZE ); + + // read all the answer + endofheaders=0; + while ( !endofheaders ) + { + if( ( ret = recv(sockfd, x->x_request+offset, 1, MSG_NOSIGNAL) ) <0) + { + error("pdp_icedthe~: no response from server"); + perror( "recv" ); + x->x_connectchild = 0; + return NULL; + } + // post ( "pdp_icedthe~ : received %d bytes at %d", ret, offset ); + for ( i=0; i<=offset; i++ ) + { + if ( ( x->x_request[i] == '\n' && x->x_request[i+1] == '\n' ) || + ( x->x_request[i] == 10 && x->x_request[i+1] == 13 ) ) + { + endofheaders=1; + } + } + if ( offset+ret < STRBUF_SIZE ) + { + offset+=ret; + } + else + { + post( "pdp_icedthe~ : headers too long." ); + x->x_connectchild = 0; + return NULL; + } + } + + // post( "pdp_icedthe~ : read HTTP headers : %s", x->x_request ); + post( "pdp_icedthe~ : got HTTP answer" ); + + strip_ice_header(x->x_request, STRBUF_SIZE); + if(sptr = strstr(x->x_request, "302")) + { + cpoint = NULL; + cpoint = strstr(x->x_request, "Location:"); + if ( cpoint == NULL ) + { + post( "pdp_icedthe~ : stream has moved but couldn't find new location out of this :" ); + post("pdp_icedthe~: %s", x->x_request ); + x->x_connectchild = 0; + return NULL; + } + url = strdup(cpoint + 10); + post("pdp_icedthe~: relocating to %s", url); + if ( close(sockfd) < 0 ) post("pdp_icedthe~: could not close socket" ); + x->x_connectchild = 0; + pdp_icedthe_connect(x, gensym(url)); + return NULL; + } + if ( !(sptr = strstr(x->x_request, "200")) ) + { + error("pdp_icedthe~: cannot connect to the stream"); + error("pdp_icedthe~: server answered : %s", x->x_request); + x->x_connectchild = 0; + return NULL; + } + + // check what we got + if( cpoint = strstr(x->x_request, "x-audiocast-mount:")) + { + if ( x->x_mountpoint ) free( x->x_mountpoint ); + x->x_mountpoint = strdup(cpoint + 18); + for ( i=0; i<(int)strlen(x->x_mountpoint); i++ ) + { + if ( x->x_mountpoint[i] == '\n' ) + { + x->x_mountpoint[i] = '\0'; + break; + } + } + post(" mountpoint: %s", x->x_mountpoint); + } + if( cpoint = strstr(x->x_request, "x-audiocast-server-url:")) + { + sptr = strdup( cpoint + 24); + for ( i=0; i<(int)strlen(sptr); i++ ) + { + if ( sptr[i] == '\n' ) + { + sptr[i] = '\0'; + break; + } + } + post(" server-url: %s", sptr); + } + if( cpoint = strstr(x->x_request, "x-audiocast-location:")) + { + sptr = strdup( cpoint + 22); + for ( i=0; i<(int)strlen(sptr); i++ ) + { + if ( sptr[i] == '\n' ) + { + sptr[i] = '\0'; + break; + } + } + post(" location: %s", sptr); + } + if( cpoint = strstr(x->x_request, "x-audiocast-admin:")) + { + sptr = strdup( cpoint + 19); + for ( i=0; i<(int)strlen(sptr); i++ ) + { + if ( sptr[i] == '\n' ) + { + sptr[i] = '\0'; + break; + } + } + post(" admin: %s", sptr); + } + if( cpoint = strstr(x->x_request, "x-audiocast-name:")) + { + x->x_name = strdup( cpoint + 17); + for ( i=0; i<(int)strlen(x->x_name); i++ ) + { + if ( x->x_name[i] == '\n' ) + { + x->x_name[i] = '\0'; + break; + } + } + post(" name: %s", x->x_name); + } + if( cpoint = strstr(x->x_request, "x-audiocast-genre:")) + { + x->x_genre = strdup( cpoint + 18); + for ( i=0; i<(int)strlen(x->x_genre); i++ ) + { + if ( x->x_genre[i] == '\n' ) + { + x->x_genre[i] = '\0'; + break; + } + } + post(" genre: %s", x->x_genre); + } + if( cpoint = strstr(x->x_request, "x-audiocast-url:")) + { + if ( x->x_url ) free( x->x_url ); + x->x_url = strdup( cpoint + 16); + for ( i=0; i<(int)strlen(x->x_url); i++ ) + { + if ( x->x_url[i] == '\n' ) + { + x->x_url[i] = '\0'; + break; + } + } + post(" url: %s", x->x_url); + } + if( cpoint = strstr(x->x_request, "x-audiocast-public:1")) + { + post(" broadcast is public"); + } + else if( cpoint = strstr(x->x_request, "x-audiocast-public:0")) + { + post(" broadcast is NOT public"); + } + if( cpoint = strstr(x->x_request, "x-audiocast-bitrate:")) + { + sptr = strdup( cpoint + 20); + for ( i=0; i<(int)strlen(sptr); i++ ) + { + if ( sptr[i] == '\n' ) + { + sptr[i] = '\0'; + break; + } + } + if(!strncmp(sptr, "320", 3))x->x_bitrate = 320; + else if(!strncmp(sptr, "256", 3))x->x_bitrate = 256; + else if(!strncmp(sptr, "224", 3))x->x_bitrate = 224; + else if(!strncmp(sptr, "192", 3))x->x_bitrate = 192; + else if(!strncmp(sptr, "160", 3))x->x_bitrate = 160; + else if(!strncmp(sptr, "144", 3))x->x_bitrate = 144; + else if(!strncmp(sptr, "128", 3))x->x_bitrate = 128; + else if(!strncmp(sptr, "112", 3))x->x_bitrate = 112; + else if(!strncmp(sptr, "96", 2))x->x_bitrate = 96; + else if(!strncmp(sptr, "80", 2))x->x_bitrate = 80; + else if(!strncmp(sptr, "64", 2))x->x_bitrate = 64; + else if(!strncmp(sptr, "56", 2))x->x_bitrate = 56; + else if(!strncmp(sptr, "48", 2))x->x_bitrate = 48; + else if(!strncmp(sptr, "40", 2))x->x_bitrate = 40; + else if(!strncmp(sptr, "32", 2))x->x_bitrate = 32; + else if(!strncmp(sptr, "24", 2))x->x_bitrate = 24; + else if(!strncmp(sptr, "16", 2))x->x_bitrate = 16; + else if(!strncmp(sptr, "8", 1))x->x_bitrate = 8; + else + { + post("pdp_icedthe~: unsupported bitrate! : %s", sptr); + x->x_connectchild = 0; + return NULL; + } + post(" bitrate: %d", x->x_bitrate); + } + + ogg_sync_init(&x->x_sync_state); + x->x_notpackets=0; + x->x_novpackets=0; + + // init supporting Vorbis structures needed in header parsing + vorbis_info_init(&x->x_vorbis_info); + vorbis_comment_init(&x->x_vorbis_comment); + + // init supporting Theora structures needed in header parsing + theora_comment_init(&x->x_theora_comment); + theora_info_init(&x->x_theora_info); + + // parse headers + while( !x->x_theorainit ) + { + ret = pdp_icedthe_get_buffer_from_network(sockfd, &x->x_sync_state); + if ( ret==0) break; + if ( ret<0) + { + post( "pdp_icedthe~ : unable to read from server" ); + pdp_icedthe_disconnect( x ); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page) > 0 ) + { + pdp_icedthe_queue_page(x); + + ogg_stream_init(&o_tempstate, ogg_page_serialno(&x->x_ogg_page)); + ogg_stream_pagein(&o_tempstate, &x->x_ogg_page); + ogg_stream_packetout(&o_tempstate, &x->x_ogg_packet); + + /* identify the codec: try theora */ + if(!x->x_notpackets && + theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet)>=0) + { + // it is theora + // post( "pdp_icedthe~ : got one theora header."); + memcpy(&x->x_statet, &o_tempstate, sizeof(o_tempstate)); + x->x_notpackets=1; + x->x_theorainit = 1; + }else + if(!x->x_novpackets && + vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet)>=0) + { + // post( "pdp_icedthe~ : got one vorbis header."); + memcpy(&x->x_statev, &o_tempstate, sizeof(o_tempstate)); + x->x_novpackets=1; + } + else + { + // post( "pdp_icedthe~ : got something else."); + wpackets++; + ogg_stream_clear(&o_tempstate); + if ( wpackets > MAX_WRONG_PACKETS ) + { + post( "pdp_icedthe~ : couldn't read any headers."); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + } + } + } + + // we're expecting more header packets. + while( (x->x_notpackets && x->x_notpacketsx_novpackets && x->x_novpacketsx_notpackets && (x->x_notpacketsx_statet, &x->x_ogg_packet))) + { + if( ret<0 ) + { + post("pdp_icedthe~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + if( theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet) ) + { + post("pdp_icedthe~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + x->x_notpackets++; + if(x->x_notpackets==THEORA_NUM_HEADER_PACKETS) break; + } + + /* look for more vorbis header packets */ + while(x->x_novpackets && (x->x_novpacketsx_statev, &x->x_ogg_packet))) + { + if(ret<0) + { + post("pdp_icedthe~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + if( vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet) ) + { + post("pdp_icedthe~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + x->x_novpackets++; + if(x->x_novpackets==THEORA_NUM_HEADER_PACKETS) break; + } + + if(ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0) + { + pdp_icedthe_queue_page(x); + } + else + { + ret=pdp_icedthe_get_buffer_from_network(sockfd, &x->x_sync_state); + if( ret==0 ) + { + post("pdp_icedthe~ : end of stream while parsing headers\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + if( ret<0 ) + { + post("pdp_icedthe~ : error reading from server\n"); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + } + } + post( "pdp_icedthe~ : parsed headers ok (not=%d) (nov=%d)", x->x_notpackets, x->x_novpackets ); + + // initialize decoders + if( x->x_notpackets ) + { + theora_decode_init(&x->x_theora_state, &x->x_theora_info); + if ( x->x_theora_info.fps_denominator != 0 ) + { + x->x_framerate = x->x_theora_info.fps_numerator/x->x_theora_info.fps_denominator; + } + else + { + x->x_framerate = DEFAULT_FRAME_RATE; + } + post("pdp_icedthe~ : stream %x is theora %dx%d %f fps video.", + x->x_statet.serialno, + x->x_theora_info.width,x->x_theora_info.height, + x->x_framerate); + if(x->x_theora_info.width!=x->x_theora_info.frame_width || + x->x_theora_info.height!=x->x_theora_info.frame_height) + { + post("pdp_icedthe~ : frame content is %dx%d with offset (%d,%d).", + x->x_theora_info.frame_width, x->x_theora_info.frame_height, + x->x_theora_info.offset_x, x->x_theora_info.offset_y); + } + x->x_vwidth = x->x_theora_info.width; + x->x_vheight = x->x_theora_info.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + switch(x->x_theora_info.colorspace) + { + case OC_CS_UNSPECIFIED: + /* nothing to report */ + break;; + case OC_CS_ITU_REC_470M: + post("pdp_icedthe~ : encoder specified ITU Rec 470M (NTSC) color."); + break;; + case OC_CS_ITU_REC_470BG: + post("pdp_icedthe~ : encoder specified ITU Rec 470BG (PAL) color."); + break;; + default: + post("pdp_icedthe~ : warning: encoder specified unknown colorspace (%d).", + x->x_theora_info.colorspace); + break;; + } + } + else + { + // tear down the partial theora setup + theora_info_clear(&x->x_theora_info); + theora_comment_clear(&x->x_theora_comment); + post("pdp_icedthe~ : could not initialize theora decoder (theora packets=%d).", x->x_notpackets); + x->x_theorainit = 0; + x->x_connectchild = 0; + return NULL; + } + + if( x->x_novpackets ) + { + vorbis_synthesis_init(&x->x_dsp_state, &x->x_vorbis_info); + vorbis_block_init(&x->x_dsp_state, &x->x_vorbis_block); + x->x_audiochannels = x->x_vorbis_info.channels; + x->x_samplerate = x->x_vorbis_info.rate; + post("pdp_icedthe~ : ogg logical stream %x is vorbis %d channel %d Hz audio.", + x->x_statev.serialno, + x->x_audiochannels, x->x_samplerate); + } + else + { + /* tear down the partial vorbis setup */ + vorbis_info_clear(&x->x_vorbis_info); + vorbis_comment_clear(&x->x_vorbis_comment); + post("pdp_icedthe~ : could not initialize vorbis decoder (vorbis packets=%d).", x->x_novpackets); + // x->x_theorainit = 0; + // return NULL; + x->x_audio = 0; + } + // everything seems to be ready + x->x_insock = sockfd; + x->x_connected = 1; + + if ( x->x_decodechild == 0 ) + { + x->x_decodechild = 1; // trick & treets + // launch decoding thread + if ( pthread_attr_init( &decode_child_attr ) < 0 ) + { + post( "pdp_icedthe~ : could not launch decoding thread" ); + perror( "pthread_attr_init" ); + pthread_exit(NULL); + } + if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_icedthe_decode, x ) < 0 ) + { + post( "pdp_icedthe~ : could not launch decoding thread" ); + perror( "pthread_create" ); + pthread_exit(NULL); + } + else + { + post( "pdp_icedthe~ : decoding thread %d launched", (int)x->x_decodechild ); + } + } + + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_icedthe~ : could not set start time" ); + } + + x->x_nbframes = 0; + x->x_endofstream = -1; + x->x_connectchild = 0; + post("pdp_icedthe~ : connect child exiting." ); + + return NULL; +} + +static void pdp_icedthe_connect(t_pdp_icedthe *x, t_symbol *s) +{ + t_int ret, i, length; + pthread_attr_t connect_child_attr; + char *hostptr = NULL, *p, *endhost = NULL, *pathptr = NULL; + + if ( x->x_connectchild != 0 ) + { + post("pdp_icedthe~ : connection request but a connection is pending ... ignored." ); + return; + } + + if ( x->x_connected ) + { + post("pdp_icedthe~ : connection request but a connection is established ... disconnecting." ); + pdp_icedthe_disconnect(x); + } + + if ( x->x_url ) free( x->x_url ); + x->x_url = (char*) malloc( strlen( s->s_name ) + 1 ); + strcpy( x->x_url, s->s_name ); + + post ("pdp_icedthe~ : connecting to url=%s", x->x_url ); + + /* strip http:// or ftp:// */ + p = x->x_url; + if (strncmp(p, "http://", 7) == 0) p+=7; + + hostptr = p; + while (*p && *p != '/' && *p != ':') /* look for end of hostname: */ p++; + + endhost = p; + switch ( *p ) + { + case ':' : + x->x_portnum = atoi( p+1 ); + while (*p && *p != '/') p++; + pathptr = p+1; + break; + case '/' : + x->x_portnum = 8000; + pathptr = p+1; + break; + default : + if ( ( p - x->x_url ) != (int)strlen( x->x_url ) ) + { + post( "pdp_icedthe~ : wrong url : %s", hostptr ); + return; + } + pathptr = ""; + break; + } + + length = (t_int)(endhost - hostptr); + if ( x->x_hostname ) + { + post ("pdp_icedthe~ : freeing hostname" ); + free( x->x_hostname ); + } + x->x_hostname=(char*)malloc( length + 1); + strncpy( x->x_hostname, hostptr, length ); + x->x_hostname[ length ] = '\0'; + + if ( x->x_mountpoint ) free( x->x_mountpoint ); + x->x_mountpoint=(char*)malloc( strlen( pathptr ) + 1); + strncpy( x->x_mountpoint, pathptr, strlen( pathptr ) ); + x->x_mountpoint[ strlen( pathptr ) ] = '\0'; + + post ("pdp_icedthe~ : connecting to host=%s port=%d mountpoint=%s", x->x_hostname, x->x_portnum, x->x_mountpoint ); + + // launch connection thread + if ( pthread_attr_init( &connect_child_attr ) < 0 ) { + post( "pdp_icedthe~ : could not launch connection thread" ); + perror( "pthread_attr_init" ); + return; + } + if ( pthread_attr_setdetachstate( &connect_child_attr, PTHREAD_CREATE_DETACHED ) < 0 ) { + post( "pdp_icedthe~ : could not launch connection thread" ); + perror( "pthread_attr_setdetachstate" ); + return; + } + if ( pthread_create( &x->x_connectchild, &connect_child_attr, pdp_icedthe_do_connect, x ) < 0 ) { + post( "pdp_icedthe~ : could not launch connection thread" ); + perror( "pthread_create" ); + return; + } + else + { + post( "pdp_icedthe~ : connection thread %d launched", (int)x->x_connectchild ); + } + + return; +} + + /* decode the audio buffer */ +static t_int *pdp_icedthe_perform(t_int *w) +{ + t_float *out1 = (t_float *)(w[1]); // left audio inlet + t_float *out2 = (t_float *)(w[2]); // right audio inlet + t_pdp_icedthe *x = (t_pdp_icedthe *)(w[3]); + int n = (int)(w[4]); // number of samples + struct timeval etime; + t_int sn; + + x->x_blocksize = n; + + // just read the buffer + if ( x->x_audioon && x->x_connected ) + { + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } + sn=0; + while (n--) + { + *(out1)=x->x_audio_inl[ sn ]; + if ( x->x_audiochannels == 1 ) + { + *(out2) = *(out1); + sn++; + } + if ( x->x_audiochannels == 2 ) + { + *(out2)=x->x_audio_inr[ sn++ ]; + } + out1++; + out2++; + } + memcpy( &x->x_audio_inl[0], &x->x_audio_inl[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + memcpy( &x->x_audio_inr[0], &x->x_audio_inr[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + x->x_audioin_position-=sn; + // post( "pdp_icedthe~ : audio in position : %d", x->x_audioin_position ); + if ( x->x_audioin_position <= sn ) + { + x->x_audioon = 0; + // post( "pdp_icedthe~ : audio off ( audioin : %d, channels=%d )", + // x->x_audioin_position, x->x_audiochannels ); + } + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock audio mutex" ); + perror( "pthread_mutex_unlock" ); + } + } + else + { + // post("pdp_icedthe~ : no available audio" ); + while (n--) + { + *(out1++) = 0.0; + *(out2++) = 0.0; + } + } + + // update framerate + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_icedthe~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + if (x->x_connected) outlet_float( x->x_outlet_framerate, x->x_secondcount ); + x->x_secondcount = 0; + } + + // output image if there's a new one decoded + if ( x->x_newpicture ) + { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } + pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); + x->x_newpicture = 0; + + // update streaming status + x->x_nbframes++; + x->x_secondcount++; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } + } + if ( x->x_endofstream == 1 ) // only once + { + outlet_float( x->x_outlet_endofstream, x->x_endofstream ); + outlet_float( x->x_outlet_time, 0. ); + x->x_endofstream = 0; + } + if ( x->x_endofstream == -1 ) // reset + { + x->x_endofstream = 0; + outlet_float( x->x_outlet_endofstream, x->x_endofstream ); + } + if ( x->x_connected != x->x_pconnected ) + { + x->x_pconnected = x->x_connected; + outlet_float( x->x_outlet_state, x->x_connected ); + } + if ( ( x->x_videotime != -1) && ( x->x_audiotime != -1 ) && + ( (x->x_videotime-x->x_audiotime) != x->x_ptime ) ) + { + x->x_ptime = x->x_videotime-x->x_audiotime; + outlet_float( x->x_outlet_time, (t_float)(x->x_videotime-x->x_audiotime) ); + } + + return (w+5); +} + +static void pdp_icedthe_dsp(t_pdp_icedthe *x, t_signal **sp) +{ + dsp_add(pdp_icedthe_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_icedthe_free(t_pdp_icedthe *x) +{ + int i; + + if ( x->x_decodechild ) + { + x->x_decodechild = 0; + } + + if ( x->x_connected ) + { + pdp_icedthe_disconnect(x); + x->x_connected = 0; + } + + post( "pdp_icedthe~ : freeing object" ); +} + +t_class *pdp_icedthe_class; + +void *pdp_icedthe_new(void) +{ + int i; + + t_pdp_icedthe *x = (t_pdp_icedthe *)pd_new(pdp_icedthe_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + + x->x_pdp_out = outlet_new(&x->x_obj, &s_anything); + + x->x_outlet_left = outlet_new(&x->x_obj, &s_signal); + x->x_outlet_right = outlet_new(&x->x_obj, &s_signal); + + x->x_outlet_state = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float); + x->x_outlet_endofstream = outlet_new(&x->x_obj, &s_float); + x->x_outlet_time = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_decodechild = 0; + x->x_connectchild = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + x->x_priority = DEFAULT_PRIORITY; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_nbframes = 0; + x->x_samplerate = 0; + x->x_audio = 1; + x->x_audiochannels = 0; + x->x_audioin_position = 0; + x->x_videoready = 0; + x->x_newpicture = 0; + x->x_endofstream = 0; + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_blocksize = MIN_AUDIO_SIZE; + x->x_insock = -1; + x->x_connected = 0; + x->x_pconnected = 0; + x->x_url = NULL; + x->x_hostname = NULL; + x->x_mountpoint = NULL; + x->x_portnum = 8000; + x->x_nbnostream = 0; + x->x_videotime = -1.0; + x->x_audiotime = -1.0; + x->x_ptime = 0.; + + memset( &x->x_audio_inl[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + memset( &x->x_audio_inr[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_icedthe_tilde_setup(void) +{ + // post( pdp_icedthe_version ); + pdp_icedthe_class = class_new(gensym("pdp_icedthe~"), (t_newmethod)pdp_icedthe_new, + (t_method)pdp_icedthe_free, sizeof(t_pdp_icedthe), 0, A_NULL); + + class_addmethod(pdp_icedthe_class, (t_method)pdp_icedthe_dsp, gensym("dsp"), A_NULL); + class_addmethod(pdp_icedthe_class, (t_method)pdp_icedthe_connect, gensym("connect"), A_SYMBOL, A_NULL); + class_addmethod(pdp_icedthe_class, (t_method)pdp_icedthe_disconnect, gensym("disconnect"), A_NULL); + class_addmethod(pdp_icedthe_class, (t_method)pdp_icedthe_priority, gensym("priority"), A_FLOAT, A_NULL); + class_addmethod(pdp_icedthe_class, (t_method)pdp_icedthe_audio, gensym("audio"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_icedthe_class, gensym("pdp_icedthe~.pd") ); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_live~.c b/modules/pdp_live~.c index c403a15..2404aba 100644 --- a/modules/pdp_live~.c +++ b/modules/pdp_live~.c @@ -83,6 +83,8 @@ typedef struct pdp_live_struct pthread_t x_connectchild; // thread used for connecting to a stream pthread_t x_decodechild; // stream decoding thread + pthread_mutex_t x_audiolock; // audio mutex + pthread_mutex_t x_videolock; // video mutex t_int x_usethread; // flag to activate decoding in a thread t_int x_autoplay; // flag to autoplay the file ( default = true ) t_int x_nextimage; // flag to play next image in manual mode @@ -178,8 +180,8 @@ static void pdp_live_bang(t_pdp_live *x) static void pdp_live_frame_cold(t_pdp_live *x, t_floatarg frameindex) { - int frame = (int)frameindex; - int ret; + t_int frame = (int)frameindex; + t_int ret, flags=0; uint64_t newpts; if (!(x->x_streaming)) return; @@ -196,7 +198,8 @@ static void pdp_live_frame_cold(t_pdp_live *x, t_floatarg frameindex) post( "pdp_live~ : couldn't seek requested frame ( framerate = %d )", x->x_framerate ); return; } - if ( ( ret = av_seek_frame(x->x_avcontext, x->x_videoindex, newpts) ) < 0 ) + if ( frame < x->x_nbframes ) flags = AVSEEK_FLAG_BACKWARD; + if ( ( ret = av_seek_frame(x->x_avcontext, x->x_videoindex, newpts, flags) ) < 0 ) { post( "pdp_live~ : couldn't seek the requested frame (ret=%d)", ret ); } @@ -242,7 +245,7 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) if ( ( x->x_avcontext->iformat->flags & AVFMT_NOFILE ) == 0 ) { post( "pdp_live~ : looping file reading..." ); - if ( ( ret = av_seek_frame(x->x_avcontext, x->x_videoindex, 0) ) < 0 ) + if ( ( ret = av_seek_frame(x->x_avcontext, x->x_videoindex, 0, 0) ) < 0 ) { post( "pdp_live~ : couldn't seek the requested frame (ret=%d)", ret ); } @@ -301,6 +304,12 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) continue; } + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_live~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } + // resample received audio // post( "pdp_live~ : resampling from %dHz-%dch to %dHz-%dch (in position=%d)", // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.sample_rate, @@ -336,6 +345,11 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) x->x_audioon = 1; // post( "pdp_live~ : audio on" ); } + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_live~ : unable to unlock audio mutex" ); + perror( "pthread_mutex_unlock" ); + } break; case CODEC_TYPE_VIDEO: @@ -383,6 +397,11 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) } else { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_live~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } x->x_newpicture=1; x->x_vwidth = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.width; x->x_vheight = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.height; @@ -429,13 +448,13 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) { if ( gettimeofday(&ctime, NULL) == -1) { - post("pdp_theorin~ : could not read time" ); + post("pdp_live~ : could not read time" ); } tplaying = ( ctime.tv_sec-x->x_starttime.tv_sec )*1000 + ( ctime.tv_usec-x->x_starttime.tv_usec )/1000; ttheoretical = ((x->x_nbframes)*1000 )/x->x_framerate; - // post( "pdp-theorin~ : %d playing since : %lldms ( theory : %lldms )", + // post( "pdp_live~ : %d playing since : %lldms ( theory : %lldms )", // x->x_nbframes, tplaying, ttheoretical ); if ( tplaying < ttheoretical ) @@ -447,6 +466,11 @@ static t_int pdp_live_decode_packet(t_pdp_live *x) if ( x->x_autoplay ) nanosleep( &mwait, NULL ); } } + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_live~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } } break; } @@ -473,7 +497,7 @@ static void *pdp_decode_stream_from_url(void *tdata) schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority; if ( sched_setscheduler(0,SCHED_FIFO,&schedprio) == -1) { - post("pdp_theorin~ : couldn't set priority for decoding thread."); + post("pdp_live~ : couldn't set priority for decoding thread."); } if ( ! (x->x_avcontext->iformat->flags & AVFMT_NOHEADER ) ) @@ -764,6 +788,11 @@ static t_int *pdp_live_perform(t_int *w) // just read the buffer if ( x->x_audioon ) { + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_live~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } sn=0; while (n--) { @@ -789,6 +818,11 @@ static t_int *pdp_live_perform(t_int *w) x->x_audioon = 0; // post( "pdp_live~ : audio off" ); } + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_live~ : unable to audio unlock mutex" ); + perror( "pthread_mutex_unlock" ); + } } else { @@ -819,6 +853,11 @@ static t_int *pdp_live_perform(t_int *w) // output image if there's a new one decoded if ( x->x_newpicture ) { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_live~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); x->x_newpicture = 0; @@ -826,6 +865,11 @@ static t_int *pdp_live_perform(t_int *w) x->x_nbframes++; x->x_secondcount++; outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_live~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } } outlet_float( x->x_outlet_streaming, x->x_streaming ); outlet_float( x->x_outlet_endofstream, x->x_endofstream ); @@ -856,6 +900,17 @@ static void pdp_live_free(t_pdp_live *x) post( "pdp_live~ : freeing object" ); pdp_packet_mark_unused(x->x_packet0); av_free_static(); + + if ( pthread_mutex_destroy( &x->x_audiolock ) < 0 ) + { + post( "pdp_live~ : unable to destroy audio mutex" ); + perror( "pthread_mutex_destroy" ); + } + if ( pthread_mutex_destroy( &x->x_videolock ) < 0 ) + { + post( "pdp_live~ : unable to destroy video mutex" ); + perror( "pthread_mutex_destroy" ); + } } t_class *pdp_live_class; @@ -907,6 +962,19 @@ void *pdp_live_new(void) x->x_previouspts = -1; x->x_firstpts = -1; + if ( pthread_mutex_init( &x->x_audiolock, NULL ) < 0 ) + { + post( "pdp_live~ : unable to initialize audio mutex" ); + perror( "pthread_mutex_init" ); + return NULL; + } + if ( pthread_mutex_init( &x->x_videolock, NULL ) < 0 ) + { + post( "pdp_live~ : unable to initialize video mutex" ); + perror( "pthread_mutex_init" ); + return NULL; + } + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); if ( !x->x_avcontext ) { diff --git a/modules/pdp_mapper.c b/modules/pdp_mapper.c new file mode 100644 index 0000000..9d6fe55 --- /dev/null +++ b/modules/pdp_mapper.c @@ -0,0 +1,280 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a pixels mapper enabling to do some cut and paste + * Written by Yves Degoyon + */ + +#include "pdp.h" +#include + +static char *pdp_mapper_version = "pdp_mapper: version 0.1, a pixels mapper, written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_mapper_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + unsigned int x_encoding; + t_int *x_pixelmap; + +} t_pdp_mapper; + +static void pdp_mapper_copy(t_pdp_mapper *x, t_floatarg fromX, t_floatarg fromY, t_floatarg toX, t_floatarg toY) +{ + if ( ( fromX >= 0 ) && ( fromX < x->x_vwidth ) && + ( toX >= 0 ) && ( toX < x->x_vwidth ) && + ( fromY >= 0 ) && ( fromY < x->x_vheight ) && + ( toY >= 0 ) && ( toY < x->x_vheight ) ) + { + x->x_pixelmap[ (t_int)toY*x->x_vwidth+(t_int)toX ] = x->x_pixelmap[ (t_int)fromY*x->x_vwidth+(t_int)fromX ]; + } +} + +static void pdp_mapper_reset(t_pdp_mapper *x) +{ + t_int px, py; + + if ( x->x_vsize > 0 ) + { + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + x->x_pixelmap[py*x->x_vwidth+px] = py*x->x_vwidth+px; + } + } + } +} + +static void pdp_mapper_swap(t_pdp_mapper *x, t_floatarg fromX, t_floatarg fromY, t_floatarg toX, t_floatarg toY) +{ + + t_int tval; + + if ( ( fromX >= 0 ) && ( fromX < x->x_vwidth ) && + ( toX >= 0 ) && ( toX < x->x_vwidth ) && + ( fromY >= 0 ) && ( fromY < x->x_vheight ) && + ( toY >= 0 ) && ( toY < x->x_vheight ) ) + { + tval = x->x_pixelmap[ (t_int)toY*x->x_vwidth+(t_int)toX ]; + x->x_pixelmap[ (t_int)toY*x->x_vwidth+(t_int)toX ] = x->x_pixelmap[ (t_int)fromY*x->x_vwidth+(t_int)fromX ]; + x->x_pixelmap[ (t_int)fromY*x->x_vwidth+(t_int)fromX ] = tval; + } +} + +static void pdp_mapper_allocate(t_pdp_mapper *x, t_int newsize) +{ + int i, px, py; + + if ( x->x_pixelmap != NULL ) + { + freebytes( x->x_pixelmap, x->x_vsize*sizeof(t_int) ); + } + + x->x_vsize = newsize; + x->x_pixelmap = (t_int*) getbytes( x->x_vsize*sizeof(t_int) ); + + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + x->x_pixelmap[py*x->x_vwidth+px] = py*x->x_vwidth+px; + } + } +} + +static void pdp_mapper_process_yv12(t_pdp_mapper *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + int i; + + t_int px, py, ppx, ppy, offset; + short int *sy, *su, *sv, t; + t_int *spy; + short int *sny, *snu, *snv; + + /* allocate all ressources */ + if ( ((t_int)header->info.image.width != x->x_vwidth ) || + ((t_int)header->info.image.height != x->x_vheight ) ) + { + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + post( "pdp_mapper : reallocating buffers" ); + pdp_mapper_allocate(x, header->info.image.width*header->info.image.height ); + } + + x->x_encoding = header->info.image.encoding; + + newheader->info.image.encoding = x->x_encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + /* copy images if necessary */ + // memcpy( newdata, data, (( x->x_vsize + (x->x_vsize>>1))<<1)); + + sy = data; + su = (data+x->x_vsize); + sv = (data+x->x_vsize+(x->x_vsize>>2)); + spy = x->x_pixelmap; + sny = newdata; + snu = (newdata+x->x_vsize); + snv = (newdata+x->x_vsize+(x->x_vsize>>2)); + + for(py=1; pyx_vheight; py++) + { + for(px=0; pxx_vwidth; px++) + { + ppy = (*(spy)/x->x_vwidth); + ppx = (*(spy)%x->x_vwidth); + *(sny) = *(sy+ppy*x->x_vwidth+ppx); + *(snu) = *(su+(ppy>>1)*(x->x_vwidth>>1)+(ppx>>1)); + *(snv) = *(sv+(ppy>>1)*(x->x_vwidth>>1)+(ppx>>1)); + sny++; spy++; + if ( ( px%2 == 0 ) && ( py%2 == 0 ) ) + { + snu++; snv++; + } + } + } + + return; +} + +static void pdp_mapper_sendpacket(t_pdp_mapper *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1); +} + +static void pdp_mapper_process(t_pdp_mapper *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_mapper_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_mapper_process_yv12, pdp_mapper_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_mapper_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_mapper_process */ + break; + + } + } +} + +static void pdp_mapper_input_0(t_pdp_mapper *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + /* add the process method and callback to the process queue */ + pdp_mapper_process(x); + } +} + +static void pdp_mapper_free(t_pdp_mapper *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + + if ( x->x_pixelmap ) freebytes( x->x_pixelmap, x->x_vsize*sizeof(t_int) ); + +} + +t_class *pdp_mapper_class; + +void *pdp_mapper_new(void) +{ + int i; + + t_pdp_mapper *x = (t_pdp_mapper *)pd_new(pdp_mapper_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + x->x_vsize = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mapper_setup(void) +{ +// post( pdp_mapper_version ); + pdp_mapper_class = class_new(gensym("pdp_mapper"), (t_newmethod)pdp_mapper_new, + (t_method)pdp_mapper_free, sizeof(t_pdp_mapper), 0, A_NULL); + + class_addmethod(pdp_mapper_class, (t_method)pdp_mapper_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mapper_class, (t_method)pdp_mapper_copy, gensym("copy"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mapper_class, (t_method)pdp_mapper_swap, gensym("swap"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mapper_class, (t_method)pdp_mapper_reset, gensym("reset"), A_NULL); + class_sethelpsymbol( pdp_mapper_class, gensym("pdp_mapper.pd") ); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_theonice~.c b/modules/pdp_theonice~.c new file mode 100644 index 0000000..42552bf --- /dev/null +++ b/modules/pdp_theonice~.c @@ -0,0 +1,1333 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a theora a/v streaming object to an icecast server + * The patched icecast server can be found here : http://mediacast1.com/~karl/ + * Written by Yves Degoyon ( ydegoyon@free.fr ) + * + */ + + +#include "pdp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIN_FRAMERATE 1 +#define MAX_FRAMERATE 100 +#define DEFAULT_FRAME_RATE 7 +#define MIN_VIDEO_QUALITY 0 +#define MAX_VIDEO_QUALITY 63 +#define DEFAULT_VIDEO_QUALITY 2 +#define MIN_VIDEO_BITRATE 45 +#define MAX_VIDEO_BITRATE 2000 +#define DEFAULT_VIDEO_BITRATE 48 +#define MIN_AUDIO_QUALITY -0.1 +#define MAX_AUDIO_QUALITY 1.0 +#define DEFAULT_AUDIO_QUALITY 0.5 +#define MIN_AUDIO_BITRATE 8 +#define MAX_AUDIO_BITRATE 2000 +#define DEFAULT_AUDIO_BITRATE 32 + +#define DEFAULT_CHANNELS 2 +#define DEFAULT_DRIFT 100 +#define DEFAULT_BITS 8 +#define MAX_AUDIO_PACKET_SIZE (128 * 1024) +// streams hard-coded serial numbers +#define STREAMV_SNO 0x987654 +#define STREAMA_SNO 0x456789 + +#define MAX_COMMENT_LENGTH 1024 +#define STRBUF_SIZE 32 +#define OGG_AUDIO_SIZE 1024 + +static char base64table[65] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/', +}; + +static char *pdp_theonice_version = "pdp_theonice~: version 0.1, a theora a/v streaming object, written by ydegoyon@free.fr"; + +typedef struct pdp_theonice_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth; + t_int x_tvwidth; // theora 16 pixels aligned width value + t_int x_vheight; + t_int x_tvheight; // theora 16 pixels aligned height value + t_int x_vsize; + + pthread_t x_connectchild; // thread used for connecting to a stream + int x_socketfd; // connection socket + t_int x_streaming; // streaming on : connected and all + t_int x_pstreaming; // previous state + char *x_passwd; // password + char x_title[MAX_COMMENT_LENGTH]; // title of the stream + char x_url[MAX_COMMENT_LENGTH]; // url of the stream + char x_genre[MAX_COMMENT_LENGTH]; // genre of the stream + char x_description[MAX_COMMENT_LENGTH]; // description + char x_artist[MAX_COMMENT_LENGTH]; // artist + char x_copyright[MAX_COMMENT_LENGTH]; + char x_date[MAX_COMMENT_LENGTH]; // starting system date + char x_hostname[MAX_COMMENT_LENGTH]; // name or IP of host to connect to + char x_mountpoint[MAX_COMMENT_LENGTH]; // mountpoint + t_int x_port; // port number + t_int x_public; // publish on www.oggcast.com + t_int x_framerate; + t_int x_mframerate; // measured framerate + t_int x_pmframerate; // previous state + t_int x_einit; + t_int x_frameswritten; + t_int x_pframeswritten; + t_int x_frameslate; + t_int x_nbframes_dropped; + t_int x_pnbframes_dropped; + t_int x_frames; + t_float x_maxdrift; /* maximum delay between audio and video */ + struct timeval x_tstart; + struct timeval x_tzero; + struct timeval x_tcurrent; + struct timeval x_tprevstream; + t_int x_cursec; // current second + t_int x_secondcount; // number of frames emitted in the current second + + /* vorbis/theora structures */ + ogg_page x_ogg_page; // ogg page for headers + ogg_page x_apage; // ogg audio page + ogg_page x_vpage; // ogg video page + ogg_packet x_ogg_packet; // ogg packet + ogg_stream_state x_statev; // vorbis stream state + ogg_stream_state x_statet; // theora stream state + theora_info x_theora_info; // theora info + theora_comment x_theora_comment; // theora comment + theora_state x_theora_state; // theora state + vorbis_info x_vorbis_info; // vorbis info + vorbis_dsp_state x_dsp_state; // vorbis dsp state + vorbis_block x_vorbis_block; // vorbis block + vorbis_comment x_vorbis_comment; // vorbis comment + yuv_buffer x_yuvbuffer; // yuv buffer + t_int x_eos; // end of stream + + t_int x_akbps; // audio bit rate + t_int x_vkbps; // video bit rate + t_float x_aquality; // audio quality + t_int x_vquality; // video quality + t_int x_abytesout; // audio bytes written + t_int x_vbytesout; // video bytes written + double x_audiotime; // audio stream time + double x_paudiotime; // previous value + double x_videotime; // video stream time + double x_pvideotime; // previous value + + /* audio structures */ + t_float x_audio_buf[DEFAULT_CHANNELS][MAX_AUDIO_PACKET_SIZE]; // buffer for incoming audio + t_int x_audioin_position; // writing position for incoming audio + t_int x_channels; // audio channels + t_int x_samplerate; // audio sample rate + t_int x_bits; // audio bits + + t_outlet *x_outlet_streaming; // indicates the action of streaming + t_outlet *x_outlet_nbframes; // number of frames emitted + t_outlet *x_outlet_framerate; // real framerate + t_outlet *x_outlet_nbframes_dropped; // number of frames dropped + t_outlet *x_outlet_atime; // audio time + t_outlet *x_outlet_vtime; // video time + t_float **x_vbuffer; // buffer from vorbis + +} t_pdp_theonice; + + /* allocate internal ressources */ +static void pdp_theonice_allocate(t_pdp_theonice *x) +{ + int ret; + + x->x_yuvbuffer.y_width=x->x_vwidth; + x->x_yuvbuffer.y_height=x->x_vheight; + x->x_yuvbuffer.y_stride=x->x_vwidth; + + x->x_yuvbuffer.uv_width=x->x_vwidth>>1; + x->x_yuvbuffer.uv_height=x->x_vheight>>1; + x->x_yuvbuffer.uv_stride=x->x_vwidth>>1; + + x->x_yuvbuffer.y = (char *)malloc( x->x_yuvbuffer.y_width * x->x_yuvbuffer.y_height ); + x->x_yuvbuffer.u = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height ); + x->x_yuvbuffer.v = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height ); +} + + /* free internal ressources */ +static void pdp_theonice_free_ressources(t_pdp_theonice *x) +{ + if ( x->x_yuvbuffer.y ) free( x->x_yuvbuffer.y ); + if ( x->x_yuvbuffer.u ) free( x->x_yuvbuffer.u ); + if ( x->x_yuvbuffer.v ) free( x->x_yuvbuffer.v ); +} + + /* initialize the encoder */ +static void pdp_theonice_init_encoder(t_pdp_theonice *x) +{ + t_int ret; + + x->x_einit=0; + + // init streams + ogg_stream_init(&x->x_statet, STREAMA_SNO); + ogg_stream_init(&x->x_statev, STREAMV_SNO); + + theora_info_init(&x->x_theora_info); + x->x_theora_info.width=x->x_tvwidth; + x->x_theora_info.height=x->x_tvheight; + x->x_theora_info.frame_width=x->x_vwidth; + x->x_theora_info.frame_height=x->x_vheight; + x->x_theora_info.offset_x=(x->x_tvwidth-x->x_vwidth)>>1; + x->x_theora_info.offset_y=(x->x_tvheight-x->x_vheight)>>1; + x->x_theora_info.fps_numerator=x->x_framerate; + x->x_theora_info.fps_denominator=1; + x->x_theora_info.aspect_numerator=1; + x->x_theora_info.aspect_denominator=1; + x->x_theora_info.colorspace=OC_CS_UNSPECIFIED; + x->x_theora_info.target_bitrate=x->x_vkbps; + x->x_theora_info.quality=x->x_vquality; + + x->x_theora_info.dropframes_p=0; + x->x_theora_info.quick_p=1; + x->x_theora_info.keyframe_auto_p=1; + x->x_theora_info.keyframe_frequency=64; + x->x_theora_info.keyframe_frequency_force=64; + x->x_theora_info.keyframe_data_target_bitrate=x->x_vkbps*1.5; + x->x_theora_info.keyframe_auto_threshold=80; + x->x_theora_info.keyframe_mindistance=8; + x->x_theora_info.noise_sensitivity=1; + + theora_encode_init(&x->x_theora_state,&x->x_theora_info); + + vorbis_info_init(&x->x_vorbis_info); + + if(x->x_aquality > -0.1) + { + ret = vorbis_encode_init_vbr(&x->x_vorbis_info, x->x_channels, x->x_samplerate, x->x_aquality); + } + else + { + ret = vorbis_encode_init(&x->x_vorbis_info, x->x_channels, x->x_samplerate, -1, x->x_akbps, -1); + } + + if (ret) + { + post( "pdp_theonice~ : could not initialize vorbis encoder" ); + x->x_einit=0; + return; + } + + vorbis_comment_init(&x->x_vorbis_comment); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"TITLE", x->x_title); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"ARTIST", x->x_artist); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"GENRE",x->x_genre); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"DESCRIPTION", x->x_description); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"LOCATION",x->x_url); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"PERFORMER",x->x_artist); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"COPYRIGHT",x->x_copyright); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"DATE",x->x_date); + vorbis_comment_add_tag(&(x->x_vorbis_comment),"ENCODER","pdp_theonice~ v0.1"); + vorbis_analysis_init(&x->x_dsp_state,&x->x_vorbis_info); + vorbis_block_init(&x->x_dsp_state,&x->x_vorbis_block); + + post( "pdp_theonice~ : encoder initialized." ); + x->x_einit=1; + +} + + /* terminate the encoding process */ +static void pdp_theonice_shutdown_encoder(t_pdp_theonice *x) +{ + t_int ret; + + if ( x->x_streaming ) + { + post( "pdp_theonice~ : shutting down encoder"); + vorbis_analysis_wrote(&x->x_dsp_state,0); + // get rid of remaining data in encoder, if any + while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block )==1) + { + vorbis_analysis( &x->x_vorbis_block, NULL ); + vorbis_bitrate_addblock( &x->x_vorbis_block ); + + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet )) + { + ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet ); + } + } + while( ( ret = ogg_stream_pageout( &x->x_statev, &x->x_apage) ) > 0 ) + { + x->x_audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&x->x_apage)); + + // post("pdp_theonice~ : writing audio : %d samples, header : %d, body : %d", nbsamples, x->x_apage.header_len, x->x_apage.body_len); + if ( ( ret = send(x->x_socketfd, (void*)x->x_apage.header, x->x_apage.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write audio packet (ret=%d).", ret ); + perror( "send" ); + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_apage.body, x->x_apage.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write audio packet (ret=%d).", ret ); + perror( "send" ); + } + } + + ogg_stream_clear(&x->x_statev); + vorbis_block_clear(&x->x_vorbis_block); + vorbis_dsp_clear(&x->x_dsp_state); + vorbis_comment_clear(&x->x_vorbis_comment); + vorbis_info_clear(&x->x_vorbis_info); + ogg_stream_clear(&x->x_statet); + theora_clear(&x->x_theora_state); + } +} + + /* disconnect from an icecast server */ +static void pdp_theonice_disconnect(t_pdp_theonice *x) +{ + int ret; + + pdp_theonice_shutdown_encoder( x ); + x->x_streaming = 0; + + if ( x->x_socketfd > 0 ) + { + if ( close( x->x_socketfd ) < 0 ) + { + post( "pdp_theonice~ : could not disconnect" ); + perror( "close" ); + } + x->x_socketfd = -1; + } +} + +static int pdp_theonice_write_headers(t_pdp_theonice *x) +{ + t_int ret; + ogg_packet aheader, aheadercomm, aheadercode; + + if ( !x->x_einit ) + { + post( "pdp_theonice~ : trying to write headers but encoder is not initialized." ); + return -1; + } + + theora_encode_header(&x->x_theora_state, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + if(ogg_stream_pageout(&x->x_statet, &x->x_ogg_page)!=1) + { + post( "pdp_theonice~ : ogg encoding error." ); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.header, x->x_ogg_page.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.body, x->x_ogg_page.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + + theora_comment_init(&x->x_theora_comment); + theora_comment_add_tag(&(x->x_theora_comment),"TITLE", x->x_title); + theora_comment_add_tag(&(x->x_theora_comment),"ARTIST", x->x_artist); + theora_comment_add_tag(&(x->x_theora_comment),"GENRE",x->x_genre); + theora_comment_add_tag(&(x->x_theora_comment),"DESCRIPTION", x->x_description); + theora_comment_add_tag(&(x->x_theora_comment),"LOCATION",x->x_url); + theora_comment_add_tag(&(x->x_theora_comment),"PERFORMER",x->x_artist); + theora_comment_add_tag(&(x->x_theora_comment),"COPYRIGHT",x->x_copyright); + theora_comment_add_tag(&(x->x_theora_comment),"DATE",x->x_date); + theora_comment_add_tag(&(x->x_theora_comment),"ENCODER","pdp_theonice~ v0.1"); + theora_encode_comment(&x->x_theora_comment, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + theora_encode_tables(&x->x_theora_state, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + + vorbis_analysis_headerout(&x->x_dsp_state, &x->x_vorbis_comment, + &aheader,&aheadercomm,&aheadercode); + ogg_stream_packetin(&x->x_statev,&aheader); + + if(ogg_stream_pageout(&x->x_statev, &x->x_ogg_page)!=1) + { + post( "pdp_theonice~ : ogg encoding error." ); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.header, x->x_ogg_page.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.body, x->x_ogg_page.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + + // remaining vorbis header packets + ogg_stream_packetin(&x->x_statev, &aheadercomm); + ogg_stream_packetin(&x->x_statev, &aheadercode); + + // flush all the headers + while(1) + { + ret = ogg_stream_flush(&x->x_statet, &x->x_ogg_page); + if(ret<0){ + post( "pdp_theonice~ : ogg encoding error." ); + return -1; + } + if(ret==0)break; + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.header, x->x_ogg_page.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.body, x->x_ogg_page.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + } + + while(1) + { + ret = ogg_stream_flush(&x->x_statev, &x->x_ogg_page); + if(ret<0){ + post( "pdp_theonice~ : ogg encoding error." ); + return -1; + } + if(ret==0)break; + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.header, x->x_ogg_page.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_ogg_page.body, x->x_ogg_page.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write headers (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return -1; + } + } + + return 0; +} + + /* start streaming */ +static int pdp_theonice_start(t_pdp_theonice *x) +{ + time_t start_t; + t_int ret; + + if ( gettimeofday(&x->x_tstart, NULL) == -1) + { + post("pdp_theonice~ : could not set start time" ); + } + + if ( gettimeofday(&x->x_tzero, NULL) == -1) + { + post("pdp_theonice~ : could get initial time" ); + } + + time( &start_t ); + strcpy( x->x_date, ctime( &start_t )); + post("pdp_theonice~ : initializing encoder..."); + pdp_theonice_init_encoder( x ); + post("pdp_theonice~ : writing headers..."); + if ( ( ret = pdp_theonice_write_headers( x ) ) < 0 ) + { + return ret; + } + post("pdp_theonice~ : start streaming at %d frames/second", x->x_framerate); +} + + /* set password */ +static void pdp_theonice_passwd(t_pdp_theonice *x, t_symbol *passwd) +{ + post("pdp_theonice~ : setting password to %s", passwd->s_name ); + x->x_passwd = passwd->s_name; +} + + /* set url */ +static void pdp_theonice_url(t_pdp_theonice *x, t_symbol *url) +{ + post("pdp_theonice~ : setting location to %s", url->s_name ); + strcpy(x->x_url, url->s_name); +} + + /* set title */ +static void pdp_theonice_title(t_pdp_theonice *x, t_symbol *s, int argc, t_atom *argv) +{ + t_int i; + + strcpy( x->x_title, "" ); + for ( i=0; ix_title, "%s %s", x->x_title, argv[i].a_w.w_symbol->s_name ); + } + if (argv[i].a_type == A_FLOAT) + { + sprintf( x->x_title, "%s %d", x->x_title, (t_int)argv[i].a_w.w_float ); + } + } + sprintf( x->x_title, "%s", x->x_title+1 ); + post("pdp_theonice~ : setting title to %s", x->x_title ); +} + + /* set artist */ +static void pdp_theonice_artist(t_pdp_theonice *x, t_symbol *s, int argc, t_atom *argv) +{ + t_int i; + + strcpy( x->x_artist, "" ); + for ( i=0; ix_artist, "%s %s", x->x_artist, argv[i].a_w.w_symbol->s_name ); + } + if (argv[i].a_type == A_FLOAT) + { + sprintf( x->x_artist, "%s %d", x->x_artist, (t_int)argv[i].a_w.w_float ); + } + } + sprintf( x->x_artist, "%s", x->x_artist+1 ); + post("pdp_theonice~ : setting artist to %s", x->x_artist ); +} + + /* set description */ +static void pdp_theonice_description(t_pdp_theonice *x, t_symbol *s, int argc, t_atom *argv) +{ + t_int i; + + strcpy( x->x_description, "" ); + for ( i=0; ix_description, "%s %s", x->x_description, argv[i].a_w.w_symbol->s_name ); + } + if (argv[i].a_type == A_FLOAT) + { + sprintf( x->x_description, "%s %d", x->x_description, (t_int)argv[i].a_w.w_float ); + } + } + sprintf( x->x_description, "%s", x->x_description+1 ); + post("pdp_theonice~ : setting description to %s", x->x_description ); +} + + /* set genre */ +static void pdp_theonice_genre(t_pdp_theonice *x, t_symbol *s, int argc, t_atom *argv) +{ + t_int i; + + strcpy( x->x_genre, "" ); + for ( i=0; ix_genre, "%s %s", x->x_genre, argv[i].a_w.w_symbol->s_name ); + } + if (argv[i].a_type == A_FLOAT) + { + sprintf( x->x_genre, "%s %d", x->x_genre, (t_int)argv[i].a_w.w_float ); + } + } + sprintf( x->x_genre, "%s", x->x_genre+1 ); + post("pdp_theonice~ : setting genre to %s", x->x_genre ); +} + +char *pdp_theonice_base64_encode(char *data) +{ + int len = strlen(data); + char *out = t_getbytes(len*4/3 + 4); + char *result = out; + int chunk; + + while(len > 0) { + chunk = (len >3)?3:len; + *out++ = base64table[(*data & 0xFC)>>2]; + *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)]; + + switch(chunk) { + case 3: + *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)]; + *out++ = base64table[(*(data+2)) & 0x3F]; + break; + case 2: + *out++ = base64table[((*(data+1) & 0x0F)<<2)]; + *out++ = '='; + break; + case 1: + *out++ = '='; + *out++ = '='; + break; + } + data += chunk; + len -= chunk; + } + *out = 0; + + return result; +} + +static void sendsock( t_int sockfd, char *buf, size_t buflen ) +{ + if ( send( sockfd, buf, buflen, MSG_NOSIGNAL ) != (t_int)buflen ) + { + post( "pdp_theonice~ : could not send message to the server" ); + post( "pdp_theonice~ : message : %s", buf ); + } +} + + /* connect to an icecast server */ +static void *pdp_theonice_do_connect(void *tdata) +{ + char buf[MAX_COMMENT_LENGTH]; /* buffer to hold commands sent to the server */ + char *base64; /* buffer to hold 64bit encoded strings */ + char resp[STRBUF_SIZE]; + unsigned int len; + fd_set fdset; + struct timeval tv; + t_int sockfd, ret; + struct sockaddr_in sinfo; + struct hostent *hp; + t_pdp_theonice *x; + + x = (t_pdp_theonice *)tdata; + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("pdp_theonice~: internal error while attempting to open socket"); + return NULL; + } + + /* connect socket using hostname provided in command line */ + sinfo.sin_family = AF_INET; + hp = gethostbyname(x->x_hostname); + if (hp == 0) + { + post("pdp_theonice~: wrong server name or IP."); + close(sockfd); + return NULL; + } + memcpy((char *)&sinfo.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + sinfo.sin_port = htons((unsigned short)x->x_port); + + /* try to connect. */ + post("pdp_theonice~: connecting to port %d", x->x_port); + if (connect(sockfd, (struct sockaddr *) &sinfo, sizeof (sinfo)) < 0) + { + error("pdp_theonice~: connection failed!\n"); + close(sockfd); + return NULL; + } + + /* check if we can read/write from/to the socket */ + FD_ZERO( &fdset); + FD_SET( sockfd, &fdset); + tv.tv_sec = 0; /* seconds */ + tv.tv_usec = 500; /* microseconds */ + + ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); + if(ret < 0) + { + error("pdp_theonice~: can not read from socket."); + close(sockfd); + return NULL; + } + ret = select(sockfd + 1, NULL, &fdset, NULL, &tv); + if(ret < 0) + { + error("pdp_theonice~: can not write to socket."); + close(sockfd); + return NULL; + } + + /* send the request, a string like: "SOURCE / HTTP/1.0\r\n" */ + sprintf(buf, "SOURCE /%s", x->x_mountpoint); + sendsock(sockfd, buf, strlen(buf)); + strcpy( buf, " HTTP/1.0\r\n"); + sendsock(sockfd, buf, strlen(buf)); + /* send basic authorization as base64 encoded string */ + sprintf(resp, "source:%s", x->x_passwd); + len = strlen(resp); + base64 = pdp_theonice_base64_encode(resp); + sprintf(resp, "Authorization: Basic %s\r\n", base64); + sendsock(sockfd, resp, strlen(resp)); + t_freebytes(base64, len*4/3 + 4); + /* send application name */ + strcpy( buf, "User-Agent: pdp_theonice~"); + sendsock(sockfd, buf, strlen(buf)); + /* send content type: vorbis */ + strcpy( buf, "\r\nContent-Type: application/ogg"); + sendsock(sockfd, buf, strlen(buf)); + /* send the ice headers */ + /* name */ + sprintf( buf, "\r\nice-name: %s", x->x_title ); + sendsock(sockfd, buf, strlen(buf)); + /* url */ + sprintf( buf, "\r\nice-url: %s", x->x_url ); + sendsock(sockfd, buf, strlen(buf)); + /* genre */ + sprintf( buf, "\r\nice-genre: %s", x->x_genre ); + sendsock(sockfd, buf, strlen(buf)); + /* public */ + strcpy( buf, "\r\nice-public: 1" ); + sendsock(sockfd, buf, strlen(buf)); + /* bitrate */ + sprintf(buf, "\r\nice-audio-info: bitrate=%d", x->x_akbps); + sendsock(sockfd, buf, strlen(buf)); + /* description */ + sprintf(buf, "\r\nice-description: %s", x->x_description); + sendsock(sockfd, buf, strlen(buf)); + /* end of header: write an empty line */ + strcpy( buf, "\r\n\r\n"); + sendsock(sockfd, buf, strlen(buf)); + + /* read the anticipated response: "OK" */ + len = recv(sockfd, resp, STRBUF_SIZE, MSG_NOSIGNAL); + if ( strstr( resp, "OK" ) == NULL ) + { + post("pdp_theonice~: login failed!"); + if ( len>0 ) post("pdp_theonice~: server answered : %s", resp); + close(sockfd); + return NULL; + } + post("pdp_theonice~: logged in to %s", x->x_hostname); + + post("pdp_theonice~: connecting child %d exiting....", x->x_connectchild); + x->x_connectchild = 0; + + x->x_socketfd = sockfd; + if ( ( ret = pdp_theonice_start( x ) ) < 0 ) + { + x->x_socketfd = -1; + return NULL; + } + else + { + x->x_streaming = 1; + x->x_frameswritten = 0; + x->x_videotime = 0.; + x->x_audiotime = 0.; + x->x_frameslate = 0; + x->x_nbframes_dropped = 0; + x->x_secondcount = 0; + x->x_frames = 0; + } + + return NULL; + +} + + /* launch a connection thread */ +static void pdp_theonice_connect(t_pdp_theonice *x, t_symbol *shost, t_symbol *smountpoint, t_floatarg fport) +{ + t_int ret=0; + pthread_attr_t connect_child_attr; + + // check parameters + if ( gethostbyname( shost->s_name ) == NULL ) + { + post("pdp_theonice~ : it looks like your server is unknown here.." ); + return; + } + else + { + strcpy( x->x_hostname, shost->s_name); + } + + if ( ( (t_int)fport < 0 ) || ( (t_int)fport > 65535 ) ) + { + post("pdp_theonice~ : wrong port number." ); + return; + } + else + { + x->x_port = (t_int)fport; + } + + strcpy( x->x_mountpoint, smountpoint->s_name ); + sprintf( x->x_url, "http://%s:%d/%s", x->x_hostname, x->x_port, x->x_mountpoint ); + post("pdp_theonice~ : connecting to %s", x->x_url ); + + if ( ( x->x_streaming ) || ( x->x_connectchild != 0 ) ) + { + post("pdp_theonice~ : connection request but a connection is pending ... disconnecting" ); + pdp_theonice_disconnect(x); + } + + // launch connection thread + if ( pthread_attr_init( &connect_child_attr ) < 0 ) { + post( "pdp_theonice~ : could not launch connection thread" ); + perror( "pthread_attr_init" ); + return; + } + if ( pthread_attr_setdetachstate( &connect_child_attr, PTHREAD_CREATE_DETACHED ) < 0 ) { + post( "pdp_theonice~ : could not launch connection thread" ); + perror( "pthread_attr_setdetachstate" ); + return; + } + if ( pthread_create( &x->x_connectchild, &connect_child_attr, pdp_theonice_do_connect, x ) < 0 ) { + post( "pdp_theonice~ : could not launch connection thread" ); + perror( "pthread_create" ); + return; + } + else + { + post( "pdp_theonice~ : connection thread %d launched", x->x_connectchild ); + } + + return; + +} + + /* set video bitrate */ +static void pdp_theonice_vbitrate(t_pdp_theonice *x, t_floatarg vbitrate ) +{ + if ( ( (t_int) vbitrate < MIN_VIDEO_BITRATE ) || ( (t_int) vbitrate > MAX_VIDEO_BITRATE ) ) + { + post( "pdp_theonice~ : wrong video bitrate %d : should be in [%d,%d] kbps", + (t_int) vbitrate, MIN_VIDEO_BITRATE, MAX_VIDEO_BITRATE ); + return; + } + x->x_vkbps = (t_int) vbitrate; +} + + /* set audio bitrate */ +static void pdp_theonice_abitrate(t_pdp_theonice *x, t_floatarg abitrate ) +{ + if ( ( (t_int) abitrate < MIN_AUDIO_BITRATE ) || ( (t_int) abitrate > MAX_AUDIO_BITRATE ) ) + { + post( "pdp_theonice~ : wrong audio bitrate %d : should be in [%d,%d] kbps", + (t_int) abitrate, MIN_AUDIO_BITRATE, MAX_AUDIO_BITRATE ); + return; + } + x->x_akbps = (t_int) abitrate; +} + + /* set video quality */ +static void pdp_theonice_vquality(t_pdp_theonice *x, t_floatarg vquality ) +{ + if ( ( (t_int) vquality < MIN_VIDEO_QUALITY ) || ( (t_int) vquality > MAX_VIDEO_QUALITY ) ) + { + post( "pdp_theonice~ : wrong video quality %d : should be in [%d,%d]", + (t_int) vquality, MIN_VIDEO_QUALITY, MAX_VIDEO_QUALITY ); + return; + } + x->x_vquality = (t_int) vquality; +} + + /* set audio quality */ +static void pdp_theonice_aquality(t_pdp_theonice *x, t_floatarg aquality ) +{ + if ( ( (t_int) aquality < MIN_AUDIO_QUALITY ) || ( (t_int) aquality > MAX_AUDIO_QUALITY ) ) + { + post( "pdp_theonice~ : wrong audio quality %d : should be in [%d,%d]", + (t_int) aquality, MIN_AUDIO_QUALITY, MAX_AUDIO_QUALITY ); + return; + } + x->x_aquality = (t_int) aquality; +} + + /* set framerate */ +static void pdp_theonice_framerate(t_pdp_theonice *x, t_floatarg fframerate ) +{ + if ( ( (t_int) fframerate < MIN_FRAMERATE ) || ( (t_int) fframerate > MAX_FRAMERATE ) ) + { + post( "pdp_theonice~ : wrong framerate %d : should be in [%d,%d]", + (t_int) fframerate, MIN_FRAMERATE, MAX_FRAMERATE ); + return; + } + x->x_framerate = (t_int) fframerate; +} + + /* store audio data in PCM format in a buffer for now */ +static t_int *pdp_theonice_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); // left audio inlet + t_float *in2 = (t_float *)(w[2]); // right audio inlet + t_pdp_theonice *x = (t_pdp_theonice *)(w[3]); + int n = (int)(w[4]); // number of samples + t_float fsample; + t_int isample, i; + + if ( x->x_streaming ) + { + // just fills the buffer + while (n--) + { + fsample=*(in1++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + x->x_audio_buf[0][x->x_audioin_position]=fsample; + fsample=*(in2++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + x->x_audio_buf[1][x->x_audioin_position]=fsample; + x->x_audioin_position=(x->x_audioin_position+1)%(MAX_AUDIO_PACKET_SIZE); + if ( x->x_audioin_position >= MAX_AUDIO_PACKET_SIZE-1 ) + { + post( "pdp_theonice~ : reaching end of audio buffer" ); + } + } + } + + if ( x->x_streaming != x->x_pstreaming ) + { + x->x_pstreaming = x->x_streaming; + outlet_float(x->x_outlet_streaming, x->x_streaming); + } + if ( x->x_frameswritten != x->x_pframeswritten ) + { + x->x_pframeswritten = x->x_frameswritten; + outlet_float(x->x_outlet_nbframes, x->x_frameswritten); + } + if ( x->x_nbframes_dropped != x->x_pnbframes_dropped ) + { + x->x_pnbframes_dropped = x->x_nbframes_dropped; + outlet_float(x->x_outlet_nbframes_dropped, x->x_nbframes_dropped); + } + if ( x->x_mframerate != x->x_pmframerate ) + { + x->x_pmframerate = x->x_mframerate; + outlet_float(x->x_outlet_framerate, x->x_mframerate); + } + if ( x->x_audiotime != x->x_paudiotime ) + { + x->x_paudiotime = x->x_audiotime; + if ( x->x_audiotime >= 0. ) outlet_float(x->x_outlet_atime, x->x_audiotime); + } + if ( x->x_videotime != x->x_pvideotime ) + { + x->x_pvideotime = x->x_videotime; + if ( x->x_videotime >= 0. ) outlet_float(x->x_outlet_vtime, x->x_videotime); + } + + return (w+5); +} + +static void pdp_theonice_dsp(t_pdp_theonice *x, t_signal **sp) +{ + dsp_add(pdp_theonice_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_theonice_process_yv12(t_pdp_theonice *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + unsigned char *data = (unsigned char *)pdp_packet_data(x->x_packet0); + t_int i, ret; + t_int px, py; + char *pY, *pU, *pV; + struct timeval tstream; + struct timeval etime; + t_int nbaudiosamples, nbusecs, nbsamples; + t_float fframerate=0.0; + t_int ttime, atime; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) ) + { + post( "pdp_theonice~: reallocating ressources" ); + pdp_theonice_free_ressources( x ); + pdp_theonice_shutdown_encoder( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + x->x_tvwidth=((x->x_vwidth + 15) >>4)<<4; + x->x_tvheight=((x->x_vheight + 15) >>4)<<4; + pdp_theonice_allocate( x ); + if ( x->x_tzero.tv_sec != 0 ) + { + pdp_theonice_init_encoder( x ); + pdp_theonice_write_headers( x ); + } + } + + x->x_frames++; + + if ( x->x_frameswritten == 0 ) + { + if ( gettimeofday(&x->x_tprevstream, NULL) == -1) + { + post("pdp_theonice~ : could set start time" ); + } + } + + // check if it's time to emit a frame + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_theonice~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + x->x_mframerate = x->x_secondcount; + if ( x->x_mframerate < x->x_framerate ) + { + x->x_frameslate += x->x_framerate-x->x_mframerate; + } + x->x_secondcount = 0; + } + ttime = ( ( x->x_frameswritten ) % x->x_framerate ) * ( 1000 / x->x_framerate ); + atime = ( etime.tv_usec / 1000 ); + // post("pdp_theonice~ : actual : %d, theoretical : %d", atime, ttime ); + if ( ( atime < ttime ) && ( x->x_frameslate <= 0 ) ) + { + x->x_nbframes_dropped++; + return; + } + + pY = x->x_yuvbuffer.y; + memcpy( (void*)pY, (void*)&data[0], x->x_vsize ); + pV = x->x_yuvbuffer.v; + memcpy( (void*)pV, (void*)&data[x->x_vsize], (x->x_vsize>>2) ); + pU = x->x_yuvbuffer.u; + memcpy( (void*)pU, (void*)&data[x->x_vsize+(x->x_vsize>>2)], (x->x_vsize>>2) ); + + if ( x->x_socketfd > 0 && x->x_streaming ) + { + // calculate the number of audio samples to output + if ( gettimeofday(&tstream, NULL) == -1) + { + post("pdp_theonice~ : could set stop time" ); + } + // calculate time diff in micro seconds + nbusecs = ( tstream.tv_usec - x->x_tprevstream.tv_usec ) + + ( tstream.tv_sec - x->x_tprevstream.tv_sec )*1000000; + nbaudiosamples = (sys_getsr()*1000000)/nbusecs; + memcpy( &x->x_tprevstream, &tstream, sizeof( struct timeval) ); + + if ( x->x_audioin_position > nbaudiosamples ) + { + nbsamples = nbaudiosamples; + } + else + { + nbsamples = x->x_audioin_position; + } + // if ( x->x_audiotime > x->x_videotime ) nbsamples = -1; + + if ( ( ret = theora_encode_YUVin( &x->x_theora_state, &x->x_yuvbuffer ) ) != 0 ) + { + post( "pdp_theonice~ : could not encode yuv image (ret=%d).", ret ); + } + else + { + // stream one packet + theora_encode_packetout(&x->x_theora_state, 0, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + // post( "pdp_theonice~ : new (theora) ogg packet : bytes:%ld, bos:%ld, eos:%ld, no:%lld", + // x->x_ogg_packet.bytes, x->x_ogg_packet.b_o_s, + // x->x_ogg_packet.e_o_s, x->x_ogg_packet.packetno ); + + while( ( ret = ogg_stream_pageout(&x->x_statet, &x->x_vpage) ) >0 ) + { + x->x_videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&x->x_vpage)); + // post("pdp_theonice~ : writing video : header : %d, body : %d", x->x_vpage.header_len, x->x_vpage.body_len); + if ( ( ret = send(x->x_socketfd, (void*)x->x_vpage.header, x->x_vpage.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write video packet (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_vpage.body, x->x_vpage.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write video packet (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return; + } + } + x->x_frameswritten++; + if ( x->x_frameslate > 0 ) x->x_frameslate--; + x->x_secondcount++; + } + + // audio is ahead, don't send no audio + if ( nbsamples < 0 ) return; + + x->x_vbuffer=vorbis_analysis_buffer( &x->x_dsp_state, nbsamples ); + if ( !x->x_vbuffer ) return; + + memcpy( (void*)&x->x_vbuffer[0][0], (void*)&x->x_audio_buf[0][0], nbsamples*sizeof( t_float ) ); + memcpy( (void*)&x->x_vbuffer[1][0], (void*)&x->x_audio_buf[1][0], nbsamples*sizeof( t_float ) ); + + vorbis_analysis_wrote( &x->x_dsp_state, nbsamples); + + while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block )==1) + { + + // analysis, assume we want to use bitrate management + vorbis_analysis( &x->x_vorbis_block, NULL); + vorbis_bitrate_addblock( &x->x_vorbis_block ); + + // weld packets into the bitstream + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet )) + { + ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet); + } + + } + + x->x_eos=0; + while( !x->x_eos ) + { + ret = ogg_stream_pageout( &x->x_statev, &x->x_apage); + if ( ret == 0 ) break; + x->x_audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&x->x_apage)); + + // post("pdp_theonice~ : writing audio : %d samples, header : %d, body : %d", nbsamples, x->x_apage.header_len, x->x_apage.body_len); + if ( ( ret = send(x->x_socketfd, (void*)x->x_apage.header, x->x_apage.header_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write audio packet (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return; + } + if ( ( ret = send(x->x_socketfd, (void*)x->x_apage.body, x->x_apage.body_len, MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_theonice~ : could not write audio packet (ret=%d).", ret ); + perror( "send" ); + pdp_theonice_disconnect(x); + return; + } + if (ogg_page_eos(&x->x_apage)) x->x_eos=1; + } + + memcpy( &x->x_audio_buf[0][0], &x->x_audio_buf[0][nbsamples], + ( x->x_audioin_position-nbsamples ) * sizeof( t_float ) ); + memcpy( &x->x_audio_buf[1][0], &x->x_audio_buf[1][nbsamples], + ( x->x_audioin_position-nbsamples ) * sizeof( t_float ) ); + x->x_audioin_position -= nbsamples; + + } + + return; +} + +static void pdp_theonice_killpacket(t_pdp_theonice *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; +} + +static void pdp_theonice_process(t_pdp_theonice *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_BITMAP == header->type)){ + + /* pdp_theonice_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_BITMAP_YV12: + pdp_queue_add(x, pdp_theonice_process_yv12, pdp_theonice_killpacket, &x->x_queue_id); + break; + + default: + /* don't know the type, so dont pdp_theonice_process */ + break; + + } + } +} + +static void pdp_theonice_input_0(t_pdp_theonice *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + /* add the process method and callback to the process queue */ + pdp_theonice_process(x); + } + +} + +static void pdp_theonice_free(t_pdp_theonice *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + // close video file if existing + pdp_theonice_disconnect(x); + +} + +t_class *pdp_theonice_class; + +void *pdp_theonice_new(void) +{ + t_int i; + + t_pdp_theonice *x = (t_pdp_theonice *)pd_new(pdp_theonice_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + x->x_outlet_streaming = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes_dropped = outlet_new(&x->x_obj, &s_float); + x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float); + x->x_outlet_atime = outlet_new(&x->x_obj, &s_float); + x->x_outlet_vtime = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + x->x_yuvbuffer.y = NULL; + x->x_yuvbuffer.u = NULL; + x->x_yuvbuffer.v = NULL; + + /* audio defaults */ + x->x_samplerate = sys_getsr(); + x->x_channels = DEFAULT_CHANNELS; + x->x_bits = DEFAULT_BITS; + + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_vkbps = DEFAULT_VIDEO_BITRATE; + x->x_vquality = DEFAULT_VIDEO_QUALITY; + x->x_akbps = DEFAULT_AUDIO_BITRATE; + x->x_aquality = DEFAULT_AUDIO_QUALITY; + x->x_maxdrift = DEFAULT_DRIFT/1000; + + x->x_socketfd = -1; + x->x_passwd = "letmein"; + strcpy( x->x_title, "The Aesthetics Of Our Anger" ); + strcpy(x->x_url, "http://www.indymedia.org"); + strcpy( x->x_genre, "angrrry"); + strcpy( x->x_description, "Images From Infowar"); + strcpy( x->x_artist, "Recuerdos De Luchas"); + strcpy( x->x_copyright, "Creative Commons"); + x->x_public = 1; + strcpy( x->x_mountpoint, "theora.ogg"); + strcpy( x->x_hostname, "localhost"); + x->x_port = 8000; + + x->x_frames = 0; + x->x_eos = 0; + x->x_frameswritten = 0; + x->x_pframeswritten = 0; + x->x_frameslate = 0; + x->x_mframerate = 0; + x->x_pmframerate = 0; + x->x_nbframes_dropped = 0; + x->x_pnbframes_dropped = 0; + + x->x_tzero.tv_sec = 0; + x->x_tzero.tv_usec = 0; + + x->x_audiotime = -1.; + x->x_paudiotime = -1.; + x->x_videotime = -1.; + x->x_pvideotime = -1.; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_theonice_tilde_setup(void) +{ + // post( pdp_theonice_version ); + pdp_theonice_class = class_new(gensym("pdp_theonice~"), (t_newmethod)pdp_theonice_new, + (t_method)pdp_theonice_free, sizeof(t_pdp_theonice), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_theonice_class, t_pdp_theonice, x_f ); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_dsp, gensym("dsp"), 0); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_connect, gensym("connect"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_disconnect, gensym("disconnect"), A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_abitrate, gensym("audiobitrate"), A_FLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_vbitrate, gensym("videobitrate"), A_FLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_aquality, gensym("audioquality"), A_FLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_vquality, gensym("videoquality"), A_FLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_framerate, gensym("framerate"), A_FLOAT, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_passwd, gensym("passwd"), A_SYMBOL, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_title, gensym("title"), A_GIMME, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_artist, gensym("artist"), A_GIMME, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_url, gensym("url"), A_SYMBOL, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_description, gensym("description"), A_GIMME, A_NULL); + class_addmethod(pdp_theonice_class, (t_method)pdp_theonice_genre, gensym("genre"), A_GIMME, A_NULL); + class_sethelpsymbol( pdp_theonice_class, gensym("pdp_theonice~.pd") ); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_theorin~.c b/modules/pdp_theorin~.c index 2a7c72b..b52aa33 100644 --- a/modules/pdp_theorin~.c +++ b/modules/pdp_theorin~.c @@ -42,7 +42,7 @@ #define VIDEO_BUFFER_SIZE (1024*1024) #define MAX_AUDIO_PACKET_SIZE (64 * 1024) -#define MIN_AUDIO_SIZE (64*1024) +#define MIN_AUDIO_SIZE (128*1024) #define DEFAULT_CHANNELS 1 #define DEFAULT_WIDTH 320 @@ -52,6 +52,7 @@ #define MIN_PRIORITY 0 #define DEFAULT_PRIORITY 1 #define MAX_PRIORITY 20 +#define NB_NOFRAMES_HIT 10 #define THEORA_NUM_HEADER_PACKETS 3 @@ -80,6 +81,8 @@ typedef struct pdp_theorin_struct t_outlet *x_outlet_filesize; // for informing of the file size pthread_t x_decodechild; // file decoding thread + pthread_mutex_t x_audiolock; // audio mutex + pthread_mutex_t x_videolock; // video mutex t_int x_usethread; // flag to activate decoding in a thread t_int x_autoplay; // flag to autoplay the file ( default = true ) t_int x_nextimage; // flag to play next image in manual mode @@ -90,9 +93,9 @@ typedef struct pdp_theorin_struct t_int x_decoding; // decoding flag t_int x_theorainit; // flag for indicating that theora is initialized t_int x_videoready; // video ready flag + t_int x_noframeshits; // number of tries without getting a frame t_int x_newpicture; // new picture flag t_int x_newpictureready;// new picture ready flag - t_int x_loop; // looping flag ( default = on ) t_int x_notpackets; // number of theora packets decoded t_int x_novpackets; // number of vorbis packets decoded t_int x_endoffile; // end of the file reached @@ -124,9 +127,10 @@ typedef struct pdp_theorin_struct /* audio structures */ t_int x_audio; // flag to activate the decoding of audio - t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ - t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ - t_int x_audioin_position; // writing position for incoming audio + t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; // left buffer for pd + t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; // right buffer for pd + t_int x_audioin_position;// writing position for incoming audio + t_float **x_pcm; // buffer for vorbis decoding } t_pdp_theorin; @@ -162,14 +166,6 @@ static void pdp_theorin_autoplay(t_pdp_theorin *x, t_floatarg fautoplay ) } } -static void pdp_theorin_loop(t_pdp_theorin *x, t_floatarg floop ) -{ - if ( ( floop == 0. ) || ( floop == 1. ) ) - { - x->x_loop = (int)floop; - } -} - static void pdp_theorin_bang(t_pdp_theorin *x) { if ( x->x_nextimage == 1 ) @@ -201,40 +197,39 @@ static t_int pdp_theorin_queue_page(t_pdp_theorin *x) static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) { int ret, count, maxsamples, samples, si=0, sj=0; - float **pcm; struct timespec mwait; struct timeval ctime; long long tplaying; long long ttheoretical; unsigned char *pY, *pU, *pV; unsigned char *psY, *psU, *psV; + t_float **lpcm; t_int px, py; // post( "pdp_theorin~ : decode packet" ); if ( !x->x_reading ) return -1; - while ( x->x_novpackets && !x->x_audioon ) + while ( x->x_novpackets ) { /* if there's pending, decoded audio, grab it */ - if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &pcm))>0) + x->x_pcm = NULL; + if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &x->x_pcm))>0) { if (x->x_audio) { + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } maxsamples=(3*MAX_AUDIO_PACKET_SIZE-x->x_audioin_position); samples=(retx_audioin_position + samples*x->x_audiochannels < 3*MAX_AUDIO_PACKET_SIZE ) - { - memcpy( (void*)&x->x_audio_inl[x->x_audioin_position], pcm[0], samples*sizeof(t_float) ); - memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], pcm[1], samples*sizeof(t_float) ); - x->x_audioin_position = ( x->x_audioin_position + samples ) % (4*MAX_AUDIO_PACKET_SIZE); - } - else - { - post( "pdp_theorin~ : audio overflow : packet ignored..."); - x->x_audioin_position = 0; - } + memcpy( (void*)&x->x_audio_inl[x->x_audioin_position], x->x_pcm[0], samples*sizeof(t_float) ); + memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], x->x_pcm[1], samples*sizeof(t_float) ); + x->x_audioin_position = ( x->x_audioin_position + samples ) % (3*MAX_AUDIO_PACKET_SIZE); + if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) ) { x->x_audioon = 1; @@ -243,6 +238,15 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) // tell vorbis how many samples were read // post( "pdp_theorin~ : got %d audio samples (audioin=%d)", samples, x->x_audioin_position ); vorbis_synthesis_read(&x->x_dsp_state, samples); + if((ret=vorbis_synthesis_lapout(&x->x_dsp_state, &x->x_pcm))>0) + { + // post( "pdp_theorin~ : supplemental samples (nb=%d)", ret ); + } + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to audio unlock mutex" ); + perror( "pthread_mutex_unlock" ); + } } else { @@ -276,27 +280,15 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) theora_decode_packetin(&x->x_theora_state, &x->x_ogg_packet); // post( "pdp_theorin~ : got one video frame" ); x->x_videoready=1; + x->x_noframeshits=0; } else { // post( "pdp_theorin~ : no more video frame (frames=%d)", x->x_nbframes ); - if ( x->x_nbframes > 0 ) + x->x_noframeshits++; + if ( x->x_nbframes > 0 && ( x->x_noframeshits > NB_NOFRAMES_HIT ) ) { - if ( !x->x_loop ) - { x->x_endoffile = 1; - } - else - { - // restart a new loop - if ( gettimeofday(&x->x_starttime, NULL) == -1) - { - post("pdp_theorin~ : could not set start time" ); - } - } - x->x_nbframes = 0; - x->x_audioin_position = 0; // reset audio - x->x_audioon = 0; } break; } @@ -304,6 +296,11 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) if ( x->x_videoready ) { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } theora_decode_YUVout(&x->x_theora_state, &x->x_yuvbuffer); // create a new pdp packet from PIX_FMT_YUV420P image format @@ -350,6 +347,11 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) { x->x_newpictureready = 1; } + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } } } @@ -363,7 +365,7 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) tplaying = ( ctime.tv_sec-x->x_starttime.tv_sec )*1000 + ( ctime.tv_usec-x->x_starttime.tv_usec )/1000; ttheoretical = ((x->x_nbframes)*1000 )/x->x_framerate; - // post( "pdp-theorin~ : %d playing since : %lldms ( theory : %lldms )", + // post( "pdp_theorin~ : %d playing since : %lldms ( theory : %lldms )", // x->x_nbframes, tplaying, ttheoretical ); if ( ttheoretical <= tplaying ) @@ -374,19 +376,6 @@ static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) } - // check end of file - if(!x->x_videoready && feof(x->x_infile)) - { - if ( x->x_loop ) - { - if ( fseek( x->x_infile, 0x0, SEEK_SET ) < 0 ) - { - post( "pdp_theorin~ : could not reset file." ); - perror( "fseek" ); - } - } - } - // read more data in if( ( x->x_audioin_position < MIN_AUDIO_SIZE ) || ( !x->x_newpicture && !x->x_newpictureready ) ) { @@ -412,7 +401,7 @@ static void *pdp_decode_file(void *tdata) struct timespec twait; twait.tv_sec = 0; - twait.tv_nsec = 5000000; // 5 ms + twait.tv_nsec = 10000000; // 10 ms schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority; if ( sched_setscheduler(0, SCHED_FIFO, &schedprio) == -1) @@ -449,7 +438,7 @@ static void pdp_theorin_close(t_pdp_theorin *x) struct timespec twait; twait.tv_sec = 0; - twait.tv_nsec = 10000000; // 10 ms + twait.tv_nsec = 100000000; // 100 ms if ( x->x_infile == NULL ) { @@ -460,8 +449,9 @@ static void pdp_theorin_close(t_pdp_theorin *x) if ( x->x_reading ) { x->x_newpicture = 0; + x->x_newpictureready = 0; x->x_reading = 0; - post("pdp_theorin~ : waiting end of decoding..." ); + // post("pdp_theorin~ : waiting end of decoding..." ); while ( x->x_decoding ) nanosleep( &twait, NULL ); if ( fclose( x->x_infile ) < 0 ) @@ -492,7 +482,6 @@ static void pdp_theorin_close(t_pdp_theorin *x) x->x_notpackets = 0; x->x_novpackets = 0; - x->x_endoffile = 0; x->x_nbframes = 0; x->x_decoding = 0; x->x_theorainit = 0; @@ -515,14 +504,15 @@ static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) if ( x->x_infile != NULL ) { - post("pdp_theorin~ : open request but a file is open ... closing" ); + // post("pdp_theorin~ : open request but a file is open ... closing" ); pdp_theorin_close(x); } if ( x->x_filename ) free( x->x_filename ); x->x_filename = (char*) malloc( strlen( s->s_name ) + 1 ); strcpy( x->x_filename, s->s_name ); - post( "pdp_theorin~ : opening file : %s", x->x_filename ); + // post( "pdp_theorin~ : opening file : %s", x->x_filename ); + x->x_audio = 1; if ( ( x->x_infile = fopen(x->x_filename,"r") ) == NULL ) { @@ -635,17 +625,17 @@ static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) } } } - post( "pdp_theorin~ : parsed headers ok." ); + // post( "pdp_theorin~ : parsed headers ok." ); // initialize decoders if( x->x_notpackets ) { theora_decode_init(&x->x_theora_state, &x->x_theora_info); x->x_framerate = (t_int)x->x_theora_info.fps_numerator/x->x_theora_info.fps_denominator; - post("pdp_theorin~ : stream %x is theora %dx%d %d fps video.", - x->x_statet.serialno, - x->x_theora_info.width,x->x_theora_info.height, - x->x_framerate); + // post("pdp_theorin~ : stream %x is theora %dx%d %d fps video.", + // x->x_statet.serialno, + // x->x_theora_info.width,x->x_theora_info.height, + // x->x_framerate); if(x->x_theora_info.width!=x->x_theora_info.frame_width || x->x_theora_info.height!=x->x_theora_info.frame_height) { @@ -663,10 +653,10 @@ static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) /* nothing to report */ break;; case OC_CS_ITU_REC_470M: - post("pdp_theorin~ : encoder specified ITU Rec 470M (NTSC) color."); + // post("pdp_theorin~ : encoder specified ITU Rec 470M (NTSC) color."); break;; case OC_CS_ITU_REC_470BG: - post("pdp_theorin~ : encoder specified ITU Rec 470BG (PAL) color."); + // post("pdp_theorin~ : encoder specified ITU Rec 470BG (PAL) color."); break;; default: post("pdp_theorin~ : warning: encoder specified unknown colorspace (%d).", @@ -690,9 +680,9 @@ static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) vorbis_block_init(&x->x_dsp_state, &x->x_vorbis_block); x->x_audiochannels = x->x_vorbis_info.channels; x->x_samplerate = x->x_vorbis_info.rate; - post("pdp_theorin~ : ogg logical stream %x is vorbis %d channel %d Hz audio.", - x->x_statev.serialno, - x->x_audiochannels, x->x_samplerate); + // post("pdp_theorin~ : ogg logical stream %x is vorbis %d channel %d Hz audio.", + // x->x_statev.serialno, + // x->x_audiochannels, x->x_samplerate); } else { @@ -788,8 +778,13 @@ static t_int *pdp_theorin_perform(t_int *w) x->x_blocksize = n; // just read the buffer - if ( x->x_audioon ) + if ( x->x_audioon && x->x_reading ) { + if ( pthread_mutex_lock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock audio mutex" ); + perror( "pthread_mutex_lock" ); + } sn=0; while (n--) { @@ -816,6 +811,11 @@ static t_int *pdp_theorin_perform(t_int *w) // post( "pdp_theorin~ : audio off ( audioin : %d, channels=%d )", // x->x_audioin_position, x->x_audiochannels ); } + if ( pthread_mutex_unlock( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock audio mutex" ); + perror( "pthread_mutex_unlock" ); + } } else { @@ -827,6 +827,8 @@ static t_int *pdp_theorin_perform(t_int *w) } } + if ( !x->x_reading ) return (w+5); + // check if the framerate has been exceeded if ( gettimeofday(&etime, NULL) == -1) { @@ -842,19 +844,28 @@ static t_int *pdp_theorin_perform(t_int *w) // output image if there's a new one decoded if ( x->x_newpicture ) { + if ( pthread_mutex_lock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to lock video mutex" ); + perror( "pthread_mutex_lock" ); + } pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); x->x_newpicture = 0; // update streaming status x->x_nbframes++; x->x_secondcount++; + // post( "pdp_theorin~ : frame #%d", x->x_nbframes ); outlet_float( x->x_outlet_nbframes, x->x_nbframes ); - + if ( pthread_mutex_unlock( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to unlock video mutex" ); + perror( "pthread_mutex_unlock" ); + } } if ( x->x_endoffile == 1 ) // only once { outlet_float( x->x_outlet_endoffile, x->x_endoffile ); - x->x_endoffile = 0; } if ( x->x_endoffile == -1 ) // reset { @@ -883,8 +894,19 @@ static void pdp_theorin_free(t_pdp_theorin *x) { pdp_theorin_close(x); } + + if ( pthread_mutex_destroy( &x->x_audiolock ) < 0 ) + { + post( "pdp_theorin~ : unable to destroy audio mutex" ); + perror( "pthread_mutex_destroy" ); + } + if ( pthread_mutex_destroy( &x->x_videolock ) < 0 ) + { + post( "pdp_theorin~ : unable to destroy video mutex" ); + perror( "pthread_mutex_destroy" ); + } - post( "pdp_theorin~ : freeing object" ); + // post( "pdp_theorin~ : freeing object" ); } t_class *pdp_theorin_class; @@ -909,6 +931,18 @@ void *pdp_theorin_new(void) x->x_packet0 = -1; x->x_decodechild = 0; + if ( pthread_mutex_init( &x->x_audiolock, NULL ) < 0 ) + { + post( "pdp_theorin~ : unable to initialize audio mutex" ); + perror( "pthread_mutex_init" ); + return NULL; + } + if ( pthread_mutex_init( &x->x_videolock, NULL ) < 0 ) + { + post( "pdp_theorin~ : unable to initialize video mutex" ); + perror( "pthread_mutex_init" ); + return NULL; + } x->x_decoding = 0; x->x_theorainit = 0; x->x_usethread = 1; @@ -927,7 +961,6 @@ void *pdp_theorin_new(void) x->x_novpackets = 0; x->x_blocksize = MIN_AUDIO_SIZE; x->x_autoplay = 1; - x->x_loop = 1; x->x_nextimage = 0; x->x_infile = NULL; x->x_reading = 0; @@ -957,7 +990,6 @@ void pdp_theorin_tilde_setup(void) class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_priority, gensym("priority"), A_FLOAT, A_NULL); class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_audio, gensym("audio"), A_FLOAT, A_NULL); class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_autoplay, gensym("autoplay"), A_FLOAT, A_NULL); - class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_loop, gensym("loop"), A_FLOAT, A_NULL); class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_threadify, gensym("thread"), A_FLOAT, A_NULL); class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_bang, gensym("bang"), A_NULL); class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); diff --git a/modules/pdp_theorout~.c b/modules/pdp_theorout~.c index 96d610a..f5c776b 100644 --- a/modules/pdp_theorout~.c +++ b/modules/pdp_theorout~.c @@ -166,8 +166,8 @@ static void pdp_theorout_init_encoder(t_pdp_theorout *x) x->x_theora_info.offset_y=(x->x_tvheight-x->x_vheight)>>1; x->x_theora_info.fps_numerator=x->x_framerate; x->x_theora_info.fps_denominator=1; - x->x_theora_info.aspect_numerator=x->x_vwidth; - x->x_theora_info.aspect_denominator=x->x_vheight; + x->x_theora_info.aspect_numerator=1; + x->x_theora_info.aspect_denominator=1; x->x_theora_info.colorspace=OC_CS_UNSPECIFIED; x->x_theora_info.target_bitrate=x->x_vkbps; x->x_theora_info.quality=x->x_vquality; @@ -181,8 +181,10 @@ static void pdp_theorout_init_encoder(t_pdp_theorout *x) x->x_theora_info.keyframe_auto_threshold=80; x->x_theora_info.keyframe_mindistance=8; x->x_theora_info.noise_sensitivity=1; + x->x_theora_info.sharpness=2; theora_encode_init(&x->x_theora_state,&x->x_theora_info); + theora_info_clear (&x->x_theora_info); vorbis_info_init(&x->x_vorbis_info); @@ -203,6 +205,7 @@ static void pdp_theorout_init_encoder(t_pdp_theorout *x) } vorbis_comment_init(&x->x_vorbis_comment); + vorbis_comment_add_tag (&x->x_vorbis_comment, "ENCODER", "pdp_theorout~"); vorbis_analysis_init(&x->x_dsp_state,&x->x_vorbis_info); vorbis_block_init(&x->x_dsp_state,&x->x_vorbis_block); @@ -249,6 +252,7 @@ static void pdp_theorout_write_headers(t_pdp_theorout *x) } theora_comment_init(&x->x_theora_comment); + theora_comment_add_tag (&x->x_theora_comment, "ENCODER", "pdp_theorout~"); theora_encode_comment(&x->x_theora_comment, &x->x_ogg_packet); ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); theora_encode_tables(&x->x_theora_state, &x->x_ogg_packet); @@ -532,6 +536,10 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) ogg_page vpage; t_float **vbuffer; double videotime, audiotime; + theora_info lti; + theora_comment ltc; + ogg_packet logp, logp2; + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || ( (int)(header->info.image.height) != x->x_vheight ) || @@ -607,17 +615,28 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) else { // stream one packet - theora_encode_packetout(&x->x_theora_state, 0, &x->x_ogg_packet); - ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + theora_encode_packetout(&x->x_theora_state, 0, &logp); + ogg_stream_packetin(&x->x_statet, &logp); // post( "pdp_theorout~ : new (theora) ogg packet : bytes:%ld, bos:%ld, eos:%ld, no:%lld", - // x->x_ogg_packet.bytes, x->x_ogg_packet.b_o_s, - // x->x_ogg_packet.e_o_s, x->x_ogg_packet.packetno ); + // logp.bytes, logp.b_o_s, + // logp.e_o_s, logp.packetno ); + while( ( ret = ogg_stream_pageout(&x->x_statet, &vpage) ) >0 ) { videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage)); - x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile ); - x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile ); + if ( ( ret = fwrite(vpage.header, 1, vpage.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_vbytesout+=ret; + if ( ( ret = fwrite(vpage.body, 1, vpage.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_vbytesout+=ret; } } @@ -655,9 +674,9 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) vorbis_bitrate_addblock( &x->x_vorbis_block ); // weld packets into the bitstream - while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet)) + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &logp2)) { - ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet); + ogg_stream_packetin( &x->x_statev, &logp2); } } @@ -665,8 +684,18 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) while( ogg_stream_pageout( &x->x_statev, &apage) >0 ) { audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage)); - x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile ); - x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile ); + if ( ( ret = fwrite(apage.header, 1, apage.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_abytesout+=ret; + if ( ( ret = fwrite(apage.body, 1, apage.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_abytesout+=ret; } memcpy( &x->x_audio_buf[0][0], &x->x_audio_buf[0][nbrecorded], @@ -693,14 +722,24 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) else { // stream one packet - theora_encode_packetout(&x->x_theora_state, 1, &x->x_ogg_packet); - ogg_stream_packetin( &x->x_statet, &x->x_ogg_packet); + theora_encode_packetout(&x->x_theora_state, 1, &logp); + ogg_stream_packetin( &x->x_statet, &logp); while( ( ret = ogg_stream_pageout( &x->x_statet, &vpage) ) > 0 ) { videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage)); - x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile ); - x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile ); + if ( ( ret = fwrite(vpage.header, 1, vpage.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_vbytesout+=ret; + if ( ( ret = fwrite(vpage.body, 1, vpage.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_vbytesout+=ret; } } @@ -714,17 +753,27 @@ static void pdp_theorout_process_yv12(t_pdp_theorout *x) vorbis_bitrate_addblock( &x->x_vorbis_block); // weld packets into the bitstream - while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet)) + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &logp2)) { - ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet); + ogg_stream_packetin( &x->x_statev, &logp2); } } while( ogg_stream_pageout( &x->x_statev, &apage) >0 ) { audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage)); - x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile ); - x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile ); + if ( ( ret = fwrite(apage.header, 1, apage.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_abytesout+=ret; + if ( ( ret = fwrite(apage.body, 1, apage.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + } + x->x_abytesout+=ret; } post("pdp_theorout~ : stop recording"); -- cgit v1.2.1