Record RTSP stream with FFmpeg libavformat - c

I'm trying to record RTSP stream from Axis camera with FFmpeg libavformat.
I can grab video from files and then save it to another file, this is OK. But camera sends strange data, FPS is 100 and camera sends every 4th frame so result FPS is about 25. But libavformat set packets dts/pts for 90000 fps (default?) and new file stream has 100fps. Result is one hour video with only 100 frames.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
int main(int argc, char** argv) {
AVFormatContext* context = avformat_alloc_context();
int video_stream_index;
av_register_all();
avcodec_register_all();
avformat_network_init();
//open rtsp
if(avformat_open_input(&context, "rtsp://195.200.199.8/mpeg4/media.amp",NULL,NULL) != 0){
return EXIT_FAILURE;
}
if(avformat_find_stream_info(context,NULL) < 0){
return EXIT_FAILURE;
}
//search video stream
for(int i =0;i<context->nb_streams;i++){
if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
}
AVPacket packet;
av_init_packet(&packet);
//open output file
AVOutputFormat* fmt = av_guess_format(NULL,"test2.avi",NULL);
AVFormatContext* oc = avformat_alloc_context();
oc->oformat = fmt;
avio_open2(&oc->pb, "test.avi", AVIO_FLAG_WRITE,NULL,NULL);
AVStream* stream=NULL;
int cnt = 0;
//start reading packets from stream and write them to file
av_read_play(context);//play RTSP
while(av_read_frame(context,&packet)>=0 && cnt <100){//read 100 frames
if(packet.stream_index == video_stream_index){//packet is video
if(stream == NULL){//create stream in file
stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec);
avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec);
stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio;
avformat_write_header(oc,NULL);
}
packet.stream_index = stream->id;
av_write_frame(oc,&packet);
cnt++;
}
av_free_packet(&packet);
av_init_packet(&packet);
}
av_read_pause(context);
av_write_trailer(oc);
avio_close(oc->pb);
avformat_free_context(oc);
return (EXIT_SUCCESS);
}
Result file is here: http://dl.dropbox.com/u/1243577/test.avi
Thanks for any advice

Here's how I do it. What I found was when receiving H264 the framerate in the stream is not correct. It sends 1/90000 Timebase. I skip initializing the new stream from the incoming stream and just copy certain parameters. The incoming r_frame_rate should be accurate if max_analyze_frames works correctly.
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <sys/time.h>
time_t get_time()
{
struct timeval tv;
gettimeofday( &tv, NULL );
return tv.tv_sec;
}
int main( int argc, char* argv[] )
{
AVFormatContext *ifcx = NULL;
AVInputFormat *ifmt;
AVCodecContext *iccx;
AVCodec *icodec;
AVStream *ist;
int i_index;
time_t timenow, timestart;
int got_key_frame = 0;
AVFormatContext *ofcx;
AVOutputFormat *ofmt;
AVCodecContext *occx;
AVCodec *ocodec;
AVStream *ost;
int o_index;
AVPacket pkt;
int ix;
const char *sProg = argv[ 0 ];
const char *sFileInput;
const char *sFileOutput;
int bRunTime;
if ( argc != 4 ) {
printf( "Usage: %s url outfile runtime\n", sProg );
return EXIT_FAILURE;
}
sFileInput = argv[ 1 ];
sFileOutput = argv[ 2 ];
bRunTime = atoi( argv[ 3 ] );
// Initialize library
av_log_set_level( AV_LOG_DEBUG );
av_register_all();
avcodec_register_all();
avformat_network_init();
//
// Input
//
//open rtsp
if ( avformat_open_input( &ifcx, sFileInput, NULL, NULL) != 0 ) {
printf( "ERROR: Cannot open input file\n" );
return EXIT_FAILURE;
}
if ( avformat_find_stream_info( ifcx, NULL ) < 0 ) {
printf( "ERROR: Cannot find stream info\n" );
avformat_close_input( &ifcx );
return EXIT_FAILURE;
}
snprintf( ifcx->filename, sizeof( ifcx->filename ), "%s", sFileInput );
//search video stream
i_index = -1;
for ( ix = 0; ix < ifcx->nb_streams; ix++ ) {
iccx = ifcx->streams[ ix ]->codec;
if ( iccx->codec_type == AVMEDIA_TYPE_VIDEO ) {
ist = ifcx->streams[ ix ];
i_index = ix;
break;
}
}
if ( i_index < 0 ) {
printf( "ERROR: Cannot find input video stream\n" );
avformat_close_input( &ifcx );
return EXIT_FAILURE;
}
//
// Output
//
//open output file
ofmt = av_guess_format( NULL, sFileOutput, NULL );
ofcx = avformat_alloc_context();
ofcx->oformat = ofmt;
avio_open2( &ofcx->pb, sFileOutput, AVIO_FLAG_WRITE, NULL, NULL );
// Create output stream
//ost = avformat_new_stream( ofcx, (AVCodec *) iccx->codec );
ost = avformat_new_stream( ofcx, NULL );
avcodec_copy_context( ost->codec, iccx );
ost->sample_aspect_ratio.num = iccx->sample_aspect_ratio.num;
ost->sample_aspect_ratio.den = iccx->sample_aspect_ratio.den;
// Assume r_frame_rate is accurate
ost->r_frame_rate = ist->r_frame_rate;
ost->avg_frame_rate = ost->r_frame_rate;
ost->time_base = av_inv_q( ost->r_frame_rate );
ost->codec->time_base = ost->time_base;
avformat_write_header( ofcx, NULL );
snprintf( ofcx->filename, sizeof( ofcx->filename ), "%s", sFileOutput );
//start reading packets from stream and write them to file
av_dump_format( ifcx, 0, ifcx->filename, 0 );
av_dump_format( ofcx, 0, ofcx->filename, 1 );
timestart = timenow = get_time();
ix = 0;
//av_read_play(context);//play RTSP (Shouldn't need this since it defaults to playing on connect)
av_init_packet( &pkt );
while ( av_read_frame( ifcx, &pkt ) >= 0 && timenow - timestart <= bRunTime ) {
if ( pkt.stream_index == i_index ) { //packet is video
// Make sure we start on a key frame
if ( timestart == timenow && ! ( pkt.flags & AV_PKT_FLAG_KEY ) ) {
timestart = timenow = get_time();
continue;
}
got_key_frame = 1;
pkt.stream_index = ost->id;
pkt.pts = ix++;
pkt.dts = pkt.pts;
av_interleaved_write_frame( ofcx, &pkt );
}
av_free_packet( &pkt );
av_init_packet( &pkt );
timenow = get_time();
}
av_read_pause( ifcx );
av_write_trailer( ofcx );
avio_close( ofcx->pb );
avformat_free_context( ofcx );
avformat_network_deinit();
return EXIT_SUCCESS;
}

