Increasing microphone gain programmatically - c++

I'm trying to increase the gain of my microphone for VOIP scenarios.
I'm using PortAudio to acquire an input stream (with samples of type paFloat32), I'm multiplying these values by a float, then passing the resultant stream to an output device.
Note: I'm passing it to a virtual output device that automatically redirects to a virtual input device (program: VB-Cable), which VOIP applications can use as the microphone input with gain applied.
I'm wondering if there are better ways to increase gain of a signal that maintain quality better.
I've read that it's better to perform such gain calculations by first converting the input to a higher precision format, performing the gain multiplication in this format, apply clipping, then cast back down to the original.
I'm not sure how to do this with PortAudio's paFloat32 type, I've included my attempt commented out in the source code. When I enable it there is notable noise issues even with gain set to 1.
Dependencies: tinycon, PortAudio
Compiling: g++ main.cpp tinycon.cpp -o main -L./ -lcygportaudio-2 -lrt -lm -pthread -std=c++11
Code:
#include "portaudio.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include "tinycon.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (441)
#define DITHER_FLAG (1)
#define PA_SAMPLE_TYPE paFloat32
#define SAMPLE_SIZE (4)
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%f"
/*******************************************************************/
double multiplier = 1.0;
double multiplierStep = 0.1;
int main(int argc, char **argv);
int xrun(PaStream *stream, int err, char* sampleBlock);
void error1(PaStream *stream, char* sampleBlock);
void error2(PaStream *stream, int err);
void listDevices();
// Use tinycon and a second thread for non blocking input
class tcon : public tinyConsole
{
public:
tcon (std::string s): tinyConsole(s) {;}
int hotkeys(char c)
{
if (c == 's') {
if (multiplier >= (0+multiplierStep)) {
multiplier -= multiplierStep;
}
printf( "Multiplier: %f\n", multiplier );
return 1;
}
if (c == 'w') {
multiplier += multiplierStep;
printf( "Multiplier: %f\n", multiplier );
return 1;
}
return 0;
}
};
int inputThread() {
tcon tc (std::string(""));
tc.run();
}
void listDevices() {
int i, numDevices, defaultDisplayed;
const PaDeviceInfo *deviceInfo;
Pa_Initialize();
numDevices = Pa_GetDeviceCount();
printf( "Number of devices = %d\n", numDevices );
int isInputDevice = 0;
for( i=0; i<numDevices; i++ )
{
deviceInfo = Pa_GetDeviceInfo( i );
int isInputDevice = (deviceInfo->maxInputChannels > 0);
printf( "%sDeviceID: %d, Name: %s\n", (isInputDevice ? "Input" : "Output"), i, deviceInfo->name);
}
fprintf (stderr, "Press any key to close\n");
getch();
}
int main (int argc, char **argv)
{
int c;
int inputDeviceId = -1;
int outputDeviceId = -1;
opterr = 0;
const char* helpMessage =
"-h : show this help message\n"
"-i <int> : select the INPUT DEVICE by id\n"
"-o <int> : select the OUPUT DEVICE by id\n"
"-m <double> : SIGNAL MULTIPLIER\n"
"-s <double> : SIGNAL MULTIPLIER STEP (press w or s while console focused to go up and down by this ammount.\n"
"-d : list devices\n";
while ((c = getopt (argc, argv, "i:o:l:m:s:hd")) != -1) {
switch (c) {
case 'i':
inputDeviceId = atoi(optarg);
break;
case 'o':
outputDeviceId = atoi(optarg);
break;
case 'm':
multiplier = atof(optarg);
break;
case 's':
multiplierStep = atof(optarg);
break;
case 'd':
listDevices();
return 0;
case '?':
if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
case 'h':
fprintf (stderr, helpMessage);
fprintf (stderr, "Press any key to close\n");
getch();
return 1;
default:
abort ();
}
}
// Start non blocking input thread
std::thread nonBlockingInputThread(inputThread);
PaStreamParameters inputParameters, outputParameters;
PaStream *stream = NULL;
PaError err;
const PaDeviceInfo* inputInfo;
const PaDeviceInfo* outputInfo;
char *sampleBlock = NULL;
int i;
int numBytes;
int numChannels;
err = Pa_Initialize();
if( err != paNoError ) error2(stream, err);
inputParameters.device = (inputDeviceId == -1) ? Pa_GetDefaultInputDevice() : inputDeviceId; /* default input device */
inputInfo = Pa_GetDeviceInfo( inputParameters.device );
outputParameters.device = (outputDeviceId == -1) ? Pa_GetDefaultOutputDevice() : outputDeviceId; /* default output device */
outputInfo = Pa_GetDeviceInfo( outputParameters.device );
numChannels = inputInfo->maxInputChannels < outputInfo->maxOutputChannels
? inputInfo->maxInputChannels : outputInfo->maxOutputChannels;
inputParameters.channelCount = numChannels;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = inputInfo->defaultHighInputLatency ;
inputParameters.hostApiSpecificStreamInfo = NULL;
printf( "Input device # %d.\n", inputParameters.device );
printf( " Name: %s\n", inputInfo->name );
outputParameters.channelCount = numChannels;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = outputInfo->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
printf( "Output device # %d.\n", outputParameters.device );
printf( " Name: %s\n", outputInfo->name );
/* -- setup -- */
err = Pa_OpenStream(
&stream,
&inputParameters,
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if( err != paNoError ) error2(stream, err);
numBytes = FRAMES_PER_BUFFER * numChannels * SAMPLE_SIZE ;
sampleBlock = (char *) malloc( numBytes );
if( sampleBlock == NULL )
{
printf("Could not allocate record array.\n");
error1(stream, sampleBlock);
}
err = Pa_StartStream( stream );
if( err != paNoError ) error1(stream, sampleBlock);
while (1) {
// You may get underruns or overruns if the output is not primed by PortAudio.
err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) xrun(stream, err, sampleBlock);
int blockIndex;
float* sampleBlockShort = (float*)sampleBlock;
for (blockIndex = 0; blockIndex < FRAMES_PER_BUFFER; blockIndex++) {
/*
double dSample = (double)sampleBlockShort[blockIndex];
dSample *= multiplier;
if (dSample > 32767.0) dSample = 32767.0;
if (dSample < -32768.0) dSample = -32768.0;
sampleBlockShort[blockIndex] = (short)dSample;
*/
sampleBlockShort[blockIndex] *= multiplier;
}
err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) xrun(stream, err, sampleBlock);
}
printf("Wire off.\n"); fflush(stdout);
err = Pa_StopStream( stream );
if( err != paNoError ) error1(stream, sampleBlock);
free( sampleBlock );
Pa_Terminate();
return 0;
}
int xrun(PaStream *stream, int err, char* sampleBlock) {
printf("err = %d\n", err); fflush(stdout);
if( stream ) {
Pa_AbortStream( stream );
Pa_CloseStream( stream );
}
free( sampleBlock );
Pa_Terminate();
if( err & paInputOverflow )
fprintf( stderr, "Input Overflow.\n" );
if( err & paOutputUnderflow )
fprintf( stderr, "Output Underflow.\n" );
return -2;
}
void error1(PaStream *stream, char* sampleBlock) {
free( sampleBlock );
exit(-1);
}
void error2(PaStream *stream, int err) {
if( stream ) {
Pa_AbortStream( stream );
Pa_CloseStream( stream );
}
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
exit(-1);
}

