Update of /cvsroot/pure-data/externals/pidip/modules In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21968/modules
Added Files: pdp_ieee1394.c pdp_mp4audiosource.cpp pdp_mp4audiosync.cpp pdp_mp4config.cpp pdp_mp4live~.cpp pdp_mp4playermedia.cpp pdp_mp4playersession.cpp pdp_mp4player~.cpp pdp_mp4videosource.cpp pdp_mp4videosync.cpp pdp_spotlight.c Log Message: New in PiDiP 0.12.13
--- NEW FILE: pdp_mp4config.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com * Bill May wmay@cisco.com * * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr) */
#include "pdp_mp4config.h"
CLiveConfig::CLiveConfig( SConfigVariable* variables, config_index_t numVariables, const char* defaultFileName) : CConfigSet(variables, numVariables, defaultFileName) { m_appAutomatic = false; m_videoEncode = true; m_videoMaxWidth = 768; m_videoMaxHeight = 576; m_videoNeedRgbToYuv = false; m_videoMpeg4ConfigLength = 0; m_videoMpeg4Config = NULL; m_videoMaxVopSize = 128 * 1024; m_audioEncode = true; }
CLiveConfig::~CLiveConfig() { CHECK_AND_FREE(m_videoMpeg4Config); }
// recalculate derived values void CLiveConfig::Update() { UpdateVideo(); UpdateAudio(); }
void CLiveConfig::UpdateVideo() { m_videoEncode = true;
CalculateVideoFrameSize();
GenerateMpeg4VideoConfig(this); }
void CLiveConfig::UpdateFileHistory(const char* fileName) { }
void CLiveConfig::CalculateVideoFrameSize() { u_int16_t frameHeight; float aspectRatio = GetFloatValue(CONFIG_VIDEO_ASPECT_RATIO);
// crop video to appropriate aspect ratio modulo 16 pixels if ((aspectRatio - VIDEO_STD_ASPECT_RATIO) < 0.1) { frameHeight = GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT); } else { frameHeight = (u_int16_t)( (float)GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH) / aspectRatio);
if ((frameHeight % 16) != 0) { frameHeight += 16 - (frameHeight % 16); }
if (frameHeight > GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT)) { // OPTION might be better to insert black lines // to pad image but for now we crop down frameHeight = GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT); if ((frameHeight % 16) != 0) { frameHeight -= (frameHeight % 16); } } }
m_videoWidth = GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH); m_videoHeight = frameHeight;
m_ySize = m_videoWidth * m_videoHeight; m_uvSize = m_ySize / 4; m_yuvSize = (m_ySize * 3) / 2; }
void CLiveConfig::UpdateAudio() { m_audioEncode = true; }
void CLiveConfig::UpdateRecord() { }
bool CLiveConfig::IsOneSource() { return true; }
bool CLiveConfig::IsCaptureVideoSource() { return false; }
bool CLiveConfig::IsCaptureAudioSource() { return false; }
bool CLiveConfig::IsFileVideoSource() { return false; }
bool CLiveConfig::IsFileAudioSource() { return false; }
--- NEW FILE: pdp_mp4player~.cpp --- /* * 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 quicktime stream picker object * A lot of this object code is inspired by the code from mpeg4ip * Copyright (c) 2000, 2001, 2002 Dave Mackie, Bill May & others * The rest is written by Yves Degoyon ( ydegoyon@free.fr ) */
#include "pdp_mp4player~.h"
static char *pdp_mp4player_version = "pdp_mp4player~: version 0.1, a mpeg4ip stream decoder ( ydegoyon@free.fr).";
#ifdef __cplusplus extern "C" { #endif
static void pdp_mp4player_audio(t_pdp_mp4player *x, t_floatarg faudio ) { if ( ( faudio == 0. ) || ( faudio == 1. ) ) { x->x_audio = (int)faudio; } }
static void pdp_mp4player_overtcp(t_pdp_mp4player *x, t_floatarg fovertcp ) { if ( ( fovertcp == 0. ) || ( fovertcp == 1. ) ) { x->x_rtpovertcp = (t_int)fovertcp; config.set_config_value(CONFIG_USE_RTP_OVER_RTSP, x->x_rtpovertcp); if ( x->x_rtpovertcp ) { post("pdp_mp4player~ : using rtp over rtsp (tcp)" ); } else { post("pdp_mp4player~ : using rtp mode (udp)" ); } } }
static void pdp_mp4player_priority(t_pdp_mp4player *x, t_floatarg fpriority ) { if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) ) { x->x_priority = (int)fpriority; } }
static void pdp_mp4player_vwidth(t_pdp_mp4player *x, t_floatarg fWidth ) { if ( ( (t_int) fWidth <= 0 ) ) { post("pdp_mp4player~ : wrong width : %d", fWidth ); return; }
post( "pdp_mp4player~ : setting width : %d", (t_int) fWidth ); config.set_config_value( CONFIG_VIDEO_RAW_WIDTH, (t_int) fWidth );
}
static void pdp_mp4player_vheight(t_pdp_mp4player *x, t_floatarg fHeight ) { if ( ( (t_int) fHeight <= 0 ) ) { post("pdp_mp4player~ : wrong height : %d", fHeight ); return; }
post( "pdp_mp4player~ : setting height : %d", (t_int) fHeight ); config.set_config_value( CONFIG_VIDEO_RAW_HEIGHT, (t_int) fHeight );
}
static void pdp_mp4player_disconnect(t_pdp_mp4player *x) { if (!x->x_streaming) { post("pdp_mp4player~ : close request but no stream is played ... ignored" ); return; }
x->x_streaming = 0;
outlet_float( x->x_outlet_streaming, x->x_streaming ); x->x_nbframes = 0; outlet_float( x->x_outlet_nbframes, x->x_nbframes );
post( "pdp_mp4player~ : deleting session" ); delete x->x_psession; post( "pdp_mp4player~ : deleting semaphore" ); SDL_DestroySemaphore(x->x_psem); }
static void *pdp_mp4player_decode(void *tdata) { t_pdp_mp4player *x = (t_pdp_mp4player*)tdata; struct sched_param schedprio; t_int pmin, pmax; struct timespec twait, mwait;
twait.tv_sec = 0; twait.tv_nsec = 10000000; // 10 ms
schedprio.sched_priority = 0; if ( sched_setscheduler(0, SCHED_OTHER, &schedprio) == -1) { post("pdp_mp4player~ : couldn't set scheduler for decoding thread.\n"); } if ( setpriority( PRIO_PROCESS, 0, x->x_priority ) < 0 ) { post("pdp_mp4player~ : couldn't set priority to %d for decoding thread.\n", x->x_priority ); } else { post("pdp_mp4player~ : priority set to %d for thread %d.\n", x->x_priority, x->x_decodechild ); }
while ( x->x_streaming ) { x->x_decodingstate = x->x_psession->sync_thread(x->x_decodingstate); nanosleep( &twait, NULL ); // nothing to read, just wait }
post( "pdp_mp4player~ : decoding thread %d exiting....", x->x_decodechild ); x->x_decodechild = 0; pthread_exit(NULL); }
static void pdp_mp4player_connect(t_pdp_mp4player *x, t_symbol *s) { t_int ret, i; char buffer[1024]; char errmsg[512]; pthread_attr_t decode_child_attr;
if ( x->x_streaming ) { post("pdp_mp4player~ : connection request but a connection is pending ... disconnecting" ); pdp_mp4player_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 );
x->x_psem = SDL_CreateSemaphore(0); snprintf(buffer, sizeof(buffer), "pdp_mp4player~ - %s", x->x_url); x->x_psession = new CPlayerSession(&x->x_queue, x->x_psem, buffer, x); if (x->x_psession == NULL) { post("pdp_mp4player~ : FATAL : could not create session" ); return; }
ret = parse_name_for_session(x->x_psession, x->x_url, errmsg, sizeof(errmsg), NULL); if (ret < 0) { post("pdp_mp4player~ : FATAL : wrong url : %s : reason : %s", x->x_url, errmsg ); delete x->x_psession; return; }
if (ret > 0) { post("pdp_mp4player~ : %s", errmsg ); }
x->x_psession->set_up_sync_thread();
if (x->x_psession->play_all_media(TRUE) != 0) { post("pdp_mp4player~ : FATAL : couldn't play all medias" ); delete x->x_psession; return; }
// launch decoding thread if ( pthread_attr_init( &decode_child_attr ) < 0 ) { post( "pdp_mp4player~ : could not launch decoding thread" ); perror( "pthread_attr_init" ); return; } if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_mp4player_decode, x ) < 0 ) { post( "pdp_mp4player~ : could not launch decoding thread" ); perror( "pthread_create" ); return; }
post("pdp_mp4player~ : session started" ); x->x_streaming = 1;
return; }
/* decode the stream to fill up buffers */ static t_int *pdp_mp4player_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_mp4player *x = (t_pdp_mp4player *)(w[3]); int n = (int)(w[4]); // number of samples short sampleL, sampleR; struct timeval etime; t_int sn;
// just read the buffer if ( x->x_audioon ) { sn=0; n=n*DEFAULT_CHANNELS; while (n--) { sampleL=x->x_audio_in[ sn++ ]; *(out1) = ((t_float)sampleL)/32768.0; if ( DEFAULT_CHANNELS == 1 ) { *(out2) = *(out1); } if ( DEFAULT_CHANNELS == 2 ) { sampleR=x->x_audio_in[ sn++ ]; *(out2) = ((t_float)sampleR)/32768.0; } out1++; out2++; } x->x_audioin_position-=sn; memcpy( &x->x_audio_in[0], &x->x_audio_in[sn], 4*MAX_AUDIO_PACKET_SIZE-sn ); // post( "pdp_mp4player~ : audio in position : %d", x->x_audioin_position ); if ( x->x_audioin_position <= sn ) { x->x_audioon = 0; // post( "pdp_mp4player~ : audio off" ); } } else { // post("pdp_mp4player~ : no available audio" ); while (n--) { *(out1++) = 0.0; *(out2++) = 0.0; } }
// check if the framerate has been exceeded if ( gettimeofday(&etime, NULL) == -1) { post("pdp_mp4player~ : could not read time" ); } if ( etime.tv_sec != x->x_cursec ) { x->x_cursec = etime.tv_sec; outlet_float( x->x_outlet_framerate, x->x_secondcount ); x->x_secondcount = 0; }
if ( x->x_newpicture ) { pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0);
// update streaming status outlet_float( x->x_outlet_streaming, x->x_streaming ); x->x_nbframes++; x->x_secondcount++; outlet_float( x->x_outlet_nbframes, x->x_nbframes ); }
return (w+5); }
static void pdp_mp4player_dsp(t_pdp_mp4player *x, t_signal **sp) { dsp_add(pdp_mp4player_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); }
static void pdp_mp4player_free(t_pdp_mp4player *x) { int i;
if ( x->x_streaming ) { pdp_mp4player_disconnect(x); } post( "pdp_mp4player~ : freeing object" ); pdp_packet_mark_unused(x->x_packet0);
// remove invalid global ports close_plugins(); }
t_class *pdp_mp4player_class;
void *pdp_mp4player_new(void) { int i;
t_pdp_mp4player *x = (t_pdp_mp4player *)pd_new(pdp_mp4player_class);
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_streaming = 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_packet0 = -1; x->x_nbframes = 0; x->x_cursec = 0; x->x_secondcount = 0; x->x_audioin_position = 0; x->x_priority = DEFAULT_PRIORITY; x->x_decodechild = 0; x->x_newpicture = 0;
memset( &x->x_audio_buf[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) ); memset( &x->x_audio_in[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) );
// initialize mpeg4hippies initialize_plugins(); config.read_config_file(); rtsp_set_error_func(player_library_message); rtsp_set_loglevel(config.get_config_value(CONFIG_RTSP_DEBUG)); rtp_set_error_msg_func(player_library_message); rtp_set_loglevel(config.get_config_value(CONFIG_RTP_DEBUG)); sdp_set_error_func(player_library_message); sdp_set_loglevel(config.get_config_value(CONFIG_SDP_DEBUG)); http_set_error_func(player_library_message); http_set_loglevel(config.get_config_value(CONFIG_HTTP_DEBUG));
x->x_rtpovertcp = 0; config.set_config_value(CONFIG_USE_RTP_OVER_RTSP, x->x_rtpovertcp);
return (void *)x; }
void pdp_mp4player_tilde_setup(void) { // post( pdp_mp4player_version ); pdp_mp4player_class = class_new(gensym("pdp_mp4player~"), (t_newmethod)pdp_mp4player_new, (t_method)pdp_mp4player_free, sizeof(t_pdp_mp4player), 0, A_NULL);
class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_dsp, gensym("dsp"), A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_connect, gensym("connect"), A_SYMBOL, A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_disconnect, gensym("disconnect"), A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_audio, gensym("audio"), A_FLOAT, A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_overtcp, gensym("overtcp"), A_FLOAT, A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_priority, gensym("priority"), A_FLOAT, A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_vwidth, gensym("vwidth"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_vheight, gensym("vheight"), A_DEFFLOAT, A_NULL); class_sethelpsymbol( pdp_mp4player_class, gensym("pdp_mp4player~.pd") );
}
#ifdef __cplusplus } #endif
--- NEW FILE: pdp_mp4playersession.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com [...1006 lines suppressed...] if (m_video_sync != NULL) m_video_sync->flush_sync_buffers(); if (m_audio_sync != NULL) m_audio_sync->flush_sync_buffers(); break; } } return (state); }
int pdp_sync_thread (void *data) { CPlayerSession *p; int state = SYNC_STATE_INIT; p = (CPlayerSession *)data; do { state = p->sync_thread(state); } while (state != SYNC_STATE_EXIT); return (0); }
--- NEW FILE: pdp_mp4videosync.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com * video aspect ratio by: * Peter Maersk-Moller peter@maersk-moller.net * * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr) */
/* * video.cpp - provides codec to video hardware class */
#include <string.h> #include "pdp_mp4videosync.h" #include "pdp_mp4playersession.h" #include "player_util.h" #include "m_pd.h"
#define video_message(loglevel, fmt...) message(loglevel, "videosync", fmt)
CPDPVideoSync::CPDPVideoSync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) : CVideoSync(psptr) { char buf[32];
m_screen = NULL; m_image = NULL; m_video_initialized = 0; m_config_set = 0; m_have_data = 0; m_y_buffer[0] = NULL; m_u_buffer[0] = NULL; m_v_buffer[0] = NULL; m_buffer_filled[0] = 0; m_play_index = m_fill_index = 0; m_decode_waiting = 0; m_dont_fill = 0; m_paused = 1; m_behind_frames = 0; m_total_frames = 0; m_behind_time = 0; m_behind_time_max = 0; m_skipped_render = 0; m_video_scale = 2; m_msec_per_frame = 0; m_consec_skipped = 0; m_fullscreen = 0; m_filled_frames = 0; m_double_width = 0; m_pixel_width = 0; m_pixel_height = 0; m_max_width = 0; m_max_height = 0; m_father = pdp_father; }
CPDPVideoSync::~CPDPVideoSync (void) { if (m_fullscreen != 0) { m_fullscreen = 0; do_video_resize(); } if (m_image) { m_image = NULL; } if (m_screen) { m_screen = NULL; } if (m_y_buffer[0] != NULL) { free(m_y_buffer[0]); m_y_buffer[0] = NULL; } if (m_u_buffer[0] != NULL) { free(m_u_buffer[0]); m_u_buffer[0] = NULL; } if (m_v_buffer[0] != NULL) { free(m_v_buffer[0]); m_v_buffer[0] = NULL; } }
void CPDPVideoSync::config (int w, int h) { m_width = w; m_height = h; m_y_buffer[0] = (uint8_t *)malloc(w * h * sizeof(uint8_t)); m_u_buffer[0] = (uint8_t *)malloc(w/2 * h/2 * sizeof(uint8_t)); m_v_buffer[0] = (uint8_t *)malloc(w/2 * h/2 * sizeof(uint8_t)); m_buffer_filled[0] = 0; m_config_set = 1; post( "pdp_mp4videosync : configuration done : %dx%d", m_width, m_height ); }
int CPDPVideoSync::initialize_video (const char *name) { if (m_video_initialized == 0) { if (m_config_set) { int ret; int video_scale = m_video_scale;
int w = m_width * video_scale / 2; if (m_double_width) w *= 2; int h = m_height * video_scale / 2; m_video_initialized = 1; post( "pdp_mp4videosync : video initialized : %dx%d", m_width, m_height ); return (1); } else { return (0); } } return (1); }
int CPDPVideoSync::is_video_ready (uint64_t &disptime) { return 1; }
void CPDPVideoSync::play_video (void) {
}
int64_t CPDPVideoSync::play_video_at (uint64_t current_time, int &have_eof) { uint64_t play_this_at; unsigned int ix; uint8_t *to, *from;
post( "pdp_mp4videosync : play video at : %ld", current_time );
return (10); }
int CPDPVideoSync::get_video_buffer(uint8_t **y, uint8_t **u, uint8_t **v) {
post( "pdp_mp4videosync : get video buffer" ); *y = m_y_buffer[m_fill_index]; *u = m_u_buffer[m_fill_index]; *v = m_v_buffer[m_fill_index]; return (1); }
void CPDPVideoSync::filled_video_buffers (uint64_t time) { int ix; // post( "pdp_mp4videosync : filled video buffer : %ld", time ); m_psptr->wake_sync_thread(); }
void CPDPVideoSync::set_video_frame(const uint8_t *y, const uint8_t *u, const uint8_t *v, int pixelw_y, int pixelw_uv, uint64_t time) { // post( "pdp_mp4videosync : set video frame : %dx%d", m_width, m_height ); m_psptr->wake_sync_thread();
// pass the data to the pdp object m_father->x_newpicture = 1; return; }
void CPDPVideoSync::flush_sync_buffers (void) { post( "pdp_mp4videosync : flush sync buffer" ); }
void CPDPVideoSync::flush_decode_buffers (void) { post( "pdp_mp4videosync : flush decode buffer" ); }
static void pdp_video_configure (void *ifptr, int w, int h, int format, double aspect_ratio ) { ((CPDPVideoSync *)ifptr)->config(w, h); }
static int pdp_video_get_buffer (void *ifptr, uint8_t **y, uint8_t **u, uint8_t **v) { return (((CPDPVideoSync *)ifptr)->get_video_buffer(y, u, v)); }
static void pdp_video_filled_buffer(void *ifptr, uint64_t time) { ((CPDPVideoSync *)ifptr)->filled_video_buffers(time); }
static void pdp_video_have_frame (void *ifptr, const uint8_t *y, const uint8_t *u, const uint8_t *v, int m_pixelw_y, int m_pixelw_uv, uint64_t time) { CPDPVideoSync *foo = (CPDPVideoSync *)ifptr;
foo->set_video_frame(y, u, v, m_pixelw_y, m_pixelw_uv, time); }
video_vft_t video_vft = { message, pdp_video_configure, pdp_video_get_buffer, pdp_video_filled_buffer, pdp_video_have_frame, };
video_vft_t *get_video_vft (void) { return (&video_vft); }
CPDPVideoSync *pdp_create_video_sync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) { return new CPDPVideoSync(psptr, pdp_father); }
--- NEW FILE: pdp_spotlight.c --- /* * 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 an adaptation of lens effect from effectv * Originally written by Fukuchi Kentaro & others * Pd-fication by Yves Degoyon */
#include "pdp.h" #include "yuv.h" #include <math.h>
static char *pdp_spotlight_version = "pdp_spotlight: version 0.1, specially made for cabaret, written by Yves Degoyon (ydegoyon@free.fr)";
typedef struct pdp_spotlight_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; t_int x_cx; // coordinates of lens center t_int x_cy; // coordinates of lens center t_int x_ssize; // width of the spotlight t_float x_strength; // strength of the light (0<=strength<=1)
t_int x_colorR; // red component of the color t_int x_colorG; // green component of the color t_int x_colorB; // blue component of the color
} t_pdp_spotlight;
static void pdp_spotlight_ssize(t_pdp_spotlight *x, t_floatarg fssize ) { if ( fssize>=0 ) { x->x_ssize = (int)fssize; } }
static void pdp_spotlight_cy(t_pdp_spotlight *x, t_floatarg fcy ) { if ( fcy>0 ) { x->x_cy = (int)fcy; } }
static void pdp_spotlight_cx(t_pdp_spotlight *x, t_floatarg fcx ) { if ( fcx>0 ) { x->x_cx = (int)fcx; } }
static void pdp_spotlight_r(t_pdp_spotlight *x, t_floatarg fr ) { if ( ( fr >= 0 ) && ( fr <= 255 ) ) { x->x_colorR = (int)fr; } }
static void pdp_spotlight_g(t_pdp_spotlight *x, t_floatarg fg ) { if ( ( fg >= 0 ) && ( fg <= 255 ) ) { x->x_colorG = (int)fg; } }
static void pdp_spotlight_b(t_pdp_spotlight *x, t_floatarg fb ) { if ( ( fb >= 0 ) && ( fb <= 255 ) ) { x->x_colorB = (int)fb; } }
static void pdp_spotlight_strength(t_pdp_spotlight *x, t_floatarg fstrength ) { if ( ( fstrength >= 0.0 ) && ( fstrength <= 1.0 ) ) { x->x_strength = fstrength; } }
static void pdp_spotlight_process_yv12(t_pdp_spotlight *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;
short int *poy, *pou, *pov, *pny, *pnu, *pnv; short int pmx, pMx, pmy, pMy; int px, py, ray2;
x->x_vwidth = header->info.image.width; x->x_vheight = header->info.image.height; x->x_vsize = x->x_vwidth*x->x_vheight;
newheader->info.image.encoding = header->info.image.encoding; newheader->info.image.width = x->x_vwidth; newheader->info.image.height = x->x_vheight;
memcpy(newdata, data, (x->x_vsize + (x->x_vsize>>1))<<1);
poy = data; pou = data + x->x_vsize; pov = data + x->x_vsize + (x->x_vsize>>2); pny = newdata; pnu = newdata + x->x_vsize; pnv = newdata + x->x_vsize + (x->x_vsize>>2); if ( x->x_cy-x->x_ssize < 0 ) { pmy=0; } else { pmy=x->x_cy-x->x_ssize; } if ( x->x_cy+x->x_ssize > x->x_vheight ) { pMy=x->x_vheight-1; } else { pMy=x->x_cy+x->x_ssize; } if ( x->x_cx-x->x_ssize < 0 ) { pmx=0; } else { pmx=x->x_cx-x->x_ssize; } if ( x->x_cx+x->x_ssize > x->x_vwidth ) { pMx=x->x_vwidth-1; } else { pMx=x->x_cx+x->x_ssize; } ray2 = pow( x->x_ssize, 2 ); for (py = pmy; py <= pMy ; py++) { for (px = pmx; px <= pMx; px++) { if ( ( pow( (px-x->x_cx), 2 ) + pow( (py-x->x_cy), 2 ) ) < ray2 ) { *(pny+py*x->x_vwidth+px) = (t_float)(*(pny+py*x->x_vwidth+px))*(1.-x->x_strength) + (t_float)(((yuv_RGBtoY( (x->x_colorB << 16) + (x->x_colorG << 8) + x->x_colorR ))<<7)) *(x->x_strength); *(pnu+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = (t_float)(*(pnu+(py>>1)*(x->x_vwidth>>1)+(px>>1)))*(1.-x->x_strength) + (t_float)(((yuv_RGBtoU( (x->x_colorB << 16) + (x->x_colorG << 8) + x->x_colorR ))-128)<<8)*(x->x_strength); *(pnv+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = (t_float)(*(pnv+(py>>1)*(x->x_vwidth>>1)+(px>>1)))*(1.-x->x_strength) + (t_float)(((yuv_RGBtoV( (x->x_colorB << 16) + (x->x_colorG << 8) + x->x_colorR ))-128)<<8)*(x->x_strength); } } }
return; }
static void pdp_spotlight_sendpacket(t_pdp_spotlight *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_spotlight_process(t_pdp_spotlight *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_spotlight_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_spotlight_process_yv12, pdp_spotlight_sendpacket, &x->x_queue_id); break;
default: /* don't know the type, so dont pdp_spotlight_process */ break; } } }
static void pdp_spotlight_input_0(t_pdp_spotlight *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_spotlight_process(x);
} }
static void pdp_spotlight_free(t_pdp_spotlight *x) { int i;
pdp_queue_finish(x->x_queue_id); pdp_packet_mark_unused(x->x_packet0); }
t_class *pdp_spotlight_class;
void *pdp_spotlight_new(void) { int i;
t_pdp_spotlight *x = (t_pdp_spotlight *)pd_new(pdp_spotlight_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cx")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cy")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ssize")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("r")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("g")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("b")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("strength"));
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_cx = 70; x->x_cy = 70; x->x_ssize = 50; x->x_colorR = 255; x->x_colorG = 255; x->x_colorB = 255; x->x_strength = 0.5;
return (void *)x; }
#ifdef __cplusplus extern "C" { #endif
void pdp_spotlight_setup(void) { // post( pdp_spotlight_version ); pdp_spotlight_class = class_new(gensym("pdp_spotlight"), (t_newmethod)pdp_spotlight_new, (t_method)pdp_spotlight_free, sizeof(t_pdp_spotlight), 0, A_NULL);
class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_cx, gensym("cx"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_cy, gensym("cy"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_ssize, gensym("ssize"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_r, gensym("r"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_g, gensym("g"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_b, gensym("b"), A_FLOAT, A_NULL); class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_strength, gensym("strength"), A_FLOAT, A_NULL); class_sethelpsymbol( pdp_spotlight_class, gensym("pdp_spotlight.pd") );
}
#ifdef __cplusplus } #endif
--- NEW FILE: pdp_mp4videosource.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com * Bill May wmay@cisco.com * * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr) */
#ifndef debug_message #define debug_message post #endif
#include <sys/mman.h>
#include "m_pd.h" #include "pdp_mp4videosource.h" #include "video_util_rgb.h"
//#define DEBUG_TIMESTAMPS 1
int CPDPVideoSource::ThreadMain(void) { // just a stub, the threaded mode is not used ( never called ) return 0; }
void CPDPVideoSource::DoStart(void) { if (m_source) return; if (!Init()) return; m_source = true; }
void CPDPVideoSource::DoStop(void) { if (!m_source) return; DoStopVideo(); m_source = false; }
bool CPDPVideoSource::Init(void) {
if (m_pConfig->GetIntegerValue(CONFIG_VIDEO_SIGNAL) == VIDEO_SIGNAL_NTSC) { m_videoSrcFrameRate = VIDEO_NTSC_FRAME_RATE; } else { m_videoSrcFrameRate = VIDEO_PAL_FRAME_RATE; }
m_videoMbuf.frames = 2; m_cacheTimestamp = false; m_videoFrameMap = (struct video_mmap*) malloc(m_videoMbuf.frames * sizeof(struct video_mmap)); if ( !m_videoFrameMap ) { post("pdp_mp4live~ : video source init : failed to allocate enough memory"); return false; }
m_videoFrameMapFrame = (uint64_t *)malloc(m_videoMbuf.frames * sizeof(uint64_t)); m_videoFrameMapTimestamp = (uint64_t *)malloc(m_videoMbuf.frames * sizeof(Timestamp)); m_captureHead = 0; m_encodeHead = -1;
m_videoFrames = 0; m_videoSrcFrameDuration = (Duration)(((float)TimestampTicks / m_videoSrcFrameRate) + 0.5); for (int i = 0; i < m_videoMbuf.frames; i++) { // initialize frame map m_videoFrameMap[i].frame = i; m_videoFrameMap[i].width = m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH); m_videoFrameMap[i].height = m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT); m_videoFrameMap[i].format = VIDEO_PALETTE_YUV420P;
if (i == 0) { m_videoCaptureStartTimestamp = GetTimestamp(); } m_lastVideoFrameMapFrameLoaded = m_videoFrameMapFrame[i] = i; m_lastVideoFrameMapTimestampLoaded = m_videoFrameMapTimestamp[i] = CalculateVideoTimestampFromFrames(i); }
m_pConfig->CalculateVideoFrameSize();
InitVideo( (m_pConfig->m_videoNeedRgbToYuv ? RGBVIDEOFRAME : YUVVIDEOFRAME), true);
SetVideoSrcSize( m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH), m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT), m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH), true);
m_maxPasses = (u_int8_t)(m_videoSrcFrameRate + 0.5);
return true; }
int8_t CPDPVideoSource::StartTimeStamp(Timestamp &frameTimestamp) { if (m_cacheTimestamp) frameTimestamp = m_videoFrameMapTimestamp[m_captureHead]; else frameTimestamp = GetTimestamp();
int8_t capturedFrame = m_captureHead; m_captureHead = (m_captureHead + 1) % m_videoMbuf.frames;
return capturedFrame; }
bool CPDPVideoSource::EndTimeStamp(int8_t frameNumber) { Timestamp calc = GetTimestamp();
if (calc > m_videoSrcFrameDuration + m_lastVideoFrameMapTimestampLoaded) { #ifdef DEBUG_TIMESTAMPS debug_message("pdp_mp4live~ : video frame delay past end of buffer - time is %llu should be %llu", calc, m_videoSrcFrameDuration + m_lastVideoFrameMapTimestampLoaded); #endif m_videoCaptureStartTimestamp = calc; m_videoFrameMapFrame[frameNumber] = 0; m_videoFrameMapTimestamp[frameNumber] = calc; } else { m_videoFrameMapFrame[frameNumber] = m_lastVideoFrameMapFrameLoaded + 1; m_videoFrameMapTimestamp[frameNumber] = CalculateVideoTimestampFromFrames(m_videoFrameMapFrame[frameNumber]); }
m_lastVideoFrameMapFrameLoaded = m_videoFrameMapFrame[frameNumber]; m_lastVideoFrameMapTimestampLoaded = m_videoFrameMapTimestamp[frameNumber]; return true; }
void CPDPVideoSource::ProcessVideo(u_int8_t *pY, u_int8_t *pU, u_int8_t *pV) { // for efficiency, process ~1 second before returning to check for commands Timestamp frameTimestamp; for (int pass = 0; pass < m_maxPasses; pass++) {
// get next frame from video capture device m_encodeHead = StartTimeStamp(frameTimestamp); if (m_encodeHead == -1) { continue; }
ProcessVideoYUVFrame( pY, pU, pV, m_videoSrcWidth, m_videoSrcWidth >> 1, frameTimestamp);
// release video frame buffer back to video capture device if (EndTimeStamp(m_encodeHead)) { m_encodeHead = (m_encodeHead + 1) % m_videoMbuf.frames; } else { debug_message("pdp_mp4live~ : couldn't release capture buffer!"); } } }
--- NEW FILE: pdp_mp4live~.cpp --- /* * 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 mpeg4ip streaming object towards a Darwin or Quicktime streaming server * A lot of this object code is inspired by the code from mpeg4ip * Copyright (c) 2000, 2001, 2002 Dave Mackie, Bill May & others * The rest is written by Yves Degoyon ( ydegoyon@free.fr ) */
#include "pdp.h" #include <math.h> #include <time.h> #include <sys/time.h>
/* mpeg4ip includes taken from the source tree ( not exported ) */ #include <mp4.h> #define DECLARE_CONFIG_VARIABLES #include "config_set.h"
#undef CONFIG_BOOL #define CONFIG_BOOL(var, name, defval) \ { &(var), (name), CONFIG_TYPE_BOOL, (defval), (defval) } #undef CONFIG_FLOAT #define CONFIG_FLOAT(var, name, defval) \ { &(var), (name), CONFIG_TYPE_FLOAT,(float) (defval), (float) (defval) } #undef CONFIG_INT #define CONFIG_INT(var, name, defval) \ { &(var), (name), CONFIG_TYPE_INTEGER,(config_integer_t) (defval), (config_integer_t)(defval) } #undef CONFIG_STRING #define CONFIG_STRING(var, name, defval) \ { &(var), (name), CONFIG_TYPE_STRING, (defval), (defval) }
#include "pdp_mp4config.h"
#undef DECLARE_CONFIG_VARIABLES #ifndef debug_message #define debug_message post #endif #include "rtp_transmitter.h" #include "pdp_mp4videosource.h" #include "pdp_mp4audiosource.h"
#define VIDEO_BUFFER_SIZE (1024*1024) #define MAX_AUDIO_PACKET_SIZE (128 * 1024) #define AUDIO_PACKET_SIZE (2*1024) /* using aac encoding */
static char *pdp_mp4live_version = "pdp_mp4live~: version 0.1, an mpeg4ip video streaming object ( ydegoyon@free.fr )";
typedef struct pdp_mp4live_struct { t_object x_obj; t_float x_f;
t_int x_packet0; t_int x_dropped; t_int x_queue_id;
t_int x_vwidth; t_int x_vheight; t_int x_vsize;
t_outlet *x_outlet_streaming; // indicates the status of streaming t_outlet *x_outlet_nbframes; // number of frames emitted t_outlet *x_outlet_framerate; // current frame rate
t_int x_streaming; // streaming flag t_int x_nbframes; // number of frames emitted t_int x_framerate; // framerate
t_int x_cursec; // current second t_int x_secondcount; // number of frames emitted in the current second
/* audio structures */ short x_audio_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for incoming audio */ short x_audio_enc_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for audio to be encoded */ uint8_t x_audio_out[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for encoded audio */ t_int x_audioin_position; // writing position for incoming audio t_int x_audio_per_frame; // number of audio samples to transmit for each frame
/* mpeg4ip data */ CLiveConfig *x_mp4Config; CRtpTransmitter *x_rtpTransmitter; CPDPVideoSource *x_videosource; CPDPAudioSource *x_audiosource;
} t_pdp_mp4live;
#ifdef __cplusplus extern "C" { #endif
static void pdp_mp4live_add_sink(t_pdp_mp4live *x, CMediaSink* pSink) { if (x->x_videosource) { x->x_videosource->AddSink(pSink); } if (x->x_audiosource) { x->x_audiosource->AddSink(pSink); } }
static void pdp_mp4live_remove_sink(t_pdp_mp4live *x, CMediaSink* pSink) { if (x->x_videosource) { x->x_videosource->RemoveSink(pSink); } if (x->x_audiosource) { x->x_audiosource->RemoveSink(pSink); } }
static void pdp_mp4live_disconnect(t_pdp_mp4live *x) { t_int ret, i;
if (!x->x_streaming) { post("pdp_mp4live~ : disconnect request but not connected ... ignored" ); return; }
if (x->x_audiosource) { x->x_audiosource->DoStop(); delete x->x_audiosource; x->x_audiosource = NULL; }
if (x->x_videosource) { x->x_videosource->DoStop(); delete x->x_videosource; x->x_videosource = NULL; }
if (x->x_rtpTransmitter) { pdp_mp4live_remove_sink(x, x->x_rtpTransmitter); x->x_rtpTransmitter->StopThread(); delete x->x_rtpTransmitter; x->x_rtpTransmitter = NULL; }
x->x_streaming = 0; outlet_float( x->x_outlet_streaming, x->x_streaming ); x->x_nbframes = 0; outlet_float( x->x_outlet_nbframes, x->x_nbframes ); x->x_framerate = 0; outlet_float( x->x_outlet_framerate, x->x_framerate ); }
static void pdp_mp4live_connect(t_pdp_mp4live *x ) { t_int ret, i;
if (x->x_streaming) { post("pdp_mp4live~ : connect request but already connected ... ignored" ); return; }
post("pdp_mp4live~ : creating video source"); if ( x->x_videosource == NULL ) { x->x_videosource = new CPDPVideoSource(); x->x_videosource->SetConfig(x->x_mp4Config); }
post("pdp_mp4live~ : creating audio source"); if ( x->x_audiosource == NULL ) { x->x_audiosource = new CPDPAudioSource(x->x_mp4Config); }
post("pdp_mp4live~ : creating rtp transmitter"); x->x_rtpTransmitter = new CRtpTransmitter(x->x_mp4Config); x->x_rtpTransmitter->StartThread();
post("pdp_mp4live~ : creating audio destination"); x->x_rtpTransmitter->CreateAudioRtpDestination(0, x->x_mp4Config->GetStringValue(CONFIG_RTP_AUDIO_DEST_ADDRESS), x->x_mp4Config->GetIntegerValue(CONFIG_RTP_AUDIO_DEST_PORT), 0);
post("pdp_mp4live~ : creating video destination"); x->x_rtpTransmitter->CreateVideoRtpDestination(0, x->x_mp4Config->GetStringValue(CONFIG_RTP_DEST_ADDRESS), x->x_mp4Config->GetIntegerValue(CONFIG_RTP_VIDEO_DEST_PORT), 0);
post("pdp_mp4live~ : starting rtp"); if ( x->x_rtpTransmitter ) { pdp_mp4live_add_sink(x, x->x_rtpTransmitter); x->x_rtpTransmitter->Start(); }
if (x->x_videosource) { post("pdp_mp4live~ : starting video source"); x->x_videosource->DoStart(); post("pdp_mp4live~ : generating key frame"); x->x_videosource->GenerateKeyFrame(); }
if (x->x_audiosource) { post("pdp_mp4live~ : starting audio source"); x->x_audiosource->DoStart(); }
x->x_streaming = 1; outlet_float( x->x_outlet_streaming, x->x_streaming ); x->x_nbframes = 0; outlet_float( x->x_outlet_nbframes, x->x_nbframes ); x->x_framerate = 0; outlet_float( x->x_outlet_framerate, x->x_framerate );
}
static void pdp_mp4live_ipaddr(t_pdp_mp4live *x, t_symbol *sIpAddr ) { t_int a, b, c, d;
if ( !strcmp( sIpAddr->s_name, "" ) ) { post("pdp_mp4live~ : wrong ip address" ); return; }
if ( sscanf( sIpAddr->s_name, "%d.%d.%d.%d", &a, &b, &c, &d ) < 4 ) { post("pdp_mp4live~ : wrong ip address : %s", sIpAddr->s_name ); return; }
post( "pdp_mp4live~ : setting ip address: %s", sIpAddr->s_name ); x->x_mp4Config->SetStringValue( CONFIG_RTP_DEST_ADDRESS, sIpAddr->s_name ); x->x_mp4Config->SetStringValue( CONFIG_RTP_AUDIO_DEST_ADDRESS, sIpAddr->s_name ); }
static void pdp_mp4live_aport(t_pdp_mp4live *x, t_floatarg fAudioPort ) { if ( ( (t_int) fAudioPort <= 0 ) || ( (t_int) fAudioPort > 65535 ) ) { post("pdp_mp4live~ : wrong audio port : %d", fAudioPort ); return; }
post( "pdp_mp4live~ : setting audio port: %d", (t_int) fAudioPort ); x->x_mp4Config->SetIntegerValue( CONFIG_RTP_AUDIO_DEST_PORT, (t_int) fAudioPort );
}
static void pdp_mp4live_vport(t_pdp_mp4live *x, t_floatarg fVideoPort ) { if ( ( (t_int) fVideoPort <= 0 ) || ( (t_int) fVideoPort > 65535 ) ) { post("pdp_mp4live~ : wrong video port : %d", fVideoPort ); return; }
post( "pdp_mp4live~ : setting video port: %d", (t_int) fVideoPort ); x->x_mp4Config->SetIntegerValue( CONFIG_RTP_VIDEO_DEST_PORT, (t_int) fVideoPort );
}
static void pdp_mp4live_ttl(t_pdp_mp4live *x, t_floatarg fTtl ) { if ( ( (t_int) fTtl <= 0 ) || ( (t_int) fTtl > 255 ) ) { post("pdp_mp4live~ : wrong ttl : %d", fTtl ); return; }
post( "pdp_mp4live~ : setting ttl : %d", (t_int) fTtl ); x->x_mp4Config->SetIntegerValue( CONFIG_RTP_MCAST_TTL, (t_int) fTtl );
}
static void pdp_mp4live_vwidth(t_pdp_mp4live *x, t_floatarg fWidth ) { if ( ( (t_int) fWidth <= 0 ) ) { post("pdp_mp4live~ : wrong width : %d", fWidth ); return; }
post( "pdp_mp4live~ : setting width : %d", (t_int) fWidth ); x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_RAW_WIDTH, (t_int) fWidth );
}
static void pdp_mp4live_vheight(t_pdp_mp4live *x, t_floatarg fHeight ) { if ( ( (t_int) fHeight <= 0 ) ) { post("pdp_mp4live~ : wrong height : %d", fHeight ); return; }
post( "pdp_mp4live~ : setting height : %d", (t_int) fHeight ); x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_RAW_HEIGHT, (t_int) fHeight );
}
static void pdp_mp4live_framerate(t_pdp_mp4live *x, t_floatarg fFrameRate ) { if ( ( (t_int) fFrameRate <= 0 ) ) { post("pdp_mp4live~ : wrong framerate : %d", fFrameRate ); return; }
post( "pdp_mp4live~ : setting framerate : %d", (t_int) fFrameRate ); x->x_mp4Config->SetFloatValue( CONFIG_VIDEO_FRAME_RATE, (t_float) fFrameRate );
}
static void pdp_mp4live_vbitrate(t_pdp_mp4live *x, t_floatarg fVBitrate ) { if ( ( (t_int) fVBitrate <= 0 ) ) { post("pdp_mp4live~ : wrong video bit rate : %d", fVBitrate ); return; }
post( "pdp_mp4live~ : setting video bit rate : %d", (t_int) fVBitrate ); x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_BIT_RATE, (t_int) fVBitrate );
}
static void pdp_mp4live_samplerate(t_pdp_mp4live *x, t_floatarg fSampleRate ) { if ( ( (t_int) fSampleRate != 44100 ) && ( (t_int) fSampleRate != 22050 ) && ( (t_int) fSampleRate != 11025 ) && ( (t_int) fSampleRate != 8000 ) ) { post("pdp_mp4live~ : wrong samplerate : %d", fSampleRate ); return; }
post( "pdp_mp4live~ : setting samplerate : %d", (t_int) fSampleRate ); x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_SAMPLE_RATE, (t_int) fSampleRate );
}
static void pdp_mp4live_abitrate(t_pdp_mp4live *x, t_floatarg fABitrate ) { if ( ( (t_int) fABitrate <= 0 ) ) { post("pdp_mp4live~ : wrong audio bit rate : %d", fABitrate ); return; }
post( "pdp_mp4live~ : setting audio bit rate : %d", (t_int) fABitrate ); x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_BIT_RATE_KBPS, (t_int) fABitrate ); x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_BIT_RATE, ((t_int) fABitrate)*1000 );
}
static void pdp_mp4live_sdp(t_pdp_mp4live *x, t_symbol *sSdpFile ) { t_int ret;
post( "pdp_mp4live~ : setting sdp filename : %s", (char *) sSdpFile->s_name ); x->x_mp4Config->SetStringValue( CONFIG_SDP_FILE_NAME, (char *) sSdpFile->s_name );
post( "pdp_mp4live~ : writing sdp file : %s", (char *) sSdpFile->s_name ); if ( ( ret = GenerateSdpFile( x->x_mp4Config ) ) ) { post( "pdp_mp4live~ : written sdp file : %s", (char *) sSdpFile->s_name ); } else { post( "pdp_mp4live~ : could not write sdp file : %s", (char *) sSdpFile->s_name ); } }
static void pdp_mp4live_process_yv12(t_pdp_mp4live *x) { t_pdp *header = pdp_packet_header(x->x_packet0); u_int8_t *data = (uint8_t *)pdp_packet_data(x->x_packet0); u_int8_t *pY, *pU, *pV; struct timeval etime;
/* allocate all ressources */ if ( ((int)header->info.image.width != x->x_vwidth) || ((int)header->info.image.height != x->x_vheight) ) { x->x_vwidth = header->info.image.width; x->x_vheight = header->info.image.height; x->x_vsize = x->x_vwidth*x->x_vheight; }
if ( x->x_streaming ) { pY = data; pU = data+x->x_vsize; pV = data+x->x_vsize+(x->x_vsize>>2);
x->x_videosource->ProcessVideo( pY, pV, pU );
/* update frames counter */
if ( gettimeofday(&etime, NULL) == -1) { post("pdp_ffmpeg~ : could not read time" ); } if ( etime.tv_sec != x->x_cursec ) { x->x_cursec = etime.tv_sec; x->x_framerate = x->x_secondcount; x->x_secondcount = 0; } x->x_nbframes++; x->x_secondcount++;
/* send an audio frame */ if ( x->x_audioin_position > x->x_audio_per_frame ) { x->x_audiosource->ProcessAudio( (u_int8_t*)x->x_audio_buf, (u_int32_t)x->x_audio_per_frame*sizeof(short) );
/* output resampled raw samples */ memcpy( x->x_audio_buf, x->x_audio_buf+x->x_audio_per_frame, x->x_audioin_position-x->x_audio_per_frame ); x->x_audioin_position-=x->x_audio_per_frame; } } return; }
static void pdp_mp4live_killpacket(t_pdp_mp4live *x) { /* delete source packet */ pdp_packet_mark_unused(x->x_packet0); x->x_packet0 = -1; }
/* store audio data in PCM format and stream it */ static t_int *pdp_mp4live_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_mp4live *x = (t_pdp_mp4live *)(w[3]); int n = (int)(w[4]); // number of samples t_float fsample; t_int isample, i;
// just fills the buffer ( a pcm buffer ) while (n--) { fsample=*(in1++); if (fsample > 1.0) { fsample = 1.0; } if (fsample < -1.0) { fsample = -1.0; } isample=(short) (32767.0 * fsample); *(x->x_audio_buf+x->x_audioin_position)=isample; x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE); if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 ) { // post( "pdp_mp4live~ : reaching end of audio buffer" ); } fsample=*(in2++); if (fsample > 1.0) { fsample = 1.0; } if (fsample < -1.0) { fsample = -1.0; } isample=(short) (32767.0 * fsample); *(x->x_audio_buf+x->x_audioin_position)=isample; x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE); if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 ) { // post( "pdp_mp4live~ : reaching end of audio buffer" ); } }
return (w+5); }
static void pdp_mp4live_dsp(t_pdp_mp4live *x, t_signal **sp) { dsp_add(pdp_mp4live_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); }
static void pdp_mp4live_process(t_pdp_mp4live *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_mp4live_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, (void*) pdp_mp4live_process_yv12, (void*) pdp_mp4live_killpacket, &x->x_queue_id); outlet_float( x->x_outlet_nbframes, x->x_nbframes ); outlet_float( x->x_outlet_framerate, x->x_framerate ); break;
default: /* don't know the type, so dont pdp_mp4live_process */ post( "pdp_mp4live~ : hey!! i don't know about that type of image : %d", pdp_packet_header(x->x_packet0)->info.image.encoding ); break; } }
}
static void pdp_mp4live_input_0(t_pdp_mp4live *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_mp4live_process(x); }
}
static void pdp_mp4live_free(t_pdp_mp4live *x) { int i;
pdp_queue_finish(x->x_queue_id); pdp_packet_mark_unused(x->x_packet0); }
t_class *pdp_mp4live_class;
void *pdp_mp4live_new(void) { int i;
t_pdp_mp4live *x = (t_pdp_mp4live *)pd_new(pdp_mp4live_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_framerate = outlet_new(&x->x_obj, &s_float);
x->x_packet0 = -1; x->x_queue_id = -1; x->x_nbframes = 0; x->x_framerate = 0; x->x_secondcount = 0; x->x_audioin_position = 0;
x->x_mp4Config = new CLiveConfig(PdpConfigVariables, sizeof(PdpConfigVariables) / sizeof(SConfigVariable), "none"); if ( x->x_mp4Config == NULL ) { post( "pdp_mp4live~ : couldn't allocate default config" ); return NULL; }
x->x_mp4Config->InitializeIndexes();
x->x_mp4Config->Update();
// update sample rate with the actual sample rate x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_SAMPLE_RATE, (t_int) sys_getsr() );
x->x_videosource = NULL; x->x_audiosource = NULL;
x->x_audio_per_frame = AUDIO_PACKET_SIZE;
return (void *)x; }
void pdp_mp4live_tilde_setup(void) { // post( pdp_mp4live_version ); pdp_mp4live_class = class_new(gensym("pdp_mp4live~"), (t_newmethod)pdp_mp4live_new, (t_method)pdp_mp4live_free, sizeof(t_pdp_mp4live), 0, A_NULL);
CLASS_MAINSIGNALIN(pdp_mp4live_class, t_pdp_mp4live, x_f ); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_dsp, gensym("dsp"), A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_connect, gensym("connect"), A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_ipaddr, gensym("ipaddr"), A_SYMBOL, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_aport, gensym("audioport"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vport, gensym("videoport"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_ttl, gensym("ttl"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vwidth, gensym("vwidth"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vheight, gensym("vheight"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_framerate, gensym("framerate"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vbitrate, gensym("vbitrate"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_samplerate, gensym("samplerate"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_abitrate, gensym("abitrate"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_disconnect, gensym("disconnect"), A_NULL); class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_sdp, gensym("sdp"), A_SYMBOL, A_NULL); class_sethelpsymbol( pdp_mp4live_class, gensym("pdp_mp4live~.pd") ); }
#ifdef __cplusplus } #endif
--- NEW FILE: pdp_mp4audiosync.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com * * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr) */
/* * audio.cpp provides an interface (CPDPAudioSync) between the codec and * the SDL audio APIs. */ #include <stdlib.h> #include <string.h> #include "pdp_mp4playersession.h" #include "pdp_mp4audiosync.h" #include "player_util.h" #include "our_config_file.h" #include "m_pd.h"
#define audio_message(loglevel, fmt...) message(loglevel, "audiosync", fmt)
static void pdp_audio_callback (void *userdata, Uint8 *stream, int len) { CPDPAudioSync *a = (CPDPAudioSync *)userdata; a->audio_callback(stream, len); }
CPDPAudioSync::CPDPAudioSync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) : CAudioSync(psptr) { m_fill_index = m_play_index = 0; for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { m_buffer_filled[ix] = 0; m_sample_buffer[ix] = NULL; } m_buffer_size = 0; m_config_set = 0; m_audio_initialized = 0; m_audio_paused = 1; m_resync_required = 0; m_dont_fill = 0; m_consec_no_buffers = 0; m_audio_waiting_buffer = 0; m_skipped_buffers = 0; m_didnt_fill_buffers = 0; m_play_time = 0 ; m_buffer_latency = 0; m_first_time = 1; m_first_filled = 1; m_buffer_offset_on = 0; m_buffer_ts = 0; m_load_audio_do_next_resync = 0; m_convert_buffer = NULL; m_father = pdp_father; }
CPDPAudioSync::~CPDPAudioSync (void) { for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { if (m_sample_buffer[ix] != NULL) free(m_sample_buffer[ix]); m_sample_buffer[ix] = NULL; } CHECK_AND_FREE(m_convert_buffer); audio_message(LOG_NOTICE, "Audio sync skipped %u buffers", m_skipped_buffers); audio_message(LOG_NOTICE, "didn't fill %u buffers", m_didnt_fill_buffers); }
void CPDPAudioSync::set_config (int freq, int channels, int format, uint32_t sample_size) { if (m_config_set != 0) return;
if (format == AUDIO_U8 || format == AUDIO_S8) m_bytes_per_sample = 1; else m_bytes_per_sample = 2;
if (sample_size == 0) { int temp; temp = freq; while ((temp & 0x1) == 0) temp >>= 1; sample_size = temp; while (sample_size < 1024) sample_size *= 2; while (((sample_size * 1000) % freq) != 0) sample_size *= 2; }
m_buffer_size = channels * sample_size * m_bytes_per_sample;
for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { m_buffer_filled[ix] = 0; m_sample_buffer[ix] = (uint8_t *)malloc(2 * m_buffer_size); } m_freq = freq; m_channels = channels; m_format = format; if (m_format == AUDIO_U8) { m_silence = 0x80; } else { m_silence = 0x00; } m_config_set = 1; m_msec_per_frame = (sample_size * 1000) / m_freq; audio_message(LOG_DEBUG, "buffer size %d msec per frame %d", m_buffer_size, m_msec_per_frame); };
uint8_t *CPDPAudioSync::get_audio_buffer (void) { int ret; int locked = 0; if (m_dont_fill == 1) { return (NULL); }
if (m_audio_initialized != 0) { locked = 1; } ret = m_buffer_filled[m_fill_index]; if (ret == 1) { m_audio_waiting_buffer = 1; m_audio_waiting_buffer = 0; if (m_dont_fill != 0) { return (NULL); } locked = 0; if (m_audio_initialized != 0) { locked = 1; } ret = m_buffer_filled[m_fill_index]; if (locked) if (ret == 1) { post("pdp_mp4audiosync : no buffer"); return (NULL); } } return (m_sample_buffer[m_fill_index]); }
void CPDPAudioSync::load_audio_buffer (uint8_t *from, uint32_t bytes, uint64_t ts, int resync) { uint8_t *to; uint32_t copied; copied = 0; if (m_buffer_offset_on == 0) { int64_t diff = ts - m_buffer_ts;
if (m_buffer_ts != 0 && diff > 1) { m_load_audio_do_next_resync = 1; audio_message(LOG_DEBUG, "timeslot doesn't match - %llu %llu", ts, m_buffer_ts); } m_buffer_ts = ts; } else { int64_t check; check = ts - m_loaded_next_ts; if (check > m_msec_per_frame) { audio_message(LOG_DEBUG, "potential resync at ts "U64" should be ts "U64, ts, m_loaded_next_ts); uint32_t left; left = m_buffer_size - m_buffer_offset_on; to = get_audio_buffer(); memset(to + m_buffer_offset_on, 0, left); filled_audio_buffer(m_buffer_ts, 0); m_buffer_offset_on = 0; m_load_audio_do_next_resync = 1; m_buffer_ts = ts; } } m_loaded_next_ts = bytes * M_64; m_loaded_next_ts /= m_bytes_per_sample; m_loaded_next_ts /= m_freq; m_loaded_next_ts += ts;
while ( bytes > 0) { to = get_audio_buffer(); if (to == NULL) { return; } int copy; uint32_t left;
left = m_buffer_size - m_buffer_offset_on; copy = MIN(left, bytes); memcpy(to + m_buffer_offset_on, from, copy); bytes -= copy; copied += copy; from += copy; m_buffer_offset_on += copy; if (m_buffer_offset_on >= m_buffer_size) { m_buffer_offset_on = 0; filled_audio_buffer(m_buffer_ts, resync | m_load_audio_do_next_resync); m_buffer_ts += m_msec_per_frame; resync = 0; m_load_audio_do_next_resync = 0; } } return; }
void CPDPAudioSync::filled_audio_buffer (uint64_t ts, int resync) { uint32_t fill_index; int locked; // m_dont_fill will be set when we have a pause if (m_dont_fill == 1) { return; } // resync = 0; fill_index = m_fill_index; m_fill_index++; m_fill_index %= DECODE_BUFFERS_MAX;
locked = 0; if (m_audio_initialized != 0) { locked = 1; } if (m_first_filled != 0) { m_first_filled = 0; resync = 0; m_resync_required = 0; } else { int64_t diff; diff = ts - m_last_fill_timestamp; if (diff - m_msec_per_frame > m_msec_per_frame) { // have a hole here - don't want to resync if (diff > ((m_msec_per_frame + 1) * 4)) { resync = 1; } else { // try to fill the holes m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra int64_t ts_diff; do { uint8_t *retbuffer; // Get and swap buffers. retbuffer = get_audio_buffer(); if (retbuffer == NULL) { return; } if (retbuffer != m_sample_buffer[m_fill_index]) { audio_message(LOG_ERR, "retbuffer not fill index in audio sync"); return; } locked = 0; if (m_audio_initialized != 0) { locked = 1; } m_sample_buffer[m_fill_index] = m_sample_buffer[fill_index]; m_sample_buffer[fill_index] = retbuffer; memset(retbuffer, m_silence, m_buffer_size); m_buffer_time[fill_index] = m_last_fill_timestamp; m_buffer_filled[fill_index] = 1; m_samples_loaded += m_buffer_size; fill_index++; fill_index %= DECODE_BUFFERS_MAX; m_fill_index++; m_fill_index %= DECODE_BUFFERS_MAX; audio_message(LOG_NOTICE, "Filling timestamp %llu with silence", m_last_fill_timestamp); m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra ts_diff = ts - m_last_fill_timestamp; audio_message(LOG_DEBUG, "diff is %lld", ts_diff); } while (ts_diff > 0); locked = 0; if (m_audio_initialized != 0) { locked = 1; } } } else { if (m_last_fill_timestamp == ts) { audio_message(LOG_NOTICE, "Repeat timestamp with audio %llu", ts); return; } } } m_last_fill_timestamp = ts; m_buffer_filled[fill_index] = 1; m_samples_loaded += m_buffer_size; m_buffer_time[fill_index] = ts; if (resync) { m_resync_required = 1; m_resync_buffer = fill_index; #ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "Resync from filled_audio_buffer"); #endif }
// Check this - we might not want to do this unless we're resyncing if (resync) m_psptr->wake_sync_thread(); #ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "Filling " LLU " %u %u", ts, fill_index, m_samples_loaded); #endif }
void CPDPAudioSync::set_eof(void) { uint8_t *to; if (m_buffer_offset_on != 0) { to = get_audio_buffer(); if (to != NULL) { uint32_t left; left = m_buffer_size - m_buffer_offset_on; memset(to + m_buffer_offset_on, 0, left); m_buffer_offset_on = 0; filled_audio_buffer(m_buffer_ts, 0); m_buffer_ts += m_msec_per_frame; } } CAudioSync::set_eof(); }
int CPDPAudioSync::initialize_audio (int have_video) { return (1); }
int CPDPAudioSync::is_audio_ready (uint64_t &disptime) { disptime = m_buffer_time[m_play_index]; return (m_dont_fill == 0 && m_buffer_filled[m_play_index] == 1); }
uint64_t CPDPAudioSync::check_audio_sync (uint64_t current_time, int &have_eof) { return (0); }
void CPDPAudioSync::audio_callback (Uint8 *stream, int ilen) { int freed_buffer = 0; uint32_t bufferBytes = (uint32_t)ilen; uint64_t this_time; int delay = 0; int playtime;
}
void CPDPAudioSync::play_audio (void) { m_first_time = 1; m_audio_paused = 0; m_play_sample_index = 0; }
void CPDPAudioSync::flush_sync_buffers (void) { clear_eof(); m_dont_fill = 1; if (m_audio_waiting_buffer) { m_audio_waiting_buffer = 0; } }
void CPDPAudioSync::flush_decode_buffers (void) { int locked = 0; if (m_audio_initialized != 0) { locked = 1; } m_dont_fill = 0; m_first_filled = 1; for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { m_buffer_filled[ix] = 0; } m_buffer_offset_on = 0; m_play_index = m_fill_index = 0; m_audio_paused = 1; m_resync_buffer = 0; m_samples_loaded = 0; }
void CPDPAudioSync::set_volume (int volume) { m_volume = (volume * SDL_MIX_MAXVOLUME)/100; }
void CPDPAudioSync::audio_convert_data (void *from, uint32_t samples) { if (m_obtained.format == AUDIO_U8 || m_obtained.format == AUDIO_S8) { // bytewise - easy int8_t *src, *dst; src = (int8_t *) from; dst = (int8_t *) m_convert_buffer; if (m_channels == 2) { // we got 1, wanted 2 for (uint32_t ix = 0; ix < samples; ix++) { int16_t sum = *src++; sum += *src++; sum /= 2; if (sum < -128) sum = -128; else if (sum > 128) sum = 128; *dst++ = sum & 0xff; } } else { // we got 2, wanted 1 for (uint32_t ix = 0; ix < samples; ix++) { *dst++ = *src; *dst++ = *src++; } } } else { int16_t *src, *dst; src = (int16_t *) from; dst = (int16_t *) m_convert_buffer; samples /= 2; if (m_channels == 1) { // 1 channel to 2 for (uint32_t ix = 0; ix < samples; ix++) { *dst++ = *src; *dst++ = *src; src++; } } else { // 2 channels to 1 for (uint32_t ix = 0; ix < samples; ix++) { int32_t sum = *src++; sum += *src++; sum /= 2; if (sum < -32768) sum = -32768; else if (sum > 32767) sum = 32767; *dst++ = sum & 0xffff; } }
} }
static void pdp_audio_config (void *ifptr, int freq, int chans, int format, uint32_t max_buffer_size) { ((CPDPAudioSync *)ifptr)->set_config(freq, chans, format, max_buffer_size); }
static uint8_t *pdp_get_audio_buffer (void *ifptr) { return ((CPDPAudioSync *)ifptr)->get_audio_buffer(); }
static void pdp_filled_audio_buffer (void *ifptr, uint64_t ts, int resync_req) { ((CPDPAudioSync *)ifptr)->filled_audio_buffer(ts, resync_req); }
static void pdp_load_audio_buffer (void *ifptr, uint8_t *from, uint32_t bytes, uint64_t ts, int resync) { ((CPDPAudioSync *)ifptr)->load_audio_buffer(from, bytes, ts, resync); }
audio_vft_t audio_vft = { message, pdp_audio_config, pdp_get_audio_buffer, pdp_filled_audio_buffer, pdp_load_audio_buffer };
audio_vft_t *get_audio_vft (void) { return &audio_vft; }
CPDPAudioSync *pdp_create_audio_sync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) { return new CPDPAudioSync(psptr, pdp_father); }
int do_we_have_audio (void) { return 1; }
--- NEW FILE: pdp_ieee1394.c --- /* * 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 ieee1394 video input object for OSX, using QuickTime * Some code is inspired by pix_video from Gem * Written by Yves Degoyon */
#include "pdp_config.h" #include "pdp.h" #include "pdp_llconv.h" #include "pdp_imageproc.h" #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <fcntl.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/time.h> #include <sys/mman.h> #include <sched.h> #include <pthread.h> #include <Carbon/Carbon.h> #include <Quicktime/QuickTime.h> #include <Quicktime/QuickTimeComponents.h>
#define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240
typedef struct pdp_ieee1394_struct { t_object x_obj; t_float x_f;
t_outlet *x_outlet0;
bool x_initialized; bool x_auto_open;
t_int x_packet; t_pdp* x_header; short int *x_data; unsigned char *x_sdata; // static data to hold the grabbed images
unsigned int x_width; unsigned int x_height; unsigned int x_size; int x_channel; pthread_t x_thread_id; int x_continue_thread; unsigned int x_framerate; int x_frame_ready; t_int x_quality;
SeqGrabComponent x_sg; SGChannel x_vc; short x_pixelDepth; Rect x_srcRect; GWorldPtr x_srcGWorld; PixMapHandle x_pixMap; Ptr x_baseAddr; long x_rowBytes;
} t_pdp_ieee1394;
static void pdp_ieee1394_close(t_pdp_ieee1394 *x) { void *dummy;
/* terminate thread if there is one */ if(x->x_continue_thread) { x->x_continue_thread = 0; // pthread_join (x->x_thread_id, &dummy); }
// free sequence grabber // if (x->x_vc) // { // if (SGDisposeChannel(x->x_sg, x->x_vc)) // { // post("pdp_ieee1394: unable to dispose video channel"); // } // x->x_vc = NULL; // post("pdp_ieee1394: disposed video channel"); // } // if (x->x_sg) // { // if (CloseComponent(x->x_sg)) // { // post("pdp_ieee1394: unable to free sequence grabber."); // } // x->x_sg = NULL; // post("pdp_ieee1394: freed sequence grabber."); // } // if (x->x_srcGWorld) // { // DisposeGWorld(x->x_srcGWorld); // post("pdp_ieee1394: disposed world."); // x->x_srcGWorld = NULL; // }
}
static void pdp_ieee1394_capture_frame(t_pdp_ieee1394* x) { OSErr err;
err = SGIdle(x->x_sg); if (err != noErr) { post("pdp_ieee1394: SGIdle failed."); x->x_frame_ready = 0; } else { x->x_frame_ready = 1; } }
static void *pdp_ieee1394_thread(void *voidx) { t_pdp_ieee1394 *x = ((t_pdp_ieee1394 *)voidx);
/* capture with a double buffering scheme */ while (true) { if (x->x_continue_thread) { /* schedule capture command for next frame */ pdp_ieee1394_capture_frame(x); } else { sleep(1); } }
x->x_thread_id = 0; return 0; }
static void pdp_ieee1394_reset(t_pdp_ieee1394 *x) { OSErr anErr;
if ( !x->x_initialized ) { post("pdp_ieee1394: trying to reset but the sequence grabber is not initialized"); return; }
post("pdp_ieee1394: resetting....");
switch (x->x_quality) { case 0: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayNormal); post("pdp_ieee1394: set sequence grabber to : normal quality"); break; case 1: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayHighQuality); post("pdp_ieee1394: set sequence grabber to : high quality"); break; case 2: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayFast); post("pdp_ieee1394: set sequence grabber to : fast quality"); break; case 3: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayAllData); post("pdp_ieee1394: set sequence grabber to : play all data"); break; }
post("pdp_ieee1394: done."); }
static void pdp_ieee1394_quality(t_pdp_ieee1394 *x, t_floatarg fquality) { if ( ( (t_int)fquality < 0 ) || ( (t_int)fquality > 3 ) ) { post("pdp_ieee1394: wrong quality %d", (t_int)fquality ); return; } else { x->x_quality = (t_int)fquality; } }
static void pdp_ieee1394_free(t_pdp_ieee1394 *x) { pdp_ieee1394_close(x); }
static t_int pdp_ieee1394_init_grabber(t_pdp_ieee1394 *x) { OSErr anErr; x->x_srcRect.top = 0; x->x_srcRect.left = 0; x->x_srcRect.bottom = x->x_height; x->x_srcRect.right = x->x_width;
x->x_sg = OpenDefaultComponent(SeqGrabComponentType, 0); if(x->x_sg==NULL) { post("pdp_ieee1394: could not open default component"); return -1; } else { post("pdp_ieee1394: opened default component"); }
anErr = SGInitialize(x->x_sg); if(anErr!=noErr) { post("pdp_ieee1394: could not initialize sequence grabber"); return -1; } else { post("pdp_ieee1394: initialized sequence grabber"); }
anErr = SGSetDataRef(x->x_sg, 0, 0, seqGrabDontMakeMovie); if (anErr != noErr) { post("pdp_ieee1394: couldn't set data ref"); return -1; } else { post("pdp_ieee1394: set data ref ok."); }
anErr = SGNewChannel(x->x_sg, VideoMediaType, &x->x_vc); if(anErr!=noErr) { post("pdp_ieee1394: could not create new sequence grabber channnel"); return -1; } else { post("pdp_ieee1394: created new sequence grabber channnel."); }
anErr = SGSetChannelBounds(x->x_vc, &x->x_srcRect); if(anErr!=noErr) { post("pdp_ieee1394: could not set sequence grabber ChannelBounds "); return -1; } else { post("pdp_ieee1394: set sequence grabber ChannelBounds"); }
anErr = SGSetChannelUsage(x->x_vc, seqGrabPreview); if(anErr!=noErr) { post("pdp_ieee1394: could not set sequence grabber ChannelUsage "); return -1; } else { post("pdp_ieee1394: set sequence grabber ChannelUsage"); }
switch (x->x_quality) { case 0: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayNormal); post("pdp_ieee1394: set sequence grabber to : normal quality"); break; case 1: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayHighQuality); post("pdp_ieee1394: set sequence grabber to : high quality"); break; case 2: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayFast); post("pdp_ieee1394: set sequence grabber to : fast quality"); break; case 3: anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayAllData); post("pdp_ieee1394: set sequence grabber to : play all data"); break; }
anErr = QTNewGWorldFromPtr (&x->x_srcGWorld, k422YpCbCr8CodecType, &x->x_srcRect, NULL, NULL, 0, x->x_sdata, x->x_width*2); if (anErr!= noErr) { post ("pdp_ieee1394: QTNewGWorldFromPtr returned %d", anErr); return -1; }
if (NULL == x->x_srcGWorld) { post ("pdp_ieee1394: could not allocate off screen"); return -1; } SGSetGWorld(x->x_sg,(CGrafPtr)x->x_srcGWorld, NULL); SGStartPreview(x->x_sg);
return 0; }
static void pdp_ieee1394_open(t_pdp_ieee1394 *x) {
x->x_initialized = true; x->x_continue_thread = 1;
/* create thread */ if ( x->x_thread_id == 0 ) { if ( pdp_ieee1394_init_grabber( x ) != 0 ) { post("pdp_ieee1394: grabber initialization failed"); return; } x->x_frame_ready = 0; pthread_create(&x->x_thread_id, 0, pdp_ieee1394_thread, x); } }
static void pdp_ieee1394_bang(t_pdp_ieee1394 *x) { unsigned char *pQ; short int *pY, *pU, *pV; t_int px, py;
if (!(x->x_continue_thread)) { post("pdp_ieee1394: not initialized.");
if (x->x_auto_open) { post("pdp_ieee1394: attempting auto open"); pdp_ieee1394_open(x); if (!(x->x_initialized)) { post("pdp_ieee1394: auto open failed"); return; } } else return; }
/* do nothing if there is no frame ready */ if (!x->x_frame_ready) return;
x->x_packet = pdp_packet_new_image_YCrCb(x->x_width, x->x_height); x->x_header = pdp_packet_header(x->x_packet);
if (!x->x_header) { post("pdp_ieee1394: FATAL: can't allocate packet"); return; }
x->x_data = (short int *) pdp_packet_data(x->x_packet); memset( x->x_data, 0x0, (x->x_size+(x->x_size>>1))<<1 ); pQ = x->x_sdata; pY = x->x_data; pV = x->x_data+x->x_size; pU = x->x_data+x->x_size+(x->x_size>>2); for ( py=0; py<(t_int)x->x_height; py++ ) { for ( px=0; px<(t_int)x->x_width; px++ ) { *(pY+py*x->x_width+px) = (*(pQ+1+2*(py*x->x_width+px)))<<7; if ( px%2 == 0 ) { *(pU+((py>>1)*(x->x_width>>1)+(px>>1))) = (*(pQ+2*(py*x->x_width+px))-128)<<8; } if ( px%2 == 1 ) { *(pV+((py>>1)*(x->x_width>>1)+(px>>1))) = (*(pQ+2*(py*x->x_width+px))-128)<<8; } } }
pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet);
x->x_frame_ready = 0; }
t_class *pdp_ieee1394_class;
void *pdp_ieee1394_new(t_floatarg fwidth, t_floatarg fheight) { t_pdp_ieee1394 *x = (t_pdp_ieee1394 *)pd_new(pdp_ieee1394_class);
x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); x->x_initialized = false;
x->x_auto_open = true;
x->x_continue_thread = 0;
if (fwidth > 0.) { x->x_width = (int)fwidth; } else { x->x_width = DEFAULT_WIDTH; }
if (fheight > 0.) { x->x_height = (int)fheight; } else { x->x_height = DEFAULT_WIDTH; } x->x_size = x->x_width*x->x_height; x->x_sdata = (unsigned char*) getbytes( (x->x_size+(x->x_size>>1))<<1 ); if ( !x->x_sdata ) { post ("pdp_ieee1394: FATAL : couldn't allocate static data."); return NULL; }
x->x_quality = 1; x->x_thread_id = 0;
return (void *)x; }
#ifdef __cplusplus extern "C" { #endif
void pdp_ieee1394_setup(void) { pdp_ieee1394_class = class_new(gensym("pdp_ieee1394"), (t_newmethod)pdp_ieee1394_new, (t_method)pdp_ieee1394_free, sizeof(t_pdp_ieee1394), 0, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_bang, gensym("bang"), A_NULL); class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_close, gensym("close"), A_NULL); class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_open, gensym("open"), A_NULL); class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_reset, gensym("reset"), A_NULL); class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_quality, gensym("quality"), A_DEFFLOAT, A_NULL); }
#ifdef __cplusplus } #endif
--- NEW FILE: pdp_mp4audiosource.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000-2002. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com * Bill May wmay@cisco.com * * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr) */
#include "m_pd.h"
#ifndef debug_message #define debug_message post #endif #include "pdp_mp4audiosource.h"
//#define DEBUG_TIMESTAMPS 1
CPDPAudioSource::CPDPAudioSource(CLiveConfig *pConfig) : CMediaSource() { SetConfig(pConfig);
m_pcmFrameBuffer = NULL; m_prevTimestamp = 0; m_timestampOverflowArray = NULL; m_timestampOverflowArrayIndex = 0; m_audioOssMaxBufferSize = 0; }
int CPDPAudioSource::ThreadMain(void) { // just a stub, we don't use the threaded mode return 0; }
void CPDPAudioSource::DoStart() { if (m_source) { return; }
if (!Init()) { return; }
m_source = true; }
void CPDPAudioSource::DoStop() { if (!m_source) { return; }
CMediaSource::DoStopAudio();
CHECK_AND_FREE(m_timestampOverflowArray); free(m_pcmFrameBuffer); m_pcmFrameBuffer = NULL;
m_source = false; }
bool CPDPAudioSource::Init(void) { bool rc = InitAudio(true); if (!rc) { return false; }
m_channelsConfigured = m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS);
debug_message("pdp_mp4live~ : init audio : (m_audioDstChannels=%d m_audioDstSampleRate=%d )", m_audioDstChannels, m_audioDstSampleRate);
rc = SetAudioSrc(PCMAUDIOFRAME, m_channelsConfigured, m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE));
if (!rc) { return false; }
debug_message("pdp_mp4live~ : set audio src : (m_audioDstSamplesPerFrame=%d m_audioSrcChannels=%d)", m_audioDstSamplesPerFrame, m_audioSrcChannels);
// for live capture we can match the source to the destination m_audioSrcSamplesPerFrame = m_audioDstSamplesPerFrame; m_pcmFrameSize = m_audioSrcSamplesPerFrame * m_audioSrcChannels * sizeof(u_int16_t);
if (m_audioOssMaxBufferSize > 0) { size_t array_size; m_audioOssMaxBufferFrames = m_audioOssMaxBufferSize / m_pcmFrameSize; array_size = m_audioOssMaxBufferFrames * sizeof(*m_timestampOverflowArray); m_timestampOverflowArray = (Timestamp *)malloc(array_size); memset(m_timestampOverflowArray, 0, array_size); }
m_pcmFrameBuffer = (u_int8_t*)malloc(m_pcmFrameSize); if (!m_pcmFrameBuffer) { goto init_failure; }
// maximum number of passes in ProcessAudio, approx 1 sec. m_maxPasses = m_audioSrcSampleRate / m_audioSrcSamplesPerFrame;
debug_message("pdp_mp4live~ : audio source initialization done : ( frame size=%d )", m_pcmFrameSize );
return true;
init_failure: debug_message("pdp_mp4live~ : audio initialization failed");
free(m_pcmFrameBuffer); m_pcmFrameBuffer = NULL;
return false; }
void CPDPAudioSource::ProcessAudio(u_int8_t* pcmBuffer, u_int32_t pcmBufferSize) { audio_buf_info info; Timestamp currentTime = GetTimestamp(); Timestamp timestamp;
if ( pcmBufferSize > m_pcmFrameSize ) { debug_message( "pdp_mp4live~ : too many audio samples : %d should be %d", pcmBufferSize, m_pcmFrameSize ); memcpy( m_pcmFrameBuffer, pcmBuffer, m_pcmFrameSize ); } else if ( pcmBufferSize < m_pcmFrameSize ) { debug_message( "pdp_mp4live~ : too little audio samples : %d should be %d", pcmBufferSize, m_pcmFrameSize ); memcpy( m_pcmFrameBuffer, pcmBuffer, pcmBufferSize ); } else { memcpy( m_pcmFrameBuffer, pcmBuffer, pcmBufferSize ); }
if (info.bytes == m_audioOssMaxBufferSize) { // means the audio buffer is full, and not capturing // we want to make the timestamp based on the previous one // When we hit this case, we start using the m_timestampOverflowArray // This will give us a timestamp for when the array is full. // // In other words, if we have a full audio buffer (ie: it's not loading // any more), we start storing the current timestamp into the array. // This will let us "catch up", and have a somewhat accurate timestamp // when we loop around // // wmay - I'm not convinced that this actually works - if the buffer // cleans up, we'll ignore m_timestampOverflowArray if (m_timestampOverflowArray != NULL && m_timestampOverflowArray[m_timestampOverflowArrayIndex] != 0) { timestamp = m_timestampOverflowArray[m_timestampOverflowArrayIndex]; } else { timestamp = m_prevTimestamp + SrcSamplesToTicks(m_audioSrcSamplesPerFrame); }
if (m_timestampOverflowArray != NULL) m_timestampOverflowArray[m_timestampOverflowArrayIndex] = currentTime;
debug_message("pdp_mp4live~ : audio buffer full !");
} else { // buffer is not full - so, we make the timestamp based on the number // of bytes in the buffer that we read. timestamp = currentTime - SrcSamplesToTicks(SrcBytesToSamples(info.bytes)); if (m_timestampOverflowArray != NULL) m_timestampOverflowArray[m_timestampOverflowArrayIndex] = 0; }
#ifdef DEBUG_TIMESTAMPS debug_message("pdp_mp4live~ : info.bytes=%d t=%llu timestamp=%llu delta=%llu", info.bytes, currentTime, timestamp, timestamp - m_prevTimestamp); #endif
m_prevTimestamp = timestamp; if (m_timestampOverflowArray != NULL) { m_timestampOverflowArrayIndex = (m_timestampOverflowArrayIndex + 1) % m_audioOssMaxBufferFrames; }
ProcessAudioFrame(m_pcmFrameBuffer, m_pcmFrameSize, timestamp, false); }
--- NEW FILE: pdp_mp4playermedia.cpp --- /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com [...1990 lines suppressed...] double secs; secs = last_div; secs /= 1000.0; secs += total_secs;
fps = frames_decoded; fps /= secs; bps = UINT64_TO_DOUBLE(bytes_decoded); bps *= 8.0 / secs; media_message(LOG_NOTICE, "%s - bytes "U64", seconds %g, fps %g bps "U64, m_is_video ? "video" : "audio", bytes_decoded, secs, fps, bytes_decoded * 8 / total_secs); } if (m_plugin) { m_plugin->c_close(m_plugin_data); m_plugin_data = NULL; } return (0); }