I don't think you're supposed to just increment the PTS value like that. It might work in rare occasions where the time base is just right, but for the general case it won't work.
You should change this:
pkt.pts = ix++;
pkt.dts = pkt.pts;
To this:
pkt.pts = av_rescale_q(pkt.pts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base);
pkt.dts = av_rescale_q(pkt.dts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base);
What that does is convert the packet's PTS/DTS from the units used in the input stream's codec to the units of the output stream.
Also, some streams have multiple ticks-per-frame, so if the video runs at double speed you might need to this right below the above line:
pkt.pts *= ifcx->streams[0]->codec->ticks_per_frame;
pkt.dts *= ifcx->streams[0]->codec->ticks_per_frame;

In my experience with a modern H.264 encoder, I'm finding that the duration returned by ffmpeg is only a "suggestion" and that there is some "jitter" in the PTS. The only accurate way to determine frame rate or duration is to measure it yourself using the PTS values.
For an H.264 encoder running at 30fps, duration is always reported as 3000/90000, while measured duration is usually +/- 1 but periodically jumps say 3000+25 one frame and 3000-25 the next. I'm smoothing this out for recording by noticing any adjacent frames with opposite deviation and adjusting the PTS of the 2nd frame while preserving the total duration.
This give me a stream with an occasional (calculated) duration of 30001 or 2999, reflecting clock drift.
When recording a 29.97fps stream, av_read_frame() always returns a duration of 3000, while the nominal calculated duration is 3003 (correct for 29.97) with the same jitter and drift as described above.
In my case, I just built a state machine to clean up the timing. Hoping this helps someone.

Recently was doing the same. I had FPS twice lower than the camera sent. The reason was in AVstream->codec->ticks_per_frame field, set to 2. My source was progressive, and in case yours in interleaved - then that might be a reason of yet another factor of 2, giving 4x different FPS.
90000 Hz is the default timebase for video stream sent via RTSP. Timebase is different from FPS in resolution. For instance, a frame with timestamp 30000 will be shown at 1/3 sec if the timebase is 90000 Hz. The timebase should be put into the AVstream structure during the output, but AVFormatContext should have a real FPS value.

Related

C - simultaneous audio input and output using portaudio and callback function

I am trying to make a full duplex audio program that detects audio input from the mic and direct it to the speaker in real time using portaudio library and callback function but I keep getting errors. I tried to open a full input output stream then starting the read from the mic and send the captured data to a callback function that will start the streaming of audio to the speaker
Can anyone help me with this ??
Here is the errors:
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:867:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:867:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:867:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:867:(find_matching_chmap) Found no matching channel map
Output device # 14.
Output LL: 0.00870748 s
Output HL: 0.0348299 s
Now recording!!
An error occured while using the portaudio stream
Error number: -9988
Error message: Invalid stream pointer
An error occured while using the portaudio stream
Error number: -9988
Error message: Invalid stream pointer
An error occured while using the portaudio stream
Error number: -9977
Error message: Can't read from a callback stream
and this is my code:
#include "diffusion.c"
#include "confusion.c"
#include "portaudio.h"
#include "wave_file.c"
/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (0)
#define NUM_SECONDS (2)
#define NUM_CHANNELS (2)
/* #define DITHER_FLAG (paDitherOff) */
#define DITHER_FLAG (0) /**/
#define WRITE_TO_WAVE_FILE (1)
/* define sample format. */
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
SAMPLE *recordedSamples, *recordedSamples2;
int i;
int totalFrames;
int numSamples;
int numBytes;
static int playCallback (const void *input,
void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData
)
{
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Now recording!!\n"); fflush(stdout);
err = Pa_WriteStream( stream, userData, totalFrames );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
free( userData );
return paContinue;
error:
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 ) );
return paContinue;
}
int main(void);
int main(void)
{
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
SAMPLE *recordedSamples, *recordedSamples2;
int i;
int totalFrames;
int numSamples;
int numBytes;
printf("patest_read_record.c\n"); fflush(stdout);
totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
recordedSamples = (SAMPLE *) malloc( numBytes );
if( recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<numSamples; i++ ) recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto error;
}
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
printf( "Output device # %d.\n", outputParameters.device );
printf( "Output LL: %g s\n", Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency );
printf( "Output HL: %g s\n", Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency );
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
&inputParameters,
&outputParameters, /* &outputParameters, */
SAMPLE_RATE,
paFramesPerBufferUnspecified,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback, /* no callback, use blocking API */
recordedSamples ); /* no callback, so no callback userData */
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Now recording!!\n"); fflush(stdout);
err = Pa_ReadStream( stream, recordedSamples, totalFrames );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
/* save the recorded data in a wave file " recording.wav ". -------------------------- */
saveInWaveFile(recordedSamples, totalFrames, "recording.wav");
for (int i =0; i< numSamples ; i++){
recordedSamples2[i] = recordedSamples[i];
}
free( recordedSamples );
Pa_Terminate();
return 0;
error:
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 ) );
return -1;
}
First make sure that the default input and output devices are correct. You can do that by first running the pa_devs.c example in the examples folder to print a list of enabled audio devices connected to the computer. Once you have identified the correct devices, replace the lines with the device number e.g.
inputParameters.device = Pa_GetDefaultInputDevice();
for
inputParameters.device = <device number (e.g. 11)>;
and
outputParameters.device = Pa_GetDefaultOutputDevice();
for
outputParameters.device = <device number (e.g. 11)>;
Another avenue you can try is the example called paex_read_write_wire.c. This should achieve the same functionality you're talking about however uses a blocking approach rather than the callback function you have used in your code.
I can not test it now, but it should play (monitor) the input buffer (the microphone):
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
float *in = (float*)inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
out++ = in++; /* left */
out++ = in++; /* right*/
}
return 0;
}