I found that you can use the webrtc library for this as well. It comes with noise suppression which is handy. I don't understand what compression_gain_db and target_level_dbfs actually do, but setting them to their highest values seems to apply the most gain. Working in int16 as suggested by #alexander resolved many issues to my custom solution and fixing up the loop to go over the entire buffer helped as well. Both webrtc's solution and my own can be played with in realtime with the following example code.
Code example included below.
$ ./main.exe -h
-h : show this help message
-i <int> : select the INPUT DEVICE by id
-o <int> : select the OUPUT DEVICE by id
-c <int [0,90]> : compression_gain_db
-t <int [0, 31]> : target_level_dbfs
-g <0 or 1> : toggle webrtc gain control on and off (1 by default)
-k <0 or 1> : toggle custom gain control on and off (1 by default)
-f <int [1, maxInt]> : customGainControlFactor
-q <int [0, 3]> : webrtc noise supression level, high is more suppression
-e <0 or 1> : toggle webrtc noise suppression on and off (1 by default)
-d : list devices
Real time controls:
compression_gain_db UP_KEY='a' DOWN_KEY='s'
target_dbfs_level UP_KEY='d' DOWN_KEY='f'
webrtcGainControlEnabled TOGGLE_KEY='g'
webrtcNoiseSuppressionLevel UP_KEY='q' DOWN_KEY='w'
webrtcNoiseSuppressionEnabled TOGGLE_KEY='e'
customGainFactor UP_KEY='h' DOWN_KEY='j'
customGainFactorEnabled TOGGLE_KEY='k'
Press any key to close
Dependencies: tinycon, PortAudio, libwebrtc-audio-processing-devel
Note: I'm working on cygwin, if you have trouble with libwebrtc see here
Compiling: g++ main.cpp tinycon.cpp -o main -L./ -lcygportaudio-2 -lrt -lm -pthread -I/usr/include/webrtc_audio_processing/ -DWEBRTC_WIN -DWEBRTC
main.cpp
#include "portaudio.h"
#include <iostream>
#include <limits>
#include <chrono>
#include <thread>
#include <mutex>
#include "tinycon.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/include/trace.h"
using webrtc::AudioProcessing;
using webrtc::AudioFrame;
using webrtc::GainControl;
using webrtc::NoiseSuppression;
#define SAMPLE_RATE (32000)
#define FRAMES_PER_BUFFER (320)
#define DITHER_FLAG (0)
#define PA_SAMPLE_TYPE paInt16
#define SAMPLE_SIZE (2)
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
/*******************************************************************/
int customGainFactor = 1;
int customGainFactorStep = 1;
bool customGainControlEnabled = true;
int compression_gain_db = 1;
int compression_gain_dbStep = 1;
int target_level_dbfs = 1;
int target_level_dbfsStep = 1;
bool webrtcGainControlEnabled = true;
bool webrtcNoiseSuppressionEnabled = true;
int webrtcNoiseSuppressionLevel = 1;
int main(int argc, char **argv);
int xrun(PaStream *stream, int err, char* sampleBlock);
void error1(PaStream *stream, char* sampleBlock);
void error2(PaStream *stream, int err);
void listDevices();
webrtc::NoiseSuppression::Level webrtcNoiseSuppressionLevelToEnum(int level);
// Use tinycon and a second thread for non blocking input
class tcon : public tinyConsole
{
public:
tcon (std::string s): tinyConsole(s) {;}
int hotkeys(char c)
{
if (c == 'a') {
if (compression_gain_db >= (0+compression_gain_dbStep)) {
compression_gain_db -= compression_gain_dbStep;
}
printf( "Compression_gain_db: %d\n", compression_gain_db );
return 1;
}
if (c == 's') {
if (compression_gain_db <= (90-compression_gain_dbStep)) {
compression_gain_db += compression_gain_dbStep;
}
printf( "Compression_gain_db: %d\n", compression_gain_db );
return 1;
}
if (c == 'd') {
if (target_level_dbfs >= (0+target_level_dbfsStep)) {
target_level_dbfs -= target_level_dbfsStep;
}
printf( "target_level_dbfs: %d\n", target_level_dbfs );
return 1;
}
if (c == 'f') {
if (target_level_dbfs <= (31-target_level_dbfsStep)) {
target_level_dbfs += target_level_dbfsStep;
}
printf( "target_level_dbfs: %d\n", target_level_dbfs );
return 1;
}
if (c == 'g') {
webrtcGainControlEnabled = !webrtcGainControlEnabled;
printf("webrtcGainControlEnabled: %s\n", (webrtcGainControlEnabled) ? "true" : "false");
return 1;
}
if (c == 'h') {
if (customGainFactor >= (1+customGainFactorStep)) {
customGainFactor -= customGainFactorStep;
}
printf( "customGainFactor: %d\n", customGainFactor );
return 1;
}
if (c == 'j') {
customGainFactor += customGainFactorStep;
printf( "customGainFactor: %d\n", customGainFactor );
return 1;
}
if (c == 'k') {
customGainControlEnabled = !customGainControlEnabled;
printf("customGainControlEnabled: %s\n", (customGainControlEnabled) ? "true" : "false");
return 1;
}
if (c == 'q') {
if (webrtcNoiseSuppressionLevel <= (3-1)) {
webrtcNoiseSuppressionLevel += 1;
}
printf( "webrtcNoiseSuppressionLevel: %d\n", webrtcNoiseSuppressionLevel );
return 1;
}
if (c == 'w') {
if (webrtcNoiseSuppressionLevel >= (0+1)) {
webrtcNoiseSuppressionLevel -= 1;
}
printf( "webrtcNoiseSuppressionLevel: %d\n", webrtcNoiseSuppressionLevel );
return 1;
}
if (c == 'e') {
webrtcNoiseSuppressionEnabled = !webrtcNoiseSuppressionEnabled;
printf("webrtcNoiseSuppressionEnabled: %s\n", (webrtcNoiseSuppressionEnabled) ? "true" : "false");
return 1;
}
return 0;
}
};
int inputThread() {
tcon tc (std::string(""));
tc.run();
}
void listDevices() {
int i, numDevices, defaultDisplayed;
const PaDeviceInfo *deviceInfo;
Pa_Initialize();
numDevices = Pa_GetDeviceCount();
printf( "Number of devices = %d\n", numDevices );
int isInputDevice = 0;
for( i=0; i<numDevices; i++ )
{
deviceInfo = Pa_GetDeviceInfo( i );
int isInputDevice = (deviceInfo->maxInputChannels > 0);
printf( "%sDeviceID: %d, Name: %s\n", (isInputDevice ? "Input" : "Output"), i, deviceInfo->name);
}
fprintf (stderr, "Press any key to close\n");
getch();
}
int main (int argc, char **argv)
{
int c;
int inputDeviceId = -1;
int outputDeviceId = -1;
opterr = 0;
const char* helpMessage =
"-h : show this help message\n"
"-i <int> : select the INPUT DEVICE by id\n"
"-o <int> : select the OUPUT DEVICE by id\n"
"-c <int [0,90]> : compression_gain_db\n"
"-t <int [0, 31]> : target_level_dbfs\n"
"-g <0 or 1> : toggle webrtc gain control on and off (1 by default)\n"
"-k <0 or 1> : toggle custom gain control on and off (1 by default)\n"
"-f <int [1, maxInt]> : customGainControlFactor\n"
"-q <int [0, 5]> : webrtc noise supression level, high is more suppression\n"
"-e <0 or 1> : toggle webrtc noise suppression on and off (1 by default)\n"
"-d : list devices\n"
"\n"
"Real time controls:\n"
"compression_gain_db UP_KEY='a' DOWN_KEY='s'\n"
"target_dbfs_level UP_KEY='d' DOWN_KEY='f'\n"
"webrtcGainControlEnabled TOGGLE_KEY='g'\n"
"webrtcNoiseSuppressionLevel UP_KEY='q' DOWN_KEY='w'\n"
"webrtcNoiseSuppressionEnabled TOGGLE_KEY='e'\n"
"customGainFactor UP_KEY='h' DOWN_KEY='j'\n"
"customGainFactorEnabled TOGGLE_KEY='k'\n";
while ((c = getopt (argc, argv, "i:o:c:t:g:k:f:w:q:hd")) != -1) {
switch (c) {
case 'i':
inputDeviceId = atoi(optarg);
break;
case 'o':
outputDeviceId = atoi(optarg);
break;
case 'c':
compression_gain_db = atoi(optarg);
break;
case 't':
target_level_dbfs = atoi(optarg);
break;
case 'g':
webrtcGainControlEnabled = (atoi(optarg) == 1) ? true : false;
break;
case 'f':
customGainFactor = atoi(optarg);
break;
case 'k':
customGainControlEnabled = (atoi(optarg) == 1) ? true : false;
break;
case 'w':
webrtcNoiseSuppressionLevel = atoi(optarg);
break;
case 'e':
webrtcNoiseSuppressionEnabled = (atoi(optarg) == 1) ? true : false;
break;
case 'd':
listDevices();
return 0;
case '?':
if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
case 'h':
fprintf (stderr, helpMessage);
fprintf (stderr, "Press any key to close\n");
getch();
return 1;
default:
abort ();
}
}
// Start non blocking input thread
std::thread nonBlockingInputThread(inputThread);
PaStreamParameters inputParameters, outputParameters;
PaStream *stream = NULL;
PaError err;
const PaDeviceInfo* inputInfo;
const PaDeviceInfo* outputInfo;
char *sampleBlock = NULL;
int i;
int numBytes;
int numChannels;
err = Pa_Initialize();
if( err != paNoError ) error2(stream, err);
inputParameters.device = (inputDeviceId == -1) ? Pa_GetDefaultInputDevice() : inputDeviceId; /* default input device */
inputInfo = Pa_GetDeviceInfo( inputParameters.device );
outputParameters.device = (outputDeviceId == -1) ? Pa_GetDefaultOutputDevice() : outputDeviceId; /* default output device */
outputInfo = Pa_GetDeviceInfo( outputParameters.device );
numChannels = inputInfo->maxInputChannels < outputInfo->maxOutputChannels
? inputInfo->maxInputChannels : outputInfo->maxOutputChannels;
inputParameters.channelCount = numChannels;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = inputInfo->defaultHighInputLatency ;
inputParameters.hostApiSpecificStreamInfo = NULL;
printf( "Input device # %d.\n", inputParameters.device );
printf( " Name: %s\n", inputInfo->name );
outputParameters.channelCount = numChannels;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = outputInfo->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
printf( "Output device # %d.\n", outputParameters.device );
printf( " Name: %s\n", outputInfo->name );
/* -- setup -- */
err = Pa_OpenStream(
&stream,
&inputParameters,
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if( err != paNoError ) error2(stream, err);
numBytes = FRAMES_PER_BUFFER * numChannels * SAMPLE_SIZE ;
sampleBlock = (char *) malloc( numBytes );
if( sampleBlock == NULL )
{
printf("Could not allocate record array.\n");
error1(stream, sampleBlock);
}
// Configure webrtc::audioprocessing
int webrtcErr;
AudioProcessing* apm = AudioProcessing::Create();
apm->high_pass_filter()->Enable(true);
apm->noise_suppression()->set_level(webrtcNoiseSuppressionLevelToEnum(webrtcNoiseSuppressionLevel));
apm->noise_suppression()->Enable(webrtcNoiseSuppressionEnabled);
apm->gain_control()->set_mode(apm->gain_control()->kFixedDigital);
apm->gain_control()->set_compression_gain_db(compression_gain_db);
apm->gain_control()->set_target_level_dbfs(target_level_dbfs);
apm->gain_control()->Enable(webrtcGainControlEnabled);
err = Pa_StartStream( stream );
if( err != paNoError ) error1(stream, sampleBlock);
while (1) {
// You may get underruns or overruns if the output is not primed by PortAudio.
err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) xrun(stream, err, sampleBlock);
// Run custom gain solution
if (customGainControlEnabled) {
int blockIndex;
short* sampleBlockShort = (short*)sampleBlock;
for (blockIndex = 0; blockIndex < FRAMES_PER_BUFFER*numChannels; blockIndex++) {
int iSample = (int)sampleBlockShort[blockIndex];
iSample *= customGainFactor;
if (iSample > std::numeric_limits<short>::max())
iSample =
(iSample > std::numeric_limits<short>::max()) ? std::numeric_limits<short>::max()
: (iSample < std::numeric_limits<short>::min()) ? std::numeric_limits<short>::min()
: iSample;
sampleBlockShort[blockIndex] = (short)iSample;
}
}
// Apply webrtc gain and noise suppression
apm->noise_suppression()->set_level(webrtcNoiseSuppressionLevelToEnum(webrtcNoiseSuppressionLevel));
apm->noise_suppression()->Enable(webrtcNoiseSuppressionEnabled);
apm->gain_control()->set_compression_gain_db(compression_gain_db);
apm->gain_control()->set_target_level_dbfs(target_level_dbfs);
apm->gain_control()->Enable(webrtcGainControlEnabled);
webrtc::AudioFrame frame;
frame.num_channels_ = numChannels;
frame.sample_rate_hz_ = SAMPLE_RATE;
frame.samples_per_channel_ = FRAMES_PER_BUFFER;
memcpy(frame.data_, sampleBlock, numBytes);
if ((webrtcErr = apm->ProcessStream(&frame)) < 0) {
printf("Error Code: %d\n", webrtcErr); fflush(stdout);
return -1;
}
memcpy(sampleBlock, frame.data_, numBytes);
err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) xrun(stream, err, sampleBlock);
}
printf("Wire off.\n"); fflush(stdout);
err = Pa_StopStream( stream );
if( err != paNoError ) error1(stream, sampleBlock);
free( sampleBlock );
Pa_Terminate();
return 0;
}
int xrun(PaStream *stream, int err, char* sampleBlock) {
printf("err = %d\n", err); fflush(stdout);
if( stream ) {
Pa_AbortStream( stream );
Pa_CloseStream( stream );
}
free( sampleBlock );
Pa_Terminate();
if( err & paInputOverflow )
fprintf( stderr, "Input Overflow.\n" );
if( err & paOutputUnderflow )
fprintf( stderr, "Output Underflow.\n" );
return -2;
}
void error1(PaStream *stream, char* sampleBlock) {
free( sampleBlock );
exit(-1);
}
void error2(PaStream *stream, int err) {
if( stream ) {
Pa_AbortStream( stream );
Pa_CloseStream( stream );
}
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
exit(-1);
}
webrtc::NoiseSuppression::Level webrtcNoiseSuppressionLevelToEnum(int level) {
switch (level) {
case 0 : return webrtc::NoiseSuppression::Level::kLow;
case 1 : return webrtc::NoiseSuppression::Level::kModerate;
case 2 : return webrtc::NoiseSuppression::Level::kHigh;
case 3 : return webrtc::NoiseSuppression::Level::kVeryHigh;
}
}

