aboutsummaryrefslogtreecommitdiff
path: root/modules/pdp_live~.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pdp_live~.c')
-rw-r--r--modules/pdp_live~.c324
1 files changed, 209 insertions, 115 deletions
diff --git a/modules/pdp_live~.c b/modules/pdp_live~.c
index cae6a65..c403a15 100644
--- a/modules/pdp_live~.c
+++ b/modules/pdp_live~.c
@@ -35,6 +35,8 @@
#include <sys/time.h>
#include <sys/resource.h>
#include <avformat.h>
+#include <sys/types.h>
+#include <signal.h>
#define VIDEO_BUFFER_SIZE (1024*1024)
#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
@@ -46,8 +48,8 @@
#define DEFAULT_HEIGHT 240
#define DEFAULT_FRAME_RATE 25
#define END_OF_STREAM 20
-#define MIN_PRIORITY -20
-#define DEFAULT_PRIORITY 0
+#define MIN_PRIORITY 0
+#define DEFAULT_PRIORITY 1
#define MAX_PRIORITY 20
/* a trick to cope with ffmpeg versions */
@@ -66,7 +68,7 @@ typedef struct pdp_live_struct
t_int x_dropped;
t_pdp *x_header;
- short int *x_data;
+ unsigned char *x_data;
t_int x_vwidth;
t_int x_vheight;
t_int x_vsize;
@@ -82,10 +84,14 @@ typedef struct pdp_live_struct
pthread_t x_connectchild; // thread used for connecting to a stream
pthread_t x_decodechild; // stream decoding thread
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
t_int x_priority; // priority of decoding thread
char *x_url;
t_int x_streaming; // streaming flag
+ t_int x_decoding; // decoding flag
+ t_int x_loop; // looping flag ( default = on )
t_int x_nopackets; // no packet to decode
t_int x_endofstream; // end of the stream reached
t_int x_nbframes; // number of frames emitted
@@ -108,6 +114,7 @@ typedef struct pdp_live_struct
AVPicture x_picture_decoded;
long long int x_pts; // presentation time stamp
long long int x_previouspts; // previous presentation time stamp
+ long long int x_firstpts; // first presentation time stamp ( time origin )
t_int x_newpicture;
/* audio structures */
@@ -137,19 +144,80 @@ static void pdp_live_threadify(t_pdp_live *x, t_floatarg fusethread )
static void pdp_live_audio(t_pdp_live *x, t_floatarg faudio )
{
- if ( ( faudio == 0.0 ) || ( faudio == 1 ) )
+ if ( ( faudio == 0. ) || ( faudio == 1. ) )
{
x->x_audio = (int)faudio;
}
}
+static void pdp_live_autoplay(t_pdp_live *x, t_floatarg fautoplay )
+{
+ if ( ( fautoplay == 0. ) || ( fautoplay == 1. ) )
+ {
+ x->x_autoplay = (int)fautoplay;
+ }
+}
+
+static void pdp_live_loop(t_pdp_live *x, t_floatarg floop )
+{
+ if ( ( floop == 0. ) || ( floop == 1. ) )
+ {
+ x->x_loop = (int)floop;
+ }
+}
+
+static void pdp_live_bang(t_pdp_live *x)
+{
+ if ( x->x_nextimage == 1 )
+ {
+ // post( "pdp_live~ : banging too fast, previous image is not decoded yet... ignored" );
+ return;
+ }
+ x->x_nextimage = 1;
+}
+
+static void pdp_live_frame_cold(t_pdp_live *x, t_floatarg frameindex)
+{
+ int frame = (int)frameindex;
+ int ret;
+ uint64_t newpts;
+
+ if (!(x->x_streaming)) return;
+
+#if FFMPEG_VERSION_INT >= 0x000409
+ if ( ( ( x->x_avcontext->iformat->flags & AVFMT_NOFILE ) == 0 ) && ( x->x_videoindex != -1 ) )
+ {
+ if ( x->x_framerate != 0 )
+ {
+ newpts = x->x_firstpts + ( frame * 1000000 ) / x->x_framerate;
+ }
+ else
+ {
+ 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 )
+ {
+ post( "pdp_live~ : couldn't seek the requested frame (ret=%d)", ret );
+ }
+ }
+#else
+ post( "pdp_live~ : no seek function with ffmpeg < 4.0.9");
+#endif
+}
+
static t_int pdp_live_decode_packet(t_pdp_live *x)
{
- t_int chunksize=0, length;
+ t_int chunksize=0, length, err, ret;
t_int audiosize, sizeout, imagesize, pictureok;
AVFrame frame;
uint8_t *pcktptr;
- struct timeval etime;
+ unsigned char *pY, *pU, *pV;
+ uint8_t *psY, *psU, *psV;
+ t_int px, py;
+ long long tplaying;
+ long long ttheoretical;
+ struct timeval ctime;
struct timespec mwait;
if ( !x->x_streaming )
@@ -159,13 +227,35 @@ static t_int pdp_live_decode_packet(t_pdp_live *x)
// post( "pdp_live~ : trying to read packet" );
// read new packet on the stream
- if (av_read_packet(x->x_avcontext, &x->x_pkt) < 0)
+ if ( x->x_streaming && av_read_packet(x->x_avcontext, &x->x_pkt) < 0)
{
x->x_nopackets++;
// post( "pdp_live~ : decoding thread : nothing to decode : no packets :%d", x->x_nopackets );
if ( x->x_nopackets > END_OF_STREAM )
{
x->x_endofstream = 1;
+ if ( x->x_loop )
+ {
+
+#if FFMPEG_VERSION_INT >= 0x000409
+
+ 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 )
+ {
+ post( "pdp_live~ : couldn't seek the requested frame (ret=%d)", ret );
+ }
+ else
+ {
+ x->x_endofstream = 0;
+ }
+ }
+
+#else
+ post( "pdp_live~ : no seek function with ffmpeg < 4.0.9");
+#endif
+ }
}
return -1;
}
@@ -182,6 +272,7 @@ static t_int pdp_live_decode_packet(t_pdp_live *x)
pcktptr = x->x_pkt.data;
while (length > 0)
{
+ if ( !x->x_streaming ) break;
switch(x->x_avcontext->streams[x->x_pkt.stream_index]->codec.codec_type)
{
case CODEC_TYPE_AUDIO:
@@ -240,7 +331,7 @@ static t_int pdp_live_decode_packet(t_pdp_live *x)
post( "pdp_live~ : audio overflow : packet ignored...");
x->x_audioin_position = 0;
}
- if ( ( x->x_audioin_position > x->x_blocksize ) && (!x->x_audioon) )
+ if ( ( x->x_audioin_position > 4*x->x_blocksize ) && (!x->x_audioon) )
{
x->x_audioon = 1;
// post( "pdp_live~ : audio on" );
@@ -253,20 +344,9 @@ static t_int pdp_live_decode_packet(t_pdp_live *x)
x->x_avcontext->streams[x->x_pkt.stream_index]->codec.height * 3) / 2; // yuv planar
x->x_framerate = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.frame_rate / 10000;
+ if ( x->x_framerate == 0 ) x->x_framerate = DEFAULT_FRAME_RATE;
x->x_videoindex = x->x_pkt.stream_index;
- // calculate actual frame rate
- // if ( gettimeofday(&etime, NULL) == -1)
- // {
- // post("pdp_live~ : could not read time" );
- // }
- // if ( ( etime.tv_sec - x->x_starttime.tv_sec ) > 0 )
- // {
- // x->x_framerate = x->x_nbframes / ( etime.tv_sec - x->x_starttime.tv_sec );
- // }
- // if ( x->x_framerate == 0 ) x->x_framerate = 1;
- // post ("pdp_live~ : frame rate is %d", x->x_framerate );
-
chunksize = avcodec_decode_video(
&x->x_avcontext->streams[x->x_pkt.stream_index]->codec,
&frame, &pictureok,
@@ -304,37 +384,74 @@ static t_int pdp_live_decode_packet(t_pdp_live *x)
else
{
x->x_newpicture=1;
- x->x_previouspts = x->x_pts;
- x->x_pts = frame.pts;
- // post( "pdp_live : frame pts : %ld", x->x_pts );
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;
x->x_vsize = x->x_vwidth*x->x_vheight;
- if ( x->x_previouspts != -1 )
+ // create a new pdp packet from BITMAP YV12 image format
+ x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_vwidth, x->x_vheight );
+ x->x_header = pdp_packet_header(x->x_packet0);
+ x->x_data = (unsigned char *)pdp_packet_data(x->x_packet0);
+
+ 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_picture_decoded.data[0];
+ psU = x->x_picture_decoded.data[1];
+ psV = x->x_picture_decoded.data[2];
+
+ for ( py=0; py<x->x_vheight; py++)
+ {
+ memcpy( (void*)pY, (void*)psY, x->x_vwidth );
+ pY += x->x_vwidth;
+ psY += x->x_picture_decoded.linesize[0];
+ 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_picture_decoded.linesize[1];
+ psV += x->x_picture_decoded.linesize[2];
+ }
+ }
+
+ if ( x->x_firstpts == -1 )
{
- mwait.tv_sec = 0;
- mwait.tv_nsec = (x->x_pts - x->x_previouspts)*1000;
-
- if ( ( x->x_pts == 0 ) )
- {
- // post("pdp_live~ : no presentation time stamp, using framerate :%d",
- // x->x_framerate );
- mwait.tv_sec = 0;
- mwait.tv_nsec = 1000000000/((x->x_framerate+5)); // the +5 is experimental
- // it comes from the time used in decoding
- }
-
- nanosleep( &mwait, NULL ); // wait between the two successive frames
- // i know, cheap flow control
+ x->x_firstpts = x->x_pts;
}
+ x->x_previouspts = x->x_pts;
+ x->x_pts = frame.pts;
+ // post( "pdp_live : frame pts : %ld", x->x_pts );
+ if ( x->x_nbframes > 0 )
+ {
+ if ( gettimeofday(&ctime, NULL) == -1)
+ {
+ post("pdp_theorin~ : 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 )",
+ // x->x_nbframes, tplaying, ttheoretical );
+
+ if ( tplaying < ttheoretical )
+ {
+ mwait.tv_sec = 0;
+ mwait.tv_nsec = (ttheoretical - tplaying)*1000000;
+
+ // wait between the two successive frames
+ if ( x->x_autoplay ) nanosleep( &mwait, NULL );
+ }
+ }
}
break;
}
pcktptr += chunksize;
length -= chunksize;
- if ( !x->x_streaming ) break;
}
av_free_packet(&x->x_pkt);
@@ -353,18 +470,10 @@ static void *pdp_decode_stream_from_url(void *tdata)
twait.tv_sec = 0;
twait.tv_nsec = 10000000; // 10 ms
- schedprio.sched_priority = 0;
- if ( sched_setscheduler(0,SCHED_OTHER,&schedprio) == -1)
- {
- post("pdp_live~ : couldn't set scheduler for decoding thread.\n");
- }
- if ( setpriority( PRIO_PROCESS, 0, x->x_priority ) < 0 )
- {
- post("pdp_live~ : couldn't set priority to %d for decoding thread.\n", x->x_priority );
- }
- else
+ schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority;
+ if ( sched_setscheduler(0,SCHED_FIFO,&schedprio) == -1)
{
- post("pdp_live~ : priority set to %d for thread %d.\n", x->x_priority, x->x_decodechild );
+ post("pdp_theorin~ : couldn't set priority for decoding thread.");
}
if ( ! (x->x_avcontext->iformat->flags & AVFMT_NOHEADER ) )
@@ -376,19 +485,27 @@ static void *pdp_decode_stream_from_url(void *tdata)
post( "pdp_live~ : read header." );
}
- while ( x->x_streaming )
+ while ( x->x_decodechild )
{
- while ( x->x_newpicture ) nanosleep( &twait, NULL );
+ if ( ( x->x_streaming ) && ( ( x->x_autoplay ) || ( x->x_nextimage == 1 ) ) )
+ {
+ x->x_decoding = 1;
+ while ( x->x_newpicture ) nanosleep( &twait, NULL );
- // decode incoming packets
- if ( pdp_live_decode_packet( x ) < 0 )
+ // decode incoming packets
+ if ( ( x->x_streaming ) && ( pdp_live_decode_packet( x ) < 0 ) )
+ {
+ nanosleep( &twait, NULL ); // nothing to read, just wait
+ }
+ x->x_nextimage = -1;
+ }
+ else
{
- nanosleep( &twait, NULL ); // nothing to read, just wait
+ x->x_decoding = 0;
+ nanosleep( &twait, NULL ); // nothing to do, just wait
}
-
}
- post( "pdp_live~ : decoding thread %d exiting....", x->x_decodechild );
x->x_decodechild = 0;
pthread_exit(NULL);
}
@@ -419,7 +536,6 @@ static void *pdp_live_connect_to_url(void *tdata)
if ( err == -5 ) post( "pdp_live~ : not enough memory" );
if ( err == -6 ) post( "pdp_live~ : unknown format ( stream not found? )" );
x->x_connectchild = 0;
- x->x_avcontext = av_mallocz(sizeof(AVFormatContext));
pthread_exit(NULL);
}
/* If not enough info to get the stream parameters, we decode the
@@ -507,11 +623,13 @@ static void *pdp_live_connect_to_url(void *tdata)
x->x_nopackets = 0;
x->x_endofstream = 0;
x->x_nbframes = 0;
+ x->x_pts = -1;
+ x->x_previouspts = -1;
+ x->x_firstpts = -1;
- x->x_connectchild = 0;
-
- if ( x->x_usethread )
+ if ( x->x_usethread && ( x->x_decodechild == 0 ))
{
+ x->x_decodechild = 1; // trick & treets
// launch decoding thread
if ( pthread_attr_init( &decode_child_attr ) < 0 )
{
@@ -556,15 +674,15 @@ static void pdp_live_disconnect(t_pdp_live *x)
{
x->x_streaming = 0;
x->x_newpicture = 0;
- post("pdp_live~ : waiting for the end of decoding thread..." );
- while ( x->x_decodechild && ( count < 100 ) )
+ post("pdp_live~ : waiting end of decoding..." );
+ while ( x->x_decoding ) nanosleep( &twait, NULL );
+ /* close each decoder */
+ for(i=0;i<x->x_avcontext->nb_streams;i++)
{
- count++;
- sleep( 1 );
- }
- if ( x->x_decodechild )
- {
- post("pdp_live~ : zombie thread, i guess" );
+ if (avcodec_close(&x->x_avcontext->streams[i]->codec) < 0)
+ {
+ post("pdp_live~ : error while closing codec for stream #%d", i);
+ }
}
post("pdp_live~ : closing input file..." );
av_close_input_file(x->x_avcontext);
@@ -631,9 +749,6 @@ static t_int *pdp_live_perform(t_int *w)
t_float *out2 = (t_float *)(w[2]); // right audio inlet
t_pdp_live *x = (t_pdp_live *)(w[3]);
int n = (int)(w[4]); // number of samples
- short int *pY, *pU, *pV;
- uint8_t *psY, *psU, *psV;
- t_int pixRGB, px, py;
short sampleL, sampleR;
struct timeval etime;
t_int sn;
@@ -650,7 +765,6 @@ static t_int *pdp_live_perform(t_int *w)
if ( x->x_audioon )
{
sn=0;
- n=n*DEFAULT_CHANNELS;
while (n--)
{
sampleL=x->x_audio_in[ sn++ ];
@@ -670,7 +784,7 @@ static t_int *pdp_live_perform(t_int *w)
memcpy( &x->x_audio_in[0], &x->x_audio_in[sn], 4*MAX_AUDIO_PACKET_SIZE-sn );
x->x_audioin_position-=sn;
// post( "pdp_live~ : audio in position : %d", x->x_audioin_position );
- if ( x->x_audioin_position <= sn )
+ if ( x->x_audioin_position <= 0 )
{
x->x_audioon = 0;
// post( "pdp_live~ : audio off" );
@@ -694,7 +808,7 @@ static t_int *pdp_live_perform(t_int *w)
if ( etime.tv_sec != x->x_cursec )
{
x->x_cursec = etime.tv_sec;
- if (x->x_streaming) outlet_float( x->x_outlet_framerate, x->x_framerate );
+ if (x->x_streaming) outlet_float( x->x_outlet_framerate, x->x_secondcount );
x->x_secondcount = 0;
}
if ( x->x_secondcount >= x->x_framerate )
@@ -705,45 +819,13 @@ static t_int *pdp_live_perform(t_int *w)
// output image if there's a new one decoded
if ( x->x_newpicture )
{
- // create a new pdp packet from PIX_FMT_YUV420P image format
- x->x_packet0 = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight );
- x->x_header = pdp_packet_header(x->x_packet0);
- x->x_data = (short int *)pdp_packet_data(x->x_packet0);
-
- 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_picture_decoded.data[0];
- psU = x->x_picture_decoded.data[1];
- psV = x->x_picture_decoded.data[2];
-
- for ( py=0; py<x->x_vheight; py++)
- {
- for ( px=0; px<x->x_vwidth; px++)
- {
- *(pY) = ( *(psY+px) << 7 );
- *(pV) = ( ((*(psV+(px>>1)))-128) << 8 );
- *(pU) = ( ((*(psU+(px>>1)))-128) << 8 );
- pY++;
- if ( (px%2==0) && (py%2==0) )
- {
- pV++; pU++;
- }
- }
- psY += x->x_picture_decoded.linesize[0];
- if ( py%2==0 ) psU += x->x_picture_decoded.linesize[1];
- if ( py%2==0 ) psV += x->x_picture_decoded.linesize[2];
- }
-
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 );
-
- x->x_newpicture = 0;
}
outlet_float( x->x_outlet_streaming, x->x_streaming );
outlet_float( x->x_outlet_endofstream, x->x_endofstream );
@@ -760,18 +842,19 @@ static void pdp_live_free(t_pdp_live *x)
{
int i;
+ if ( x->x_decodechild )
+ {
+ x->x_decodechild = 0;
+ x->x_decoding = 0;
+ sleep( 5 ); // let the decoding thread exiting
+ }
+
if ( x->x_streaming )
{
pdp_live_disconnect(x);
- sleep(3);
}
post( "pdp_live~ : freeing object" );
pdp_packet_mark_unused(x->x_packet0);
- if (x->x_audio_resample_ctx)
- {
- audio_resample_close(x->x_audio_resample_ctx);
- x->x_audio_resample_ctx = NULL;
- }
av_free_static();
}
@@ -783,6 +866,8 @@ void *pdp_live_new(void)
t_pdp_live *x = (t_pdp_live *)pd_new(pdp_live_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);
@@ -796,6 +881,7 @@ void *pdp_live_new(void)
x->x_packet0 = -1;
x->x_connectchild = 0;
x->x_decodechild = 0;
+ x->x_decoding = 0;
x->x_usethread = 1;
x->x_priority = DEFAULT_PRIORITY;
x->x_framerate = DEFAULT_FRAME_RATE;
@@ -807,15 +893,19 @@ void *pdp_live_new(void)
x->x_secondcount = 0;
x->x_audio_resample_ctx = NULL;
x->x_nbvideostreams = 0;
- x->x_videoindex = 0;
+ x->x_videoindex = -1;
x->x_audioin_position = 0;
x->x_newpicture = 0;
x->x_endofstream = 0;
x->x_nopackets = 0;
x->x_blocksize = MIN_AUDIO_SIZE;
+ x->x_autoplay = 1;
+ x->x_loop = 1;
+ x->x_nextimage = 0;
x->x_pts = -1;
x->x_previouspts = -1;
+ x->x_firstpts = -1;
x->x_avcontext = av_mallocz(sizeof(AVFormatContext));
if ( !x->x_avcontext )
@@ -851,7 +941,11 @@ void pdp_live_tilde_setup(void)
class_addmethod(pdp_live_class, (t_method)pdp_live_disconnect, gensym("disconnect"), A_NULL);
class_addmethod(pdp_live_class, (t_method)pdp_live_priority, gensym("priority"), A_FLOAT, A_NULL);
class_addmethod(pdp_live_class, (t_method)pdp_live_audio, gensym("audio"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_live_class, (t_method)pdp_live_autoplay, gensym("autoplay"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_live_class, (t_method)pdp_live_loop, gensym("loop"), A_FLOAT, A_NULL);
class_addmethod(pdp_live_class, (t_method)pdp_live_threadify, gensym("thread"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_live_class, (t_method)pdp_live_bang, gensym("bang"), A_NULL);
+ class_addmethod(pdp_live_class, (t_method)pdp_live_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL);
class_sethelpsymbol( pdp_live_class, gensym("pdp_live~.pd") );
}