libavcodec : how to encode with h264 codec ,with mp4 container using controllable frame rate and bitrate(through c code)

I am trying to record the screen of a pc and encode the recorded frames using h264 encoder
and wrap them into a mp4 container.I want to do this because this super user link https://superuser.com/questions/300897/what-is-a-codec-e-g-divx-and-how-does-it-differ-from-a-file-format-e-g-mp/300997#300997 suggests it allows good trade-off between size and quality of the output file.
The application I am working on should allow users to record a few hours of video and have the minimum output file size with decent quality.
The code I have cooked up so far allows me to record and save .mpg(container) files with the mpeg1video encoder
Running:
ffmpeg -i test.mpg
on the output file gives the following output:
[mpegvideo # 028c7400] Estimating duration from bitrate, this may be inaccurate
Input #0, mpegvideo, from 'test.mpg':
Duration: 00:00:00.29, bitrate: 104857 kb/s
Stream #0:0: Video: mpeg1video, yuv420p(tv), 1366x768 [SAR 1:1 DAR 683:384], 104857 kb/s, 25 fps, 25 tbr, 1200k tbn, 25 tbc
I have these settings for my output:
const char * filename="test.mpg";
int codec_id= AV_CODEC_ID_MPEG1VIDEO;
AVCodec *codec11;
AVCodecContext *outContext= NULL;
int got_output;
FILE *f;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
/* put sample parameters */
outContext->bit_rate = 400000;
/* resolution must be a multiple of two */
outContext->width=pCodecCtx->width;
outContext->height=pCodecCtx->height;
/* frames per second */
outContext->time_base.num=1;
outContext->time_base.den=25;
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
outContext->gop_size = 10;
outContext->max_b_frames = 1;
outContext->pix_fmt = AV_PIX_FMT_YUV420P;
When I change int codec_id= AV_CODEC_ID_MPEG1VIDEO to int codec_id= AV_CODEC_ID_H264 i get a file that does not play with vlc.
I have read that writing the
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
array at the end of your file when finished encoding makes your file a legitimate mpeg file.It is written like this:
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
in my code. Should I do the same thing when I change my encoder to AV_CODEC_ID_H264?
I am capturing using gdi input like this:
AVDictionary* options = NULL;
//Set some options
//grabbing frame rate
av_dict_set(&options,"framerate","30",0);
AVInputFormat *ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
I want to be able to modify my grabbing rate to optimize for the outptut file size
but When I change it to 20 for example I get a video that plays so fast.How do
I get a video that plays with normal speed with frames captured at 20 fps or any
lower frame rate value?
While recording I get the following output on the standard error output:
[gdigrab # 00cdb8e0] Capturing whole desktop as 1366x768x32 at (0,0)
Input #0, gdigrab, from '(null)':
Duration: N/A, start: 1420718663.655713, bitrate: 1006131 kb/s
Stream #0:0: Video: bmp, bgra, 1366x768, 1006131 kb/s, 29.97 tbr, 1000k tbn, 29.97 tbc
[swscaler # 00d24120] Warning: data is not aligned! This can lead to a speedloss
[mpeg1video # 00cdd160] AVFrame.format is not set
[mpeg1video # 00cdd160] AVFrame.width or height is not set
[mpeg1video # 00cdd160] AVFrame.format is not set
[mpeg1video # 00cdd160] AVFrame.width or height is not set
[mpeg1video # 00cdd160] AVFrame.format is not set
How do I get rid of this error in my code?
In summary:
1) How do I encode h264 video wrapped into mp4 container?
2) How do I capture at lower frame rates and still play
the encoded video at normal speed?
3) How do I set the format(and which format--depends on the codec?)
and width and height info on the frames I write?
The code I am using in its entirety is shown below
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
//SDL
#include "SDL.h"
#include "SDL_thread.h"
}
//Output YUV420P
#define OUTPUT_YUV420P 0
//'1' Use Dshow
//'0' Use GDIgrab
#define USE_DSHOW 0
int main(int argc, char* argv[])
{
//1.WE HAVE THE FORMAT CONTEXT
//THIS IS FROM THE DESKTOP GRAB STREAM.
AVFormatContext *pFormatCtx;
int i, videoindex;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
av_register_all();
avformat_network_init();
//ASSIGN STH TO THE FORMAT CONTEXT.
pFormatCtx = avformat_alloc_context();
//Register Device
avdevice_register_all();
//Windows
#ifdef _WIN32
#if USE_DSHOW
//Use dshow
//
//Need to Install screen-capture-recorder
//screen-capture-recorder
//Website: http://sourceforge.net/projects/screencapturer/
//
AVInputFormat *ifmt=av_find_input_format("dshow");
//if(avformat_open_input(&pFormatCtx,"video=screen-capture-recorder",ifmt,NULL)!=0){
if(avformat_open_input(&pFormatCtx,"video=UScreenCapture",ifmt,NULL)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
#else
//Use gdigrab
AVDictionary* options = NULL;
//Set some options
//grabbing frame rate
av_dict_set(&options,"framerate","30",0);
//The distance from the left edge of the screen or desktop
//av_dict_set(&options,"offset_x","20",0);
//The distance from the top edge of the screen or desktop
//av_dict_set(&options,"offset_y","40",0);
//Video frame size. The default is to capture the full screen
//av_dict_set(&options,"video_size","640x480",0);
AVInputFormat *ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
#endif
#endif//FOR THE WIN32 THING.
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
printf("Couldn't find stream information.\n");
return -1;
}
videoindex=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type
==AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
break;
}
if(videoindex==-1)
{
printf("Didn't find a video stream.\n");
return -1;
}
pCodecCtx=pFormatCtx->streams[videoindex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
printf("Codec not found.\n");
return -1;
}
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
printf("Could not open codec.\n");
return -1;
}
//THIS IS WHERE YOU CONTROL THE FORMAT(THROUGH FRAMES).
AVFrame *pFrame;
pFrame=av_frame_alloc();
int ret, got_picture;
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
//TRY TO INIT THE PACKET HERE
av_init_packet(packet);
//Output Information-----------------------------
printf("File Information---------------------\n");
av_dump_format(pFormatCtx,0,NULL,0);
printf("-------------------------------------------------\n");
//<<--FOR WRITING MPG FILES
//<<--START:PREPARE TO WRITE YOUR MPG FILE.
const char * filename="test.mpg";
int codec_id= AV_CODEC_ID_MPEG1VIDEO;
AVCodec *codec11;
AVCodecContext *outContext= NULL;
int got_output;
FILE *f;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n", filename);
/* find the mpeg1 video encoder */
codec11 = avcodec_find_encoder((AVCodecID)codec_id);
if (!codec11) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
outContext = avcodec_alloc_context3(codec11);
if (!outContext) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* put sample parameters */
outContext->bit_rate = 400000;
/* resolution must be a multiple of two */
outContext->width=pCodecCtx->width;
outContext->height=pCodecCtx->height;
/* frames per second */
outContext->time_base.num=1;
outContext->time_base.den=25;
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
outContext->gop_size = 10;
outContext->max_b_frames = 1;
outContext->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(outContext->priv_data, "preset", "slow", 0);
/* open it */
if (avcodec_open2(outContext, codec11, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
AVFrame *outframe = av_frame_alloc();
int nbytes = avpicture_get_size(outContext->pix_fmt,
outContext->width,
outContext->height);
uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes);
//ASSOCIATE THE FRAME TO THE ALLOCATED BUFFER.
avpicture_fill((AVPicture*)outframe, outbuffer,
AV_PIX_FMT_YUV420P,
outContext->width, outContext->height);
SwsContext* swsCtx_ ;
swsCtx_= sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
outContext->width, outContext->height,
outContext->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
//HERE WE START PULLING PACKETS FROM THE SPECIFIED FORMAT CONTEXT.
while(av_read_frame(pFormatCtx, packet)>=0)
{
if(packet->stream_index==videoindex)
{
ret= avcodec_decode_video2(pCodecCtx,
pFrame,
&got_picture,packet );
if(ret < 0)
{
printf("Decode Error.\n");
return -1;
}
if(got_picture)
{
sws_scale(swsCtx_, pFrame->data, pFrame->linesize,
0, pCodecCtx->height, outframe->data,
outframe->linesize);
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
ret = avcodec_encode_video2(outContext, &pkt, outframe, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
}
av_free_packet(packet);
}//THE LOOP TO PULL PACKETS FROM THE FORMAT CONTEXT ENDS HERE.
//
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
//fflush(stdout);
ret = avcodec_encode_video2(outContext, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* add sequence end code to have a real mpeg file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(outContext);
av_free(outContext);
//av_freep(&frame->data[0]);
//av_frame_free(&frame);
//THIS WAS ADDED LATER
av_free(outbuffer);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
Thank you for your time.
It is possible to record and play in the normal speed.
AVDictionary* options = NULL;
av_dict_set( &options, "preset", "veryslow", 0 );
following presets are available :
{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo", 0
}
set the suitable preset.

PortAudio microphone capture, separate channel values

I'm using a microphone array (playstation eye) with PortAudio. I'm attempting microphone array processing where I can know the level of each microphone and specify the direction of a sound using beam forming or inter aural time delays. I'm having trouble determining which sound levels come from each channel.
Here is some code snippets, the first being the recordCallback.
static int recordCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = paContinue;
}
if( inputBuffer == NULL )
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = SAMPLE_SILENCE; /* 1 */
if( NUM_CHANNELS =>= 2 ) *wptr++ = SAMPLE_SILENCE; /* 2 */
if( NUM_CHANNELS =>= 3 ) *wptr++ = SAMPLE_SILENCE; /* 3 */
if( NUM_CHANNELS >= 4 ) *wptr++ = SAMPLE_SILENCE; /* 4 */
}
}
else
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = *rptr++; /* 1 */
if( NUM_CHANNELS >= 2 ) *wptr++ = *rptr++; /* 2 */
if( NUM_CHANNELS >= 3 ) *wptr++ = *rptr++; /* 3 */
if( NUM_CHANNELS >= 4 ) *wptr++ = *rptr++; /* 4 */
}
}
data->frameIndex += framesToCalc;
return finished;
}
Here is the main method where I'm playing back the audio and trying to show channel averages.
int main(void)
{
PaStreamParameters inputParameters,
outputParameters;
PaStream* stream;
PaError err = paNoError;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, val;
double average;
printf("patest_record.c\n"); fflush(stdout);
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
data.frameIndex = 0;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
goto done;
}
for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto done;
inputParameters.device = 2; /* default input device */
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto done;
}
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, /* &outputParameters, */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
&data );
if( err != paNoError ) goto done;
err = Pa_StartStream( stream );
if( err != paNoError ) goto done;
printf("\n=== Now recording!! Please speak into the microphone. ===\n"); fflush(stdout);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
{
Pa_Sleep(1000);
printf("index = %d\n", data.frameIndex ); fflush(stdout);
printf("Channel = %d\n", data.currentChannel ); fflush(stdout);
}
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto done;
/* Measure maximum peak amplitude. */
/* average for each channel */
SAMPLE channel1val =0;
SAMPLE channel2val = 0;
SAMPLE channel3val =0;
SAMPLE channel4val = 0;
long channel1avg = 0.0;
long channel2avg =0.0;
long channel3avg =0.0;
long channel4avg =0.0;
SAMPLE channel1max = 0;
SAMPLE channel2max =0;
SAMPLE channel3max =0;
SAMPLE channel4max =0;
i = 0;
do
{
channel1val = data.recordedSamples[i];
if (channel1val < 0)
{
channel1val = -channel1val;
}
if (channel1val > channel1max)
{
channel1max = channel1val;
}
channel1avg += channel1val;
i = i + 4;
}
while (i<numSamples);
i = 1;
do
{
channel2val = data.recordedSamples[i];
if (channel2val < 0)
{
channel2val = -channel2val;
}
if (channel2val > channel2max)
{
channel2max = channel2val;
}
channel2avg += channel2val;
i = i + 4;
}
while (i<numSamples);
i = 2;
do
{
channel3val = data.recordedSamples[i];
if (channel3val < 0)
{
channel3val = -channel3val;
}
if (channel3val > channel3max)
{
channel3max = channel3val;
}
channel3avg += channel3val;
i = i + 4;
}
while (i<numSamples);
i = 3;
do
{
channel4val = data.recordedSamples[i];
if (channel4val < 0)
{
channel4val = -channel4val;
}
if (channel4val > channel4max)
{
channel4max = channel4val;
}
channel4avg += channel4val;
i = i + 4;
}
while (i<numSamples);
channel1avg = channel1avg / (double)numSamples;
channel2avg = channel2avg / (double)numSamples;
channel3avg = channel3avg / (double)numSamples;
channel4avg = channel4avg / (double)numSamples;
// printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max );
// printf("sample average = %lf\n", average );
printf("channel1 max amplitude = "PRINTF_S_FORMAT"\n", channel1max);
printf("sample average = %lf\n", channel1avg);
printf("channel2 max amplitude = "PRINTF_S_FORMAT"\n", channel2max);
printf("sample average = %lf\n", channel2avg);
printf("channel3 max amplitude = "PRINTF_S_FORMAT"\n", channel3max);
printf("sample average = %lf\n", channel3avg);
printf("channel4 max amplitude = "PRINTF_S_FORMAT"\n", channel4max);
printf("sample average = %lf\n", channel4avg);
printf("/nPrinting out values/n");
for (int j=0; j<8; j++)
{
printf("Value: %lf\n", data.recordedSamples[j]);
}
/* Write recorded data to a file. */
#if WRITE_TO_FILE
{
FILE *fid;
fid = fopen("recorded.raw", "wb");
if( fid == NULL )
{
printf("Could not open file.");
}
else
{
fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
fclose( fid );
printf("Wrote data to 'recorded.raw'\n");
}
}
#endif
/* Playback recorded data. -------------------------------------------- */
data.frameIndex = 0;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default output device.\n");
goto done;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
printf("\n=== Now playing back. ===\n"); fflush(stdout);
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
&data );
if( err != paNoError ) goto done;
if( stream )
{
err = Pa_StartStream( stream );
if( err != paNoError ) goto done;
printf("Waiting for playback to finish.\n"); fflush(stdout);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto done;
printf("Done.\n"); fflush(stdout);
}
done:
Pa_Terminate();
if( data.recordedSamples ) /* Sure it is NULL or valid. */
free( data.recordedSamples );
if( err != paNoError )
{
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 ) );
err = 1; /* Always return 0 or 1, but no other return codes. */
}
return err;
}
If I run my code and blow into one microphone I get this. When I play the sound back using my code, it works fine and the sound is correct, but looking at the values outputted:
channel1 max amplitude = 1.00000000
sample average = 1.000000
channel2 max amplitude = 0.02542114
sample average = 0.025421
channel3 max amplitude = 1.00000000
sample average = 1.000000
channel4 max amplitude = 0.02627563
sample average = 0.026276
Which clearly isn't correct. As it's showing that two channels are almost identical. From what I understand, as it's capturing linear PCM, it should be mapping channels such as
SAMPLE
[
{Channel1}
{Channel2}
{Channel3}
{Channel4}
]
Now the problem is, when I blow into one microphone on audacity (which uses core-audio drivers) I get this. Clearly one microphone has peak of 1 and the others are near silent.
So I don't understand what I'm doing wrong, any pointers?
It looks like you are only recording 2 channels
inputParameters.channelCount = 2; /* stereo input */
This would explain why ch 1 and 3, and ch 2 and 4 are measuring the same level because you're just skipping every other sample from the same channel.