Related

What could be causing an unexpected_EOF error with libbzip2 readOpen function?

I'm following the bzip2 programming with libbzip2 instructions to make a script for compression/decompression, but I have run into issues with the reading step. Here's my code:
int decode = (argv[4][0]=='d');
FILE* f = fopen( argv[2], "rb" ); if( f==0 ) return 2;
FILE* g = fopen( argv[3], "wb" ); if( g==0 ) return 2;
int bzError;
int nBuf;
int f_len = flen(f);
byte* inp = new byte[f_len*4+1024*1024]; if( inp==0 ) return 3;
f_len = fread( inp, 1,f_len, f );
if( decode==0 ) {
int BLOCK_MULTIPLIER = atoi( argv[5] );
BZFILE *myBZ = BZ2_bzWriteOpen(&bzError, g, BLOCK_MULTIPLIER, 0, 0);
BZ2_bzWrite(&bzError, myBZ, inp, f_len);
BZ2_bzWriteClose(&bzError, myBZ, 0, NULL, NULL);
} else {
byte buf[4096];
BZFILE *myBZ = BZ2_bzReadOpen(&bzError, f, 0, 0, NULL, 0);
if (bzError != BZ_OK) {
fprintf(stderr, "E: BZ2_bzReadOpen: %d\n", bzError);
return -1;
}
while (bzError == BZ_OK) {
int nread = BZ2_bzRead(&bzError, myBZ, buf, sizeof buf);
cout<<"nread= "<<nread<<"\n";
if (bzError == BZ_OK || bzError == BZ_STREAM_END) {
size_t nwritten = fwrite(buf, 1, nread, stdout);
if (nwritten != (size_t) nread) {
fprintf(stderr, "E: short write\n");
return -1;
}
}
}
if (bzError != BZ_STREAM_END) {
fprintf(stderr, "E: bzip error after read: %d\n", bzError);
return -1;
}
BZ2_bzReadClose(&bzError, myBZ);
return 0;
}
The compression mode works fine, but if it is in decompression mode, the bzRead step fails and I get the following output from my error messages/statements:
nread = 0
E: bzip error after read: -7
Why would nread be 0? Also, the -7 represents an unexpected EOF, but how is that possible? I have tried running this on files compressed with the built in linux bzip2 as well, and gotten the same output.
Looks like you're reading the entire input file before you decide if you're decoding or not. If you are, then you try to continue reading that same input file even though you had already reached the end of the file. So you get nothing.

