// Crude way of automatically connecting up jack ports static void autoconnect_jack_ports( jack_client_t* client, int port_count ) { const char **all_ports; int ch=0, err = 0; int i; // Get a list of all the jack ports all_ports = jack_get_ports(client, NULL, NULL, JackPortIsOutput); if (!all_ports) { MAST_FATAL("autoconnect_jack_ports(): jack_get_ports() returned NULL"); } // Step through each port name for (i = 0; all_ports[i]; ++i) { const char* local_port = jack_port_name( g_jackport[ch] ); // Connect the port MAST_INFO("Connecting '%s' => '%s'", all_ports[i], local_port); err = jack_connect(client, all_ports[i], local_port); if (err != 0) MAST_FATAL("connect_jack_port(): failed to jack_connect() ports: %d", err); // Found enough ports ? if (++ch >= port_count) break; } free( all_ports ); }
/* Callback called by JACK when audio is available Use as little CPU time as possible, just copy accross the audio into the ring buffer */ static int process_callback(jack_nframes_t nframes, void *arg) { MastSendTool* tool = (MastSendTool*)arg; unsigned int channels = tool->get_input_channels(); size_t to_write = 0, written = 0; unsigned int c,n; // Process channel by channel for (c=0; c < channels; c++) { jack_default_audio_sample_t *buf = (jack_default_audio_sample_t*) jack_port_get_buffer(g_jackport[c], nframes); // Interleave the left and right channels for(n=0; n<nframes; n++) { g_interleavebuf[(n*channels)+c] = buf[n]; } } // Now write the interleaved audio to the ring buffer to_write = sizeof(float) * nframes * channels; written = jack_ringbuffer_write(g_ringbuffer, (char*)g_interleavebuf, to_write); if (to_write > written) { // If this goes wrong, then the buffer goes out of sync and we get static MAST_FATAL("Failed to write to ring ruffer, try increading the ring-buffer size"); return 1; } // Signal the other thread that audio is available pthread_cond_signal(&g_ringbuffer_cond); // Success return 0; }
/* mast_fill_input_buffer() Make sure input buffer if full of audio */ size_t mast_fill_input_buffer( MastAudioBuffer* buffer ) { int frames_wanted = buffer->get_write_space(); size_t bytes_wanted = frames_wanted * buffer->get_channels() * sizeof( mast_sample_t ); size_t bytes_read = 0, frames_read = 0; // Keep checking that there is enough audio available while (jack_ringbuffer_read_space(g_ringbuffer) < bytes_wanted) { MAST_WARNING( "Not enough audio available in ringbuffer; waiting" ); //MAST_DEBUG("Ring buffer is %u%% full", (jack_ringbuffer_read_space(g_ringbuffer)*100) / g_ringbuffer->size); // Wait for some more audio to become available pthread_mutex_lock(&g_ringbuffer_cond_mutex); pthread_cond_wait(&g_ringbuffer_cond, &g_ringbuffer_cond_mutex); pthread_mutex_unlock(&g_ringbuffer_cond_mutex); } // Copy frames from ring buffer to temporary buffer bytes_read = jack_ringbuffer_read(g_ringbuffer, (char*)buffer->get_write_ptr(), bytes_wanted); if (bytes_read<=0) MAST_FATAL( "Failed to read from ringbuffer" ); if (bytes_read!=bytes_wanted) MAST_WARNING("Failed to read enough audio for a full packet"); // Mark the space in the buffer as used frames_read = bytes_read / (buffer->get_channels() * sizeof( mast_sample_t )); buffer->add_frames( frames_read ); // Return the number return frames_read; }
// Initialise the codec MastCodec_MPA::MastCodec_MPA( MastMimeType *type) : MastCodec(type) { // Set default values this->samplerate = MPA_DEFAULT_SAMPLERATE; this->channels = MPA_DEFAULT_CHANNELS; // Initialise twolame this->twolame = twolame_init(); if (this->twolame==NULL) { MAST_FATAL( "Failed to initialise TwoLame" ); } // Configure twolame if (twolame_set_num_channels( this->twolame, this->channels )) { MAST_WARNING( "Failed to set number of input channels" ); } if (twolame_set_in_samplerate( this->twolame, this->samplerate )) { MAST_WARNING( "Failed to set number of input samplerate" ); } // Apply MIME type parameters to the codec this->apply_mime_type_params( type ); // Get TwoLAME ready to go... twolame_init_params( this->twolame ); }
void MastTool::set_multicast_ttl( const char* ttl_str ) { int ttl_int = atoi( ttl_str ); if (rtp_session_set_multicast_ttl( session, ttl_int )) { MAST_FATAL("Failed to set multicast TTL"); } }
void MastTool::set_session_dscp( const char* dscp_str ) { int dscp_int = this->parse_dscp( dscp_str ); if (rtp_session_set_dscp( this->get_session(), dscp_int )) { MAST_FATAL("Failed to set DSCP value"); } }
void MastTool::set_session_address( const char* in_addr ) { char* address = strdup( in_addr ); char* portstr = NULL; int port = DEFAULT_RTP_PORT; // Look for port in the address portstr = strchr(address, '/'); if (portstr && strlen(portstr)>1) { *portstr = 0; portstr++; port = atoi(portstr); } // Make sure the port number is even if (port%2 == 1) port--; // Send of recieve address? if (this->session->mode == RTP_SESSION_RECVONLY) { // Set the local address/port if (rtp_session_set_local_addr( session, address, port )) { MAST_FATAL("Failed to set local address/port (%s/%d)", address, port); } else { MAST_INFO( "Local address: %s/%d", address, port ); } } else if (this->session->mode == RTP_SESSION_SENDONLY) { // Set the remote address/port if (rtp_session_set_remote_addr( session, address, port )) { MAST_FATAL("Failed to set remote address/port (%s/%d)", address, port); } else { MAST_INFO( "Remote address: %s/%d", address, port ); } } else { MAST_FATAL("Mode unsupported by MastTool: %d", this->session->mode); } free( address ); }
void MastSendTool::prepare() { // Display some information about the chosen payload type MAST_INFO( "Sending SSRC: 0x%x", session->snd.ssrc ); MAST_INFO( "Input Format: %d Hz, %s", samplerate, channels==2 ? "Stereo" : "Mono"); mimetype->print(); // Load the codec if (codec != NULL) MAST_WARNING("Codec has already been created" ); codec = MastCodec::new_codec( mimetype ); if (codec == NULL) MAST_FATAL("Failed to get create codec" ); MAST_INFO( "Output Format: %d Hz, %s", codec->get_samplerate(), codec->get_channels()==2 ? "Stereo" : "Mono"); // Work out the payload type to use if (strcmp("MPA", codec->get_type())==0) { // MPEG Audio is a special case this->set_payloadtype_index( RTP_MPEG_AUDIO_PT ); payloadtype->channels = codec->get_channels(); } else { // Ask oRTP for the index int index = ::rtp_profile_find_payload_number( profile, codec->get_type(), codec->get_samplerate(), codec->get_channels() ); if ( index<0 ) MAST_FATAL("Failed to get payload type information from oRTP"); this->set_payloadtype_index( index ); } // Calculate the packet size frames_per_packet = codec->frames_per_packet( payload_size_limit ); if (frames_per_packet<=0) MAST_FATAL( "Invalid number of samples per packet" ); // Create audio buffers input_buffer = new MastAudioBuffer( frames_per_packet, samplerate, channels ); if (input_buffer == NULL) MAST_FATAL("Failed to create audio input buffer"); //resampled_buffer = new MastAudioBuffer( frames_per_packet, codec->get_samplerate(), codec->get_channels() ); //if (resampled_buffer == NULL) MAST_FATAL("Failed to create resampled audio buffer"); // Allocate memory for the packet buffer payload_buffer = (u_int8_t*)malloc( payload_size_limit ); if (payload_buffer == NULL) MAST_FATAL("Failed to allocate memory for payload buffer"); }
// Constructors MastTool::MastTool( const char* tool_name, RtpSessionMode mode ) { int log_level = ORTP_WARNING|ORTP_ERROR|ORTP_FATAL; // Initialise defaults this->session = NULL; this->profile = &av_profile; this->mimetype = new MastMimeType(); this->payloadtype = NULL; this->payloadtype_index = -1; this->tool_name = tool_name; this->payload_size_limit = DEFAULT_PAYLOAD_LIMIT; // Initialise the oRTP library ortp_init(); // Set the logging message level #ifdef DEBUGGING MAST_DEBUG( "Compiled with debugging enabled" ); log_level |= ORTP_DEBUG; log_level |= ORTP_MESSAGE; #endif ortp_set_log_level_mask(log_level); // Create RTP session session = rtp_session_new( mode ); if (session==NULL) { MAST_FATAL( "Failed to create oRTP session.\n" ); } // Enabled multicast loopback rtp_session_set_multicast_loopback(session, TRUE); // Callbacks rtp_session_signal_connect(session,"ssrc_changed",(RtpCallback)ssrc_changed_cb, 0); rtp_session_signal_connect(session,"payload_type_changed",(RtpCallback)pt_changed_cb, 0); rtp_session_signal_connect(session,"network_error",(RtpCallback)network_error_cb, 0); // Set the MPEG Audio payload type to 14 in the AV profile rtp_profile_set_payload(profile, RTP_MPEG_AUDIO_PT, &payload_type_mpeg_audio); // Set RTCP parameters this->set_source_sdes(); }
void MastTool::set_payloadtype_index( int idx ) { // Lookup the payload type PayloadType* pt = rtp_profile_get_payload( profile, idx ); if (pt==NULL) MAST_WARNING("Failed to get payload type for index %d", idx); this->payloadtype = pt; // Store it this->payloadtype_index = idx; MAST_INFO( "Payload type index: %d", idx ); // Apply it to the session if (rtp_session_set_send_payload_type( session, idx )) { MAST_FATAL("Failed to set session payload type index"); } }
// Callback called by JACK when buffersize changes static int buffersize_callback(jack_nframes_t nframes, void *arg) { MastSendTool* tool = (MastSendTool*)arg; int channels = tool->get_input_channels(); MAST_DEBUG("JACK buffer size is %d samples long", nframes); // (re-)allocate conversion buffer g_interleavebuf = (jack_default_audio_sample_t*) realloc( g_interleavebuf, nframes * sizeof(float) * channels ); if (g_interleavebuf == NULL) { MAST_FATAL("Failed to (re-)allocate the convertion buffer"); } // Success return 0; }
static void parse_cmd_line(int argc, char **argv, RtpSession* session) { char* local_address = NULL; int local_port = DEFAULT_RTP_PORT; int ch; // Parse the options/switches while ((ch = getopt(argc, argv, "h?")) != -1) switch (ch) { // case 'T': FIXME: timeout? case '?': case 'h': default: usage(); } // Parse the ip address and port if (argc > optind) { local_address = argv[optind]; optind++; // Look for port in the address char* portstr = strchr(local_address, '/'); if (portstr && strlen(portstr)>1) { *portstr = 0; portstr++; local_port = atoi(portstr); } } else { MAST_ERROR("missing address/port to receive from"); usage(); } // Make sure the port number is even if (local_port%2 == 1) local_port--; // Set the remote address/port if (rtp_session_set_local_addr( session, local_address, local_port )) { MAST_FATAL("Failed to set receive address/port (%s/%u)", local_address, local_port); } else { printf( "Receive address: %s/%u\n", local_address, local_port ); } }
int main(int argc, char **argv) { MastSendTool *tool = NULL; SF_INFO sfinfo; // Create the send tool object tool = new MastSendTool( MAST_TOOL_NAME ); tool->enable_scheduling(); // Parse the command line arguments // and configure the session parse_cmd_line( argc, argv, tool ); // Open the input file by filename memset( &sfinfo, 0, sizeof(sfinfo) ); g_input_file = sf_open(g_filename, SFM_READ, &sfinfo); if (g_input_file == NULL) MAST_FATAL("Failed to open input file:\n%s", sf_strerror(NULL)); tool->set_input_channels( sfinfo.channels ); tool->set_input_samplerate( sfinfo.samplerate ); // Display some information about the input file print_file_info( g_input_file, &sfinfo ); // Setup signal handlers mast_setup_signals(); // Run the main loop tool->run(); // Clean up delete tool; // Close input file if (sf_close( g_input_file )) { MAST_ERROR("Failed to close input file:\n%s", sf_strerror(g_input_file)); } // Success ! return 0; }
static FILE* open_output_file( char* filename ) { FILE* output = NULL; // Open the output file if (strcmp(filename, "-")==0) { fprintf(stderr, "Output file: STDOUT\n"); output = stdout; } else { fprintf(stderr, "Output file: %s\n", filename); output = fopen( filename, "wb" ); } // Check pointer isn't NULL if (output==NULL) { MAST_FATAL( "failed to open output file: %s", strerror(errno) ); } return output; }
int main(int argc, char **argv) { MastSendTool *tool = NULL; jack_client_t* client = NULL; // Create the send tool object tool = new MastSendTool( MAST_TOOL_NAME ); // Parse the command line arguments // and configure the session parse_cmd_line( argc, argv, tool ); // Initialise Jack client = init_jack( tool ); if (client==NULL) MAST_FATAL( "Failed to initialise JACK client" ); // Get the samplerate of the JACK Router tool->set_input_samplerate( jack_get_sample_rate( client ) ); // Setup signal handlers mast_setup_signals(); // Run the main loop tool->run(); // Clean up delete tool; // Shut down JACK deinit_jack( client ); // Success ! return 0; }
int main(int argc, char **argv) { MastTool* tool = NULL; mblk_t* packet = NULL; mblk_t* body = NULL; PayloadType* pt = NULL; int payload_size = 0; // Create an RTP session tool = new MastTool( MAST_TOOL_NAME, RTP_SESSION_RECVONLY ); if (tool==NULL) return -1; // Parse the command line arguments // and configure the session parse_cmd_line( argc, argv, tool->get_session() ); // Setup signal handlers mast_setup_signals(); // Recieve a single packet packet = tool->wait_for_rtp_packet(); if (packet == NULL) MAST_FATAL("Failed to receive a packet"); body = packet->b_cont; payload_size = (body->b_wptr - body->b_rptr); // Display information about the packet received printf("\n"); printf("RTP Header\n"); printf("==========\n"); printf("Payload type : %u\n", rtp_get_payload_type( packet ) ); printf("Payload size : %u bytes\n", payload_size ); printf("Sequence Number : %u\n", rtp_get_seqnumber( packet ) ); printf("Timestamp : %u\n", rtp_get_timestamp( packet ) ); printf("SSRC Identifier : %x\n", rtp_get_ssrc( packet ) ); printf("Marker Bit : %s\n", rtp_get_markbit( packet ) ? "Set" : "Not Set"); printf("\n"); // Lookup the payload type pt = rtp_profile_get_payload( tool->get_profile(), rtp_get_payload_type( packet ) ); if (pt == NULL) { MAST_WARNING( "Payload type %u isn't registered with oRTP", rtp_get_payload_type( packet ) ); } else { const char* mime_major = "?"; printf("Payload Details\n"); printf("===============\n"); if (pt->type==PAYLOAD_AUDIO_CONTINUOUS) mime_major = "audio"; else if (pt->type==PAYLOAD_AUDIO_PACKETIZED) mime_major = "audio"; else if (pt->type==PAYLOAD_VIDEO) mime_major = "video"; printf("Mime Type : %s/%s\n", mime_major, pt->mime_type); if (pt->clock_rate) printf("Clock Rate : %u Hz\n", pt->clock_rate); if (pt->channels) printf("Channels : %u\n", pt->channels); if (pt->bits_per_sample) printf("Bits per Sample : %u\n", pt->bits_per_sample); if (pt->normal_bitrate) { printf("Normal Bitrate : %u kbps\n", (pt->normal_bitrate/1000)); printf("Packet duration : %u ms\n", (payload_size*1000)/(pt->normal_bitrate/8) ); } if (pt->recv_fmtp) printf("Recieve FMTP : %s\n", pt->recv_fmtp); if (pt->send_fmtp) printf("Send FMTP : %s\n", pt->send_fmtp); printf("\n"); } // Parse the MPEG Audio header if (rtp_get_payload_type( packet ) == RTP_MPEG_AUDIO_PT) { /* FIXME: check fragment offset header (see rfc2250) */ unsigned char* mpa_ptr = body->b_rptr + 4; MPA_Header mh; printf("MPEG Audio Header\n"); printf("=================\n"); if (!mh.parse( mpa_ptr )) { MAST_WARNING("Failed to parse MPEG Audio header"); } else { mh.debug( stdout ); } } // Close RTP session delete tool; // Success ! return 0; }
// Initialise Jack related stuff static jack_client_t* init_jack( MastSendTool* tool ) { const char* client_name = tool->get_tool_name(); jack_client_t* client = NULL; jack_status_t status; size_t ringbuffer_size = 0; int port_count = tool->get_input_channels(); int i = 0; pthread_mutex_init(&g_ringbuffer_cond_mutex, NULL); pthread_cond_init(&g_ringbuffer_cond, NULL); // Register with Jack if ((client = jack_client_open(client_name, g_client_opt, &status)) == 0) { MAST_ERROR("Failed to start jack client: 0x%x", status); return NULL; } else { MAST_INFO( "JACK client registered as '%s'", jack_get_client_name( client ) ); } // Create our input port(s) if (port_count==1) { if (!(g_jackport[0] = jack_port_register(client, "mono", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))) { MAST_ERROR("Cannot register mono input port"); return NULL; } } else { if (!(g_jackport[0] = jack_port_register(client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))) { MAST_ERROR("Cannot register left input port"); return NULL; } if (!(g_jackport[1] = jack_port_register(client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))) { MAST_ERROR( "Cannot register left input port"); return NULL; } } // Create ring buffer ringbuffer_size = jack_get_sample_rate( client ) * g_rb_duration * port_count * sizeof(int16_t) / 1000; MAST_INFO("Duration of the ring buffer is %d ms (%d bytes)", g_rb_duration, (int)ringbuffer_size ); if (!(g_ringbuffer = jack_ringbuffer_create( ringbuffer_size ))) { MAST_ERROR("Cannot create ringbuffer %d", i); return NULL; } // Register callbacks jack_on_shutdown(client, shutdown_callback, tool ); jack_set_buffer_size_callback( client, buffersize_callback, tool); jack_set_process_callback(client, process_callback, tool); // Create conversion buffer buffersize_callback( jack_get_buffer_size(client), tool ); // Activate JACK if (jack_activate(client)) MAST_FATAL("Cannot activate JACK client"); /* Auto connect ports ? */ if (g_do_autoconnect) autoconnect_jack_ports( client, tool->get_input_channels() ); return client; }
int main(int argc, char **argv) { MastTool* tool = NULL; RtpProfile* profile = &av_profile; PayloadType* pt = NULL; FILE* output = NULL; mblk_t* packet = NULL; int ts_diff = 0; int ts = 0; // Create an RTP session tool = new MastTool( MAST_TOOL_NAME, RTP_SESSION_RECVONLY ); tool->enable_scheduling(); // Parse the command line arguments // and configure the session parse_cmd_line( argc, argv, tool ); // Recieve an initial packet packet = tool->wait_for_rtp_packet(); if (packet == NULL) MAST_FATAL("Failed to receive an initial packet"); // Lookup the payload type pt = rtp_profile_get_payload( profile, rtp_get_payload_type( packet ) ); if (pt == NULL) MAST_FATAL( "Payload type %d isn't registered with oRTP", rtp_get_payload_type( packet ) ); fprintf(stderr, "Payload type: %s\n", payload_type_get_rtpmap( pt )); // Work out the duration of the packet ts_diff = mast_rtp_packet_duration( packet ); MAST_DEBUG("ts_diff = %d", ts_diff); // Open the output file output = open_output_file( g_filename ); if (output==NULL) MAST_FATAL( "failed to open output file" ); // We can free the packet now freemsg( packet ); // Setup signal handlers mast_setup_signals(); // The main loop while( mast_still_running() ) { // Read in a packet packet = rtp_session_recvm_with_ts( tool->get_session(), ts ); if (packet==NULL) { MAST_DEBUG( "packet is NULL" ); } else { int data_len = mast_rtp_packet_size( packet ); if (data_len==0) { MAST_WARNING("Failed to get size of packet's payload"); } else { unsigned char* data_ptr = packet->b_cont->b_rptr; int bytes_written = 0; // Skip the extra header for MPA payload if (rtp_get_payload_type( packet ) == RTP_MPEG_AUDIO_PT) { data_ptr += 4; data_len -= 4; } // Update the timestamp difference ts_diff = mast_rtp_packet_duration( packet ); MAST_DEBUG("ts_diff = %d", ts_diff); MAST_DEBUG("data_len = %d", data_len); // Write to disk bytes_written = fwrite( data_ptr, 1, data_len, output ); if (bytes_written != data_len) { MAST_ERROR("Failed to write data to disk: %s", strerror(errno) ); break; } } } // Increment the timestamp for the next packet ts += ts_diff; } // Close output file if (fclose( output )) { MAST_ERROR("Failed to close output file:\n%s", strerror(errno)); } // Close RTP session delete tool; // Success return 0; }