encoding from rgb24 to mpeg4 and decoding from mpeg4 to rgb24 with libavcodec

Problem:
I am receiving rgb24 images from a webcam and am encoding them to mpeg4 using libavcodec. I am not able to decode them back to rgb24. avcodec_decode_video2 returns that it processed all of the input but the third parameter to avcodec_decode_video2 always returns 0.
What works:
Prior to calling avcodec_encode_video2, the rgb24 image is converted to yuv420p format via sws_scale. This works properly as I can take the result from sws_scale and convert it back to rgb24 successfully. I can view the results on the screen which is how I now this part works.
Description:
For each image returned from avcodec_encode_video2, I am calling another function that will decode it back to it rgb24. This scenario is setup for testing purposes to ensure I can encode/decode successfully. Below summarizes what is happening in the code:
/* variable declarations */
...
/* main processing */
while( true )
{
rgb24 = getRgb24();
sws_scale( ..., &rgb24, ..., frame->data, ... );
/* sws_scale is successful */
ret = avcodec_encode_video2( ..., &pkt, frame, &got_output );
/* avcodec_encode_video2 returns success and got_output is 1 */
/* copy encoded image to another buffer */
memcpy( hugebuffer, pkt.data, pkt.size );
memcpy( hugebuffer + pkt.size, 0, FF_INPUT_BUFFER_PADDING_SIZE );
av_init_packet( &decodePacket );
decodePacket.data = hugebuffer;
decodePacket.size = pkt.size;
len = avcodec_decode_video2( ..., decodeFrame, &got_output, &decodePacket );
/* len = pkt.size but got_output is ALWAYS 0 */
}
I wonder if there is something else I need to do before calling the decode function???
For reference, I have provided the actual code below which includes the following four functions: videoEncodeInit, videoEncode, videoDecodeInit, videoDecode. After that, I have posted how the functions are called.
/*
* Copyright (c) 2001 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* #file
* libavcodec API use example.
*
* Note that libavcodec only handles codecs (mpeg, mpeg4, etc...),
* not file formats (avi, vob, mp4, mov, mkv, mxf, flv, mpegts, mpegps, etc...). See library 'libavformat' for the
* format handling
* #example doc/examples/decoding_encoding.c
*/
#include <math.h>
extern "C"
{
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libswscale/swscale.h>
}
#include "video.h"
#include "logmsg.h"
typedef struct
{
AVCodec *codec;
AVCodecContext *c;
AVFrame *frame;
SwsContext *yuv420p_to_rgb24_ctx;
int frame_count;
} PrivateVideoDecodeParams;
static AVCodec *codec = 0;
static AVCodecContext *c = 0;
static AVFrame *frame = 0;
static SwsContext *rgb24_to_yuv420p_ctx = 0;
static int registered = 0;
extern int g_debug; /* from main.cpp */
int videoEncodeInit( VideoEncodeParams *ep )
{
int ret;
if( registered == 0 )
{
avcodec_register_all();
registered = 1;
}
if( c != 0 )
{
avcodec_close(c);
av_free(c);
}
if( frame && frame->data[0] )
{
av_freep(&frame->data[0]);
avcodec_free_frame(&frame);
}
if( rgb24_to_yuv420p_ctx )
{
sws_freeContext( rgb24_to_yuv420p_ctx );
}
/* find the mpeg1 video encoder */
codec = avcodec_find_encoder(ep->codec_id);
if (!codec)
{
logmsg( "error - Codec=%d not found [%s:%d]\n", (int)ep->codec_id, __FILE__, __LINE__ );
return( -1 );
}
c = avcodec_alloc_context3(codec);
if (!c)
{
logmsg("error - Could not allocate video codec context [%s:%d]\n", __FILE__, __LINE__ );
return(-1);
}
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = ep->width; /* was hard coded at 352 */
c->height = ep->height; /* was hard coded at 288 */
/* frames per second */
c->time_base.den = 1;
c->time_base.num = ep->fps; /* was hard coded to 25 */
c->gop_size = 10; /* emit one intra frame every ten frames */
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if(ep->codec_id == AV_CODEC_ID_H264)
{
av_opt_set(c->priv_data, "preset", "slow", 0);
}
/* open it */
ret = avcodec_open2(c, codec, NULL);
if( ret < 0)
{
logmsg( "error - Could not open codec [%s:%d]\n", __FILE__, __LINE__ );
return(-1);
}
frame = avcodec_alloc_frame();
if (!frame)
{
logmsg("error - Could not allocate video frame [%s:%d]\n", __FILE__, __LINE__ );
return(-1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc() is to be used */
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
if (ret < 0)
{
logmsg("error - Could not allocate raw picture buffer [%s:%d]\n", __FILE__, __LINE__ );
return( -1 );
}
rgb24_to_yuv420p_ctx = sws_getContext( ep->width, ep->height, AV_PIX_FMT_RGB24,
ep->width, ep->height, AV_PIX_FMT_YUV420P,
SWS_FAST_BILINEAR, 0, 0, 0 );
if( rgb24_to_yuv420p_ctx == 0 )
{
logmsg( "error - failed to create rb24 to yuv420p conversion scale [%s:%d]\n",
__FILE__, __LINE__ );
return( -1 );
}
return( 0 );
}
int videoEncode( VideoEncodeParams *ep )
{
AVPacket pkt;
int ret;
int got_output;
av_init_packet(&pkt);
pkt.data = 0; // packet data will be allocated by the encoder
pkt.size = 0;
frame->pts = ep->pts;
ep->pts++;
/* convert input to that expected by the encoder */
int srcStride = c->width * 3;
ret = sws_scale( rgb24_to_yuv420p_ctx, &ep->rgb24, &srcStride, 0, c->height, frame->data, frame->linesize );
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0)
{
logmsg("error - Error encoding frame [%s:%d]\n", __FILE__, __LINE__ );
return(-1);
}
if (got_output)
{
if( g_debug )
{
logmsg("debug - Write frame %3d (size=%5d)\n", ep->pts-1, pkt.size);
}
ep->callback( ep, pkt.data, pkt.size );
av_free_packet(&pkt);
}
return( 0 );
}
int videoDecodeInit( VideoDecodeParams *dp )
{
PrivateVideoDecodeParams *pdp;
int ret;
if( registered == 0 )
{
avcodec_register_all();
registered = 1;
}
if( dp->rgb24_out )
{
free( dp->rgb24_out );
}
dp->rgb24_out = (unsigned char*)calloc( 1, dp->width * 3 * dp->height );
pdp = (PrivateVideoDecodeParams*)dp->priv;
if( pdp )
{
if( pdp->c != 0 )
{
avcodec_close(pdp->c);
av_free(pdp->c);
}
if( pdp->frame && pdp->frame->data[0] )
{
av_freep(&pdp->frame->data[0]);
avcodec_free_frame(&pdp->frame);
}
if( pdp->yuv420p_to_rgb24_ctx )
{
sws_freeContext( pdp->yuv420p_to_rgb24_ctx );
}
free( pdp );
}
dp->priv = calloc( 1, sizeof(*pdp) );
pdp = (PrivateVideoDecodeParams*)dp->priv;
/* find the video decoder */
if( pdp->codec == 0 )
{
pdp->codec = avcodec_find_decoder(dp->codec_id);
if (!pdp->codec)
{
logmsg("error - Codec not found=%d [%s:%d]\n", (int)dp->codec_id, __FILE__, __LINE__ );
return( -1 );
}
}
pdp->c = avcodec_alloc_context3(pdp->codec);
if (!pdp->c)
{
logmsg("error - Could not allocate video codec context [%s:%d]\n", __FILE__, __LINE__ );
return( -1 );
}
if(pdp->codec->capabilities & CODEC_CAP_TRUNCATED)
{
pdp->c->flags |= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
}
/* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */
/* put sample parameters */
pdp->c->bit_rate = 400000;
/* resolution must be a multiple of two */
pdp->c->width = dp->width;
pdp->c->height = dp->height;
pdp->c->time_base.den = 1;
pdp->c->time_base.num = 25; /* was hard coded to 25 */
pdp->c->gop_size = 10; /* emit one intra frame every ten frames */
pdp->c->max_b_frames = 1;
pdp->c->pix_fmt = AV_PIX_FMT_YUV420P;
if(dp->codec_id == AV_CODEC_ID_H264)
{
av_opt_set(pdp->c->priv_data, "preset", "slow", 0);
}
/* open it */
if (avcodec_open2(pdp->c, pdp->codec, NULL) < 0)
{
logmsg("error - Could not open codec [%s:%d]\n", __FILE__, __LINE__ );
return( -1 );
}
pdp->frame = avcodec_alloc_frame();
if (!pdp->frame)
{
logmsg("error - Could not allocate video frame [%s:%d]\n", __FILE__, __LINE__ );
return( -1 );
}
pdp->frame->format = AV_PIX_FMT_YUV420P;
pdp->frame->width = c->width;
pdp->frame->height = c->height;
#if 0
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc() is to be used */
ret = av_image_alloc(pdp->frame->data, pdp->frame->linesize, pdp->c->width, pdp->c->height, pdp->c->pix_fmt, 32);
if (ret < 0)
{
logmsg("error - Could not allocate raw picture buffer [%s:%d]\n", __FILE__, __LINE__ );
return( -1 );
}
#endif
pdp->yuv420p_to_rgb24_ctx = sws_getContext( dp->width, dp->height, AV_PIX_FMT_YUV420P,
dp->width, dp->height, AV_PIX_FMT_RGB24,
SWS_FAST_BILINEAR, 0, 0, 0 );
if( pdp->yuv420p_to_rgb24_ctx == 0 )
{
logmsg( "error - failed to create yuv420p to rb24 conversion scale [%s:%d]\n",
__FILE__, __LINE__ );
return( -1 );
}
return( 0 );
}
int videoDecode( VideoDecodeParams *dp )
{
static uint8_t *inbuf = 0;
static int inbufLen = 0;
PrivateVideoDecodeParams *pdp;
AVPacket avpkt;
int len;
int got_frame;
int srcStride;
int ret;
pdp = (PrivateVideoDecodeParams*)dp->priv;
/* Make sure we have a buffer to work with. */
if( inbuf == 0 )
{
/* get a big buffer */
inbufLen = 256000;
inbuf = (uint8_t*)calloc( 1, inbufLen );
}
if( (dp->yuv420_len + FF_INPUT_BUFFER_PADDING_SIZE) > inbufLen )
{
if( inbuf )
{
free( inbuf );
}
inbufLen = dp->yuv420_len + FF_INPUT_BUFFER_PADDING_SIZE;
inbuf = (uint8_t*)calloc( 1, inbufLen );
}
/* copy the video to our buffer */
memcpy( inbuf, dp->yuv420_in, dp->yuv420_len );
/* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
memset( inbuf + dp->yuv420_len, 0, FF_INPUT_BUFFER_PADDING_SIZE );
av_init_packet(&avpkt);
avpkt.size = dp->yuv420_len;
avpkt.data = inbuf;
len = avcodec_decode_video2(pdp->c, pdp->frame, &got_frame, &avpkt);
if (len < 0)
{
logmsg("error - Error while decoding frame %d [%s:%d]\n", pdp->frame_count, __FILE__, __LINE__ );
return len;
}
if (got_frame)
{
logmsg( "got a frame\n" );
/* convert to RGB24 */
srcStride = pdp->c->width * 3;
ret = sws_scale( pdp->yuv420p_to_rgb24_ctx, pdp->frame->data, pdp->frame->linesize, 0, pdp->c->height, &dp->rgb24_out, &srcStride );
pdp->frame_count++;
}
else
{
logmsg( "no frame\n" );
}
return( got_frame );
}
Here's how the init functions are called:
videoEncodeInit :
ep.codec_id = AV_CODEC_ID_MPEG4;
ep.width = 352;
ep.height = 288;
ep.fps = 10;
ep.rgb24 = 0;
ep.pts = 0;
ep.callback = debug_videoEncodeCallback;
if( videoEncodeInit( &ep ) == -1 )
{
MyMessageBox( g_mainwindow, "Can't initialize video codec!", "Error", MB_OK | MB_ICONERROR );
exit( -1 );
}
videoDecodeInit:
dp.codec_id = AV_CODEC_ID_MPEG4;
dp.width = 352;
dp.height = 288;
videoDecodeInit( &dp );
videoEncode:

Continuous recording in PortAudio (from mic or output)

I am trying to create a music visualizer application in PortAudio, I did some basic research and found some examples on how to record from a mic to a (temporary) file. But there was no example where the data is not used runtime during the recording.
So how can I start a continuous audio-stream where I can catch the data from the current "frame"?
This is how I tried to do it:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
typedef struct{
int frameIndex;
int maxFrameIndex;
char* recordedSamples;
}
testData;
PaStream* stream;
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
testData* data = (testData*)userData;
const char* buffer_ptr = (const char*)inputBuffer;
char* index_ptr = &data->recordedSamples[data->frameIndex];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
if(framesLeft < frameCount){
framesToCalc = framesLeft;
finished = paComplete;
}else{
framesToCalc = frameCount;
finished = paContinue;
}
if(inputBuffer == NULL){
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
}else{
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = *buffer_ptr++;
}
}
data->frameIndex += framesToCalc;
return finished;
}
int setup(testData streamData){
PaError err;
err = Pa_Initialize();
if(err != paNoError){
fprintf(stderr, "Pa_Initialize error: %s\n", Pa_GetErrorText(err));
return 1;
}
PaStreamParameters inputParameters;
inputParameters.device = Pa_GetDefaultInputDevice();
if (inputParameters.device == paNoDevice) {
fprintf(stderr, "Error: No default input device.\n");
return 1;
}
inputParameters.channelCount = 1;
inputParameters.sampleFormat = paInt8;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, 256, paClipOff, recordCallback, &streamData);
if(err != paNoError){
fprintf(stderr, "Pa_OpenDefaultStream error: %s\n", Pa_GetErrorText(err));
return 1;
}
err = Pa_StartStream(stream);
if(err != paNoError){
fprintf(stderr, "Pa_StartStream error: %s\n", Pa_GetErrorText(err));
return 1;
}
return 0;
}
void quit(testData streamData){
PaError err;
err = Pa_Terminate();
if(err != paNoError){
fprintf(stderr, "Pa_Terminate error: %s\n", Pa_GetErrorText(err));
}
if(streamData.recordedSamples)
free(streamData.recordedSamples);
}
int main(){
int i;
PaError err;
testData streamData = {0};
streamData.frameIndex = 0;
streamData.maxFrameIndex = SAMPLE_RATE;
streamData.recordedSamples = (char*)malloc(SAMPLE_RATE * sizeof(char));
if(streamData.recordedSamples == NULL)
printf("Could not allocate record array.\n");
for(i=0; i<SAMPLE_RATE; i++)
streamData.recordedSamples[i] = 0;
//int totalFrames = SAMPLE_RATE;
if(!setup(streamData)){
printf("Opened\n");
int i = 0;
while(i++ < 500){
if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
break;
while((err = Pa_IsStreamActive(stream)) == 1){
Pa_Sleep(1000);
}
err = Pa_CloseStream(stream);
if(err != paNoError)
break;
streamData.frameIndex = 0;
for(i=0; i<SAMPLE_RATE; i++)
streamData.recordedSamples[i] = 0;
}
if(err != paNoError){
fprintf(stderr, "Active stream error: %s\n", Pa_GetErrorText(err));
}
quit(streamData);
}else{
puts("Couldn't open\n");
}
return 0;
}
But it gives the following output:
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
Active stream error: Can't read from a callback stream
Update:
What is the purpose of this code?
if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
break;
It seems to me like this is causing your (latest) problem. Why do you need to retrieve (and then discard) the number of frames that can be read from the stream without waiting, which would presumably be zero since stream is a callback stream?
Previous answer:
This seems highly suspicious:
static void* data;
/* ... */
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
testData* data = (testData*)userData;
/* ... */
}
Firstly, why are there two variables named data? That's just silly... Can you think of more appropriate identifiers?
Secondly, you're passing a &data (a void **) to Pa_OpenStream. Presumably, Pa_OpenStream passes that same value on to your callback function, where you treat that pointer to void * as though it points to a testData *. That's undefined behaviour.
Remove static void* data;. That's not necessary, here. Declare a new testData data = { 0 }; inside main, right at the very top. Now you're passing a testData * (converted to void *) to Pa_OpenStream, Pa_OpenStream will pass that on to your callback where you can safely convert it back to a testData * as you are. You might want to set the members of data before calling Pa_OpenStream...
To interact with the data stream real-time you'll need a mechanism that either sleeps (busy waits on Windows) for the frame period (sample rate / samples per frame) or uses a thread synchronization primitive to trigger your thread int main that there is audio ready to be processed. This will give you access to each frame of data that PortAudio delivers during its callback (called from the PortAudio thread). Here's how the concept works using boost::condition and boost::mutex.
//CAUTION THIS SNIPPET IS ONLY INTENDED TO DEMONSTRATE HOW ONE MIGHT
//SYNCHRONIZE WITH THE PORTAUDIO THREAD
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include "portaudio.h"
boost::condition waitForAudio;
boost::mutex waitForAudioMutex;
boost::mutex audioBufferMutex;
bool trigger = false;
std::deque<char> audioBuffer;
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
const char* buffer_ptr = (const char*)inputBuffer;
//Lock mutex to block user thread from modifying data buffer
audioBufferMutex.lock();
//Copy data to user buffer
for(i = 0; i < frameCount; ++i) {
audioBuffer.push_back(buffer_ptr + i);
}
//Unlock mutex, allow user to manipulate buffer
audioBufferMutex.unlock();
//Signal user thread to process audio
waitForAudioMutex.lock();
trigger= true;
waitForAudio.notify_one();
waitForAudioMutex.unlock();
return finished;
}
int main(){
Pa_Initialize();
//OPEN AND START PORTAUDIO STREAM
while(true){ //Catch signal (Ctrl+C) or some other mechanism to interrupt this loop
boost::xtime duration;
boost::xtime_get(&duration, boost::TIME_UTC);
boost::interprocess::scoped_lock<boost::mutex> lock(waitForAudioMutex);
if(!trigger) {
if(!waitForAudio.timed_wait(lock, duration)) {
//Condition timed out -- assume audio stream failed
break;
}
}
trigger= false;
audioBufferMutex.lock();
//VISUALIZE AUDIO HERE
//JUST MAKE SURE TO FINISH BEFORE PORTAUDIO MAKES ANOTHER CALLBACK
audioBufferMutex.unlock();
}
//STOP AND CLOSE PORTAUDIO STEAM
Pa_Terminate();
return 0;
}
Generally, this technique is cross-platform, however this specific implementation may only work on Linux. On Windows use SetEvent(eventVar) in place of condition::notify_one() and WaitForSingleObject(eventVar, duration) instead of condition::timed_wait(lock, duration).
You closed the stream err = Pa_CloseStream(stream); in the first iteration. In the second iteration, the channel was already closed. Try the operation err = Pa_CloseStream(stream); after all iterations.
I solved it this way:
PaStreamParameters inputParameters ,outputParameters;
PaStream* stream;
PaError err;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
err = paNoError;
inputParameters.device = 4;
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE;
data.frameIndex = 0;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
std::ofstream arch;
arch.open("signal.csv");
err = Pa_Initialize();
inputParameters.channelCount = 1;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
int contador = 0;
bool strec = true;
while (strec)
{
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
recordCallback,
&data );
err = Pa_StartStream( stream );
printf("\n===Grabando.... ===\n"); fflush(stdout);
Pa_Sleep(3000);
while( ( err = Pa_IsStreamActive(stream) ) == 1 )
{
}
err = Pa_CloseStream( stream );
for( i=0; i<numSamples; i++ )
{
val = data.recordedSamples[i];
arch << val << std::endl;
std::cout << std::endl << "valor : " << val;
}
data.frameIndex = 0;
contador++;
if (contador >= 100) //if you delete this condition continuously recorded this audio
{
strec = false;
arch.close();
}
}

Resources