reading from ssh channel and writing to a buffer

I have this function which if you connect to a system with ssh, you can call it to execute your given command on that system.
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
if (write(1, buffer, nbytes) != (unsigned int) nbytes)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}
this function works great. I just don't understand that part which is about to write from buffer into a file descriptor=1 . we haven't filled receive anywhere but it is the return value. if we call this function like below:
s = exec_ssh_command(my_ssh_session, "cat /proc/stat" );
the s won't have any value, but if we do this:
std::cout<<s;
this will print s value. and of course we can't save s in a file. can someone explain to me how is this happening?
EDIT:function to connect to ssh:
int sshconnection::sshConnection()
{
if( my_ssh_session == NULL ) {
cout << "Error creating ssh session" << endl;
return 1;
}
ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "yourip");
ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, "username");
int rc = ssh_connect(my_ssh_session);
if( rc != SSH_OK ) {
cout << "Error with connecting" << endl;
ssh_free(my_ssh_session);
return -1;
}
rc = ssh_userauth_password(my_ssh_session, NULL, "yourpassword");
if( rc != SSH_AUTH_SUCCESS) {
cout << "Error with authorization " << ssh_get_error(my_ssh_session) << endl;
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return -1;
}
// ssh_disconnect(my_ssh_session);
// ssh_free(my_ssh_session);
}
I know this is old, but I had the same issue. I came up with the following solution.
Use std::string::append like so receive.append(buffer, nbytes).
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
receive.append(buffer, nbytes);
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}

tcp data modified after socket transmission

I am writing a program to transmit data between two machines with tcp/ip socket in C/C++. The programs checks the size of each read and write to make sure the correct size of data being transferred. I also implement a checksum verification to make sure the correct data are transferred. Most of time there is no problem. Once a while, like, once in 50,000 times, I notice the data received is the same length as the sending side but some of its content were altered. The altered portion is always 64 bytes long. Below is my code of send and read. Please help me figure out how this can happen and how to prevent it. Thanks!
bool WriteSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes, uint32_t &pNumBytesWritten, uint32_t &pNumWrites)
{
int rc;
unsigned char* currPos = pData;
uint32_t numFailures = 0;
pNumBytesWritten = 0;
pNumWrites = 0;
while ( true )
{
rc = write(pSocket, currPos, pNumBytes);
pNumWrites++;
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
numFailures++;
if ( numFailures >= 5 )
break;
else
continue;
}
pNumBytesWritten += rc;
if ( rc == pNumBytes )
return true;
pNumBytes -= rc;
currPos += rc;
numFailures = 0;
};
return false;
};
bool ReadSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes)
{
int rc;
unsigned char* currPos = pData;
uint32_t numFailures = 0;
while ( true )
{
rc = read(pSocket, currPos, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
numFailures++;
if ( numFailures >= 5 )
break;
else
continue;
}
if ( rc == pNumBytes )
return true;
pNumBytes -= rc;
currPos += rc;
numFailures = 0;
};
return false;
};
If a socket read/write fails for any reason other than EINTR or EAGAIN/EWOULDBLOCK, you should consider that as a fatal error. Do not retry the operation, just stop and close the socket. But your code doesn't do that, it keeps trying to repeat the failed operation.
Also, when reading, you are not handling the case where the read returns 0 when the peer disconnects gracefully. A socket error is not reported in that case.
Try something more like this instead:
bool WriteSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes, uint32_t &pNumBytesWritten, uint32_t &pNumWrites)
{
int rc;
uint32_t numRetries = 0;
pNumBytesWritten = 0;
pNumWrites = 0;
while ( pNumBytes > 0 )
{
rc = write(pSocket, pData, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) )
{
++numRetries;
if ( numRetries < 5 )
{
// TODO: use select() or epoll() w/ timeout to
// detect when then socket is writable again...
continue;
}
}
return false;
}
++pNumWrites;
pNumBytesWritten += rc;
pNumBytes -= rc;
pData += rc;
numRetries = 0;
}
return true;
}
bool ReadSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes)
{
int rc;
uint32_t numRetries = 0;
while ( pNumBytes > 0 )
{
rc = read(pSocket, pData, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) )
{
++numRetries;
if ( numRetries < 5 )
{
// TODO: use select() or epoll() w/ timeout to
// detect when then socket is readable again...
continue;
}
}
return false;
}
else if ( rc == 0 )
{
return false;
}
else
{
pNumBytes -= rc;
pData += rc;
numRetries = 0;
}
}
return true;
}

How to write H264 raw stream into mp4 using ffmpeg directly

I want to wrap the H264 Nalus(x264 encoded) into mp4 using ffmpeg(SDK 2.1), but the output mp4 file could not play. I don't know how to set the pts and dts. Here's my code, using the code from Raw H264 frames in mpegts container using libavcodec and muxing.c from www.ffmpeg.org. My H264 stream has no B-Frame, every nalu starts with 00 00 00 01,the stream begins with sps pps then the h264 data.
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include "Stream2Mp4.h"
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswresample/swresample.h>
#define STREAM_FRAME_RATE 25
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
static int ptsInc = 0;
static int vi = -1;
static int waitkey = 1;
// < 0 = error
// 0 = I-Frame
// 1 = P-Frame
// 2 = B-Frame
// 3 = S-Frame
int getVopType( const void *p, int len )
{
if ( !p || 6 >= len )
return -1;
unsigned char *b = (unsigned char*)p;
// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{ b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
return -1;
} // end if
b += 3;
// Verify VOP id
if ( 0xb6 == *b )
{ b++;
return ( *b & 0xc0 ) >> 6;
} // end if
switch( *b )
{ case 0x65 : return 0;
case 0x61 : return 1;
case 0x01 : return 2;
} // end switch
return -1;
}
int get_nal_type( void *p, int len )
{
if ( !p || 5 >= len )
return -1;
unsigned char *b = (unsigned char*)p;
// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{ b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
return -1;
} // end if
b += 3;
return *b;
}
/* Add an output stream */
AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
AVCodecContext *c;
AVStream *st;
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!*codec)
{
printf("could not find encoder for '%s' \n", avcodec_get_name(codec_id));
exit(1);
}
st = avformat_new_stream(oc, *codec);
if (!st)
{
printf("could not allocate stream \n");
exit(1);
}
st->id = oc->nb_streams-1;
c = st->codec;
vi = st->index;
switch ((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
c->bit_rate = 64000;
c->sample_rate = 44100;
c->channels = 2;
break;
case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;
c->bit_rate = 90000;
c->width = 480;
c->height = 354;
c->time_base.den = 15;
c->time_base.num = 1;
c->gop_size = 12;
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
{
c->mb_decision = 2;
}
break;
default:
break;
}
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
{
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
return st;
}
void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret;
AVCodecContext *c = st->codec;
/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0)
{
printf("could not open video codec");
//exit(1);
}
}
int CreateMp4(AVFormatContext *&m_pOc, void *p, int len)
{
int ret;
const char* pszFileName = "output002.mp4";
AVOutputFormat *fmt;
AVCodec *video_codec;
AVStream *m_pVideoSt;
if (0x67 != get_nal_type(p, len))
{
printf("can not detect nal type");
return -1;
}
av_register_all();
avformat_alloc_output_context2(&m_pOc, NULL, NULL, pszFileName);
if (!m_pOc)
{
printf("Could not deduce output format from file extension: using MPEG. \n");
avformat_alloc_output_context2(&m_pOc, NULL, "mpeg", pszFileName);
}
if (!m_pOc)
{
return 1;
}
fmt = m_pOc->oformat;
if (fmt->video_codec != AV_CODEC_ID_NONE)
{
m_pVideoSt = add_stream(m_pOc, &video_codec, fmt->video_codec);
}
if (m_pVideoSt)
{
open_video(m_pOc, video_codec, m_pVideoSt);
}
av_dump_format(m_pOc, 0, pszFileName, 1);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&m_pOc->pb, pszFileName, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("could not open '%s': %s\n", pszFileName);
return 1;
}
}
/* Write the stream header, if any */
ret = avformat_write_header(m_pOc, NULL);
if (ret < 0)
{
printf("Error occurred when opening output file");
return 1;
}
}
/* write h264 data to mp4 file*/
void WriteVideo(AVFormatContext *&m_pOc,void* data, int nLen)
{
int ret;
if ( 0 > vi )
{
printf("vi less than 0");
//return -1;
}
AVStream *pst = m_pOc->streams[ vi ];
// Init packet
AVPacket pkt;
AVCodecContext *c = pst->codec;
av_init_packet( &pkt );
pkt.flags |= ( 0 >= getVopType( data, nLen ) ) ? AV_PKT_FLAG_KEY : 0;
pkt.stream_index = pst->index;
pkt.data = (uint8_t*)data;
pkt.size = nLen;
// Wait for key frame
if ( waitkey )
if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) )
return ;
else
waitkey = 0;
pkt.pts = (ptsInc++) * (90000/STREAM_FRAME_RATE);
//pkt.dts = (ptsInc++) * (90000/STREAM_FRAME_RATE);
ret = av_interleaved_write_frame( m_pOc, &pkt );
if (ret < 0)
{
printf("cannot write frame");
}
}
void CloseMp4(AVFormatContext *&m_pOc)
{
waitkey = -1;
vi = -1;
if (m_pOc)
av_write_trailer(m_pOc);
if (m_pOc && !(m_pOc->oformat->flags & AVFMT_NOFILE))
avio_close(m_pOc->pb);
if (m_pOc)
{
avformat_free_context(m_pOc);
m_pOc = NULL;
}
}
could anybody help me? Thank you very much!
you can try this.
if(pkt.pts != AV_NOPTS_VALUE)
{
pkt.pts = av_rescale_q_rnd(ptsInc++, c->time_base, pst->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
}

Sniffing an ethernet interface on linux

I am trying to capture only packets from a specific interface and instead I am getting packets from all of the interfaces. what am I doing wrong?
bool Snooper::raw_init (const char *device)
{
uid_t privid = geteuid();
int retval;
bool retVal = false;
do {
if ((retval = setuid(0)) != 0) {
perror("seteuid error");
break;
}
cap_t caps = cap_get_proc();
cap_value_t cap_list[2];
cap_list[0] = CAP_NET_RAW;
cap_list[1] = CAP_SETUID;
if ((retval = cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_list, CAP_SET)) == -1) {
perror("cap_set_flag error");
break;
}
if ((retval = cap_set_proc(caps)) == -1) {
perror("cap_set_proc error");
break;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof (struct ifreq));
/* Open A Raw Socket */
if ((m_sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 1) {
perror("Snooper::raw_init:socket Error");
break;
}
/* Set the device to use */
strncpy(ifr.ifr_name, device, strlen(device) + 1);
/* Get the current flags that the device might have */
if (ioctl(m_sockfd, SIOCGIFFLAGS, &ifr) == -1) {
perror("Error: Could not retrieve the flags from the device.\n");
break;
}
printf("The interface is ::: %s\n", device);
perror("Retrieved flags from interface successfully");
/* Set the old flags plus the IFF_PROMISC flag */
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(m_sockfd, SIOCSIFFLAGS, &ifr) == -1) {
perror("Error: Could not set flag IFF_PROMISC");
break;
}
printf("Setting interface ::: %s ::: to promisc\n", device);
/* Configure the device */
if (ioctl(m_sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("Error: Error getting the device index.\n");
break;
}
retVal = true;
} while(false);
if ((retval = seteuid(privid)) != 0) {
perror("seteuid error");
}
return retVal;
}
I first validate that I can suid to root since IFF_PROMISC requires it. Then create the socket for UDP traffic, preform the IOCtl for the device, and finally IOCtl for PROMISC.
Now that I have a socket ready I loop on a recv, however I get packets from the other interfaces as well.
To capture packets from a specific interface, you have to bind your socket to that interface using bind function. You can check out this answer for an example.
A small pcap Program that might be able to help You
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pcap/pcap.h>
#include<netinet/if_ether.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
void process_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *buffer)
{
// printf("In Process_Packet\n");
struct ethhdr *eth = (struct ethhdr *)(buffer);
printf("%.2x: %.2x: %.2x: %.2x: %.2x: %.2x: %.2x:\n ", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5], eth->h_dest[6]);
printf("%x \n", htons(eth->h_proto));
if(htons(eth->h_proto)== ETHERTYPE_ARP)
{
printf("ARP PACKET\n");
}
struct iphdr *iph = (struct iphdr*)(buffer + sizeof(struct ethhdr));
int ipheader = iph-> ihl *4;
printf("Source IP Address :%s\n ", inet_ntoa(iph->saddr));
printf("Destination IP Address :%s\n ", inet_ntoa(iph->daddr));
}
int main()
{
pcap_if_t *alldevspec,*devices;
pcap_addr_t *a;
pcap_t *handle;
const char filter_exp[]="IP";
bpf_u_int32 netp;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
int ret=0, count =1, n=0;
char devs[100][100],*devicename;
ret = pcap_findalldevs(&alldevspec,errbuf);
if(ret < 0)
{
printf("Error in finding the devices\n");
return -1;
}
for(devices = alldevspec; devices!= NULL; devices = devices->next)
{
printf("%d %s-%s \n",count, devices->name,devices->description);
for(a=devices->addresses;a;a=a->next)
{
printf("family %d \n", (a->addr)->sa_family);
if(devices->name != NULL)
{
strcpy(devs[count], devices->name);
}
switch((a->addr)->sa_family)
{
case AF_INET:
printf("%s \n",inet_ntoa(((struct sockaddr_in*)a->addr)->sin_addr.s_addr));
break;
case AF_INET6:
break;
}
}
++count;
}
printf("Enter the device u want to select\n");
scanf("%d",&n);
devicename = devs[n];
handle = pcap_open_live(devicename,65536,1,-1,errbuf);
if(handle == NULL)
{
printf("Error in opening the device\n");
return -1;
}
pcap_compile(handle,&fp, filter_exp,-1,netp);
pcap_setfilter(handle, &fp);
pcap_loop(handle,-1,process_packet,NULL);
return 0;
}