SXXXXXXX_RadarDataReader/_src_cpp/Files/AviFile.cpp
VALLONGOL 5de2650675 add
2025-11-12 13:43:30 +01:00

808 lines
18 KiB
C++

/*
* AviFile.cpp
*
* Created on: 08/mar/2022
* Author: chessaa
*/
#include "AviFile.h"
#include <windows.h>
#include <mmsystem.h>
#include <vfw.h>
#include "gtl_cmdio.h"
#include <stdint.h>
//#include <atomic>
//Audio capture
#define BITS_PER_SAMPLE 8
#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
const int BUFFER_DURATION = 1;
const int CHANNEL_COUNT = 1;
const int SAMPLE_RATE = 22050; //48000; //44100;
const int DATA_SIZE = BYTES_PER_SAMPLE* SAMPLE_RATE * BUFFER_DURATION * CHANNEL_COUNT;
//const int BUFFER_SIZE = 10;
const int NUMBER_OF_BUFFERS = 8;
const unsigned int WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE = 0x0010;
AviFile* AviFile_instance;
#define AVI_LOG cmdio_debug
#ifdef CAPTURE_AUDIO
class AudioCaptureImplementation
{
public:
void(*callback)(void* cookie, WAVEHDR data);
void* callback_cookie;
bool running;
MMRESULT res;
HWAVEIN micHandle;
WAVEFORMATEX format;
WAVEHDR buffers[32];
unsigned int buffers_data[32][DATA_SIZE];
volatile uint64_t buffers_queue;
bool addBuffer(WAVEHDR *buffer);
static void CALLBACK waveInProc(
HWAVEIN hwi,
UINT uMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2);
void Update();
bool StartCapture();
void StopCapture();
void closeCapture();
CRITICAL_SECTION myCs;
unsigned int time;
AudioCaptureImplementation():
callback(0),
callback_cookie(0),
running(false),
res(0),
micHandle(0),
buffers_queue(0),
time(0)
{
memset(&format, 0, sizeof format);
memset(buffers, 0, sizeof buffers);
InitializeCriticalSection(&myCs);
}
virtual ~AudioCaptureImplementation()
{
}
void lock()
{
EnterCriticalSection(&myCs);
}
void unlock()
{
LeaveCriticalSection(&myCs);
}
int flush(bool disregard=false);
};
void CALLBACK AudioCaptureImplementation::waveInProc(
HWAVEIN hwi,
UINT uMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2)
{
if (uMsg == WIM_DATA)
{
WAVEHDR* hdr = (WAVEHDR*)dwParam1;
int id = hdr->dwUser;
AudioCaptureImplementation& instance=*reinterpret_cast<AudioCaptureImplementation*>(dwInstance);
instance.lock();
instance.buffers_queue<<=8;
instance.buffers_queue|=(id+1);
#if 0
WAVEHDR& buffer=instance.buffers[id];
//cmdio_psuccess("Audio Buffer: %d 0x%X %u samples", id, instance.buffers_queue, buffer.dwBytesRecorded);
#endif
instance.unlock();
}
}
bool AudioCaptureImplementation::addBuffer(WAVEHDR* buffer)
{
res = waveInPrepareHeader(micHandle, buffer, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
cmdio_pfail("waveInPrepareHeader() failed %u", res);
return false;
}
res = waveInAddBuffer(micHandle, buffer, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
cmdio_pfail("waveInAddBuffer() failed %u", res);
return false;
}
return true;
}
bool AudioCaptureImplementation::StartCapture()
{
if (micHandle==0)
{
memset(&format, 0, sizeof format);
format.nChannels = CHANNEL_COUNT;
format.cbSize = 0;
format.nSamplesPerSec = SAMPLE_RATE;
format.wFormatTag = WAVE_FORMAT_PCM;
format.wBitsPerSample = BITS_PER_SAMPLE;
format.nBlockAlign = (CHANNEL_COUNT * format.wBitsPerSample) / 8;
format.nAvgBytesPerSec = SAMPLE_RATE * format.nBlockAlign;
res=waveInOpen(&micHandle, WAVE_MAPPER, &format, (DWORD_PTR)waveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION|WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
if (res != MMSYSERR_NOERROR)
{
cmdio_perror("waveInOpen() failed");
micHandle=0;
return false;
}
for (int i = 0; i < NUMBER_OF_BUFFERS; i++)
{
WAVEHDR* header=&buffers[i]; //new WAVEHDR;
ZeroMemory(header, sizeof(*header));
header->lpData = (char*)buffers_data[i]; //(LPSTR)new short int[DATA_SIZE];
header->dwBufferLength = DATA_SIZE;
header->dwBytesRecorded = 0;
header->dwUser = 0;
header->dwFlags = 0;
header->dwLoops = 0;
header->dwUser = i;
//buffers[i] = header;
bool ok=addBuffer(&buffers[i]);
if (ok)
cmdio_printf(AVI_LOG, "Audio: adding buffer %d 0x%X", i, header->lpData);
}
}
waveInStart(micHandle);
return true;
}
void AudioCaptureImplementation::StopCapture()
{
if (micHandle)
waveInStop(micHandle);
}
void AudioCaptureImplementation::closeCapture()
{
if (micHandle)
{
waveInClose(micHandle);
}
}
int AudioCaptureImplementation::flush(bool disregard)
{
int one_sec=0;
while(buffers_queue)
{
lock();
unsigned int id=buffers_queue & 0x0FF;
buffers_queue>>=8;
unlock();
if (id==0)
continue;
WAVEHDR& buffer=buffers[id-1];
cmdio_printf(AVI_LOG, "Audio::flush(): %d 0x%p %u", id, buffer.lpData, buffer.dwBytesRecorded);
if (!disregard)
{
if (callback)
callback(callback_cookie, buffer);
}
addBuffer(&buffer);
++one_sec;
}
return one_sec;
}
#else
class AudioCaptureImplementation
{
public:
bool addBuffer(WAVEHDR */*buffer*/)
{
return false;
}
void* callback_cookie;
void(*callback)(void* cookie, WAVEHDR data);
static void CALLBACK waveInProc(
HWAVEIN /*hwi*/,
UINT /*uMsg*/,
DWORD_PTR /*dwInstance*/,
DWORD_PTR /*dwParam1*/,
DWORD_PTR /*dwParam2*/)
{
}
void Update()
{
}
bool StartCapture()
{
return false;
}
void StopCapture()
{
}
void closeCapture()
{
}
void lock()
{
//EnterCriticalSection(&myCs);
}
void unlock()
{
//LeaveCriticalSection(&myCs);
}
int flush(bool /*disregard=false*/)
{
return 0;
}
};
#endif
class AviFile::Implementation
{
public:
struct stream_t
{
bool ready;
PAVISTREAM ps;
PAVISTREAM psCompressed;
PAVISTREAM psWrite;
AVISTREAMINFO strhdr;
AVICOMPRESSOPTIONS opts;
BITMAPINFOHEADER bi;
WAVEFORMATEX format;
unsigned long time;
void close()
{
if (ready)
{
ready=false;
if (ps)
AVIStreamClose(ps);
if (psCompressed)
AVIStreamClose(psCompressed);
}
}
};
PAVIFILE avif;
char fname[512];
HRESULT hr;
stream_t videoStream;
stream_t audioStream;
stream_t textStream;
AudioCaptureImplementation audioCapturer;
static DWORD myFOURCC()
{
//return mmioFOURCC('M', 'P', '4', 'V'); //('M', 'S', 'V', 'C');
return mmioFOURCC('M','S','V','C');
}
Implementation():
avif(0),
hr(0)
{
memset(&videoStream, 0, sizeof videoStream);
memset(&audioStream, 0, sizeof audioStream);
memset(&textStream, 0, sizeof textStream);
}
bool createTextStrem_()
{
stream_t& s=textStream;
memset(&s, 0, sizeof s);
s.strhdr.fccType = streamtypeTEXT;
s.strhdr.fccHandler = 0;
s.strhdr.dwScale = 1;
s.strhdr.dwRate = 30;
s.strhdr.dwLength = 1;
s.strhdr.dwQuality = 0;
strcpy(s.strhdr.szName, "SUBT");
hr = AVIFileCreateStream(avif, // file pointer
&s.ps, // returned stream pointer
&s.strhdr); // stream header
if (hr != AVIERR_OK)
{
cmdio_oserror("Video::AVIFileCreateStream(txts)");
return false;
}
s.opts.fccType = streamtypeTEXT;
s.opts.dwKeyFrameEvery = 1;
s.opts.dwQuality=0;
s.opts.dwFlags=0; // = AVICOMPRESSF_KEYFRAMES|AVICOMPRESSF_VALID;
s.opts.lpFormat=&s.format;
s.opts.cbFormat=sizeof s.format;
hr = AVIStreamSetFormat(s.ps, 0, &s.format, sizeof s.format);
if (hr != AVIERR_OK)
{
cmdio_oserror("Audio::AVIStreamSetFormat()");
return false;
}
s.ready=true;
return true;
}
bool createVideoStream(int rate, int rectwidth, int rectheight, int key, int quality)
{
stream_t& s=videoStream;
memset(&s, 0, sizeof s);
if (!avif)
return false;
DWORD buffersize=rectwidth*rectheight*sizeof(uint32_t);
s.strhdr.fccType = streamtypeVIDEO;// stream type
s.strhdr.fccHandler = myFOURCC(); //mmioFOURCC('M','S','V','C'); // Microsoft video 1
//strhdr.fccHandler = mmioFOURCC('I','V','5','0'); // Intel video 5.0
//strhdr.dwFlags = AVISTREAMINFO_DISABLED;
//strhdr.dwCaps =
//strhdr.wPriority =
//strhdr.wLanguage =
s.strhdr.dwScale = 10;
s.strhdr.dwRate = rate; // rate fps
//strhdr.dwStart =
//strhdr.dwLength =
//strhdr.dwInitialFrames =
s.strhdr.dwSuggestedBufferSize = buffersize;
s.strhdr.dwQuality = -1; // use the default
strcpy(s.strhdr.szName, "video");
//strhdr.dwSampleSize =
SetRect(&s.strhdr.rcFrame, 0, 0, // rectangle for stream
(int) rectwidth,
(int) rectheight);
hr = AVIFileCreateStream(avif, // file pointer
&s.ps, // returned stream pointer
&s.strhdr); // stream header
if (hr != AVIERR_OK)
{
cmdio_oserror("Video::AVIFileCreateStream()");
return false;
}
s.opts.fccType = streamtypeVIDEO;
s.opts.fccHandler = myFOURCC();
//opts.fccHandler = 0;
//opts.fccHandler = mmioFOURCC('D','I','B',' '); // Uncompressed
//opts.fccHandler = mmioFOURCC('C','V','I','D'); // Cinpak
//opts.fccHandler = mmioFOURCC('I','V','3','2'); // Intel video 3.2
//opts.fccHandler = mmioFOURCC('M','S','V','C'); // Microsoft video 1
//opts.fccHandler = mmioFOURCC('I','V','5','0'); // Intel video 5.0
s.opts.dwKeyFrameEvery = key; //-1; //50;
s.opts.dwQuality=quality; //-1;
//opts.dwBytesPerSecond
s.opts.dwFlags = AVICOMPRESSF_KEYFRAMES|AVICOMPRESSF_VALID;
//opts.lpFormat
//opts.cbFormat
//opts.lpParms
//opts.cbParms
//opts.dwInterleaveEvery
/* display the compression options dialog box if specified compressor is unknown */
hr = AVIMakeCompressedStream(&s.psCompressed, s.ps, &s.opts, 0);
if (hr != AVIERR_OK)
{
cmdio_oserror("Video::AVIMakeCompressedStream()");
return false;
}
s.bi.biHeight=rectheight;
s.bi.biWidth=rectwidth;
s.bi.biCompression=0;
s.bi.biPlanes=1;
s.bi.biBitCount=32;
s.bi.biSizeImage=s.bi.biHeight*s.bi.biWidth*4;
s.bi.biSize=sizeof s.bi;
hr = AVIStreamSetFormat(s.psCompressed, 0,
&s.bi, s.bi.biSize + s.bi.biClrUsed * sizeof(RGBQUAD));
if (hr != AVIERR_OK)
{
cmdio_oserror("Video::AVIStreamSetFormat()");
return false;
}
s.ready=true;
return true;
}
bool addVideoFrame(const void* data)
{
stream_t& s=videoStream;
if (!s.ready)
{
cmdio_pwarning("addVideoFrame(): stream not ready");
return false;
}
const DWORD ImageSize=s.bi.biSizeImage;
hr = AVIStreamWrite(s.psCompressed, // stream pointer
s.time, // time of this frame
1, // number to write
const_cast<void*>(data),
ImageSize, // lpbi->biSizeImage, // size of this frame
s.time==0 ? AVIIF_KEYFRAME : 0, // flags....
NULL,
NULL);
++s.time;
if (hr != AVIERR_OK)
{
//CString strMsg;
//strMsg.Format("Error: AVIStreamWrite, error %d",hr);
cmdio_oserror("addVideoFrame()=%d",hr);
return false;
}
return true;
}
bool createAudioStream(int rate, int ch, int bytes_per_sample)
{
stream_t& s=audioStream;
memset(&s, 0, sizeof s);
if (!avif)
return false;
s.format.nChannels=ch;
s.format.nSamplesPerSec=rate;
s.format.wFormatTag=WAVE_FORMAT_PCM;
s.format.wBitsPerSample=bytes_per_sample*8;
s.format.nBlockAlign=(ch*s.format.wBitsPerSample)/8;
s.format.nAvgBytesPerSec=ch*rate*s.format.nBlockAlign;
s.strhdr.fccType = streamtypeAUDIO;// stream type
s.strhdr.dwScale = 1;
s.strhdr.dwRate = rate; // rate fps
//strhdr.dwStart =
//strhdr.dwLength =
//strhdr.dwInitialFrames =
s.strhdr.dwSuggestedBufferSize = 0; //rate;
s.strhdr.dwQuality = -1; // use the default
s.strhdr.rcFrame=videoStream.strhdr.rcFrame;
strcpy(s.strhdr.szName, "audio");
hr = AVIFileCreateStream(avif, // file pointer
&s.ps, // returned stream pointer
&s.strhdr); // stream header
if (hr != AVIERR_OK)
{
cmdio_oserror("Audio::AVIFileCreateStream()");
return false;
}
s.psWrite=s.ps;
s.opts.fccType = streamtypeAUDIO;
s.opts.dwKeyFrameEvery = 1;
s.opts.dwQuality=-1;
//opts.dwBytesPerSecond
s.opts.dwFlags=0; // = AVICOMPRESSF_KEYFRAMES|AVICOMPRESSF_VALID;
s.opts.lpFormat=&s.format;
s.opts.cbFormat=sizeof s.format;
//opts.lpFormat
//opts.cbFormat
//opts.lpParms
//opts.cbParms
//opts.dwInterleaveEvery
/* display the compression options dialog box if specified compressor is unknown */
#if 0
hr = AVIMakeCompressedStream(&s.psCompressed, s.ps, &s.opts, 0);
if (hr != AVIERR_OK)
{
cmdio_oserror("Audio::AVIMakeCompressedStream()");
return false;
}
s.psWrite=s.psCompressed;
#endif
hr = AVIStreamSetFormat(s.psWrite, 0, &s.format, sizeof s.format);
if (hr != AVIERR_OK)
{
cmdio_oserror("Audio::AVIStreamSetFormat()");
return false;
}
s.ready=true;
audioCapturer.callback=audio_callback;
audioCapturer.callback_cookie=this;
return true;
}
bool addAudioSamples(int size, const void* data)
{
stream_t& s=audioStream;
int numsmaples=size/(s.format.wBitsPerSample/8);
if (!s.ready)
{
cmdio_pwarning("addAudioSamples(): stream not ready");
return false;
}
if (!s.psWrite)
{
cmdio_pfail("addAudioSamples(): no write stream");
return false;
}
hr = AVIStreamWrite(s.psWrite, // stream pointer
s.time, // time of this frame
numsmaples, // number to write
const_cast<void*>(data),
size, // lpbi->biSizeImage, // size of this frame
0, 0, 0);
s.time+=numsmaples;
if (hr != AVIERR_OK)
{
//CString strMsg;
//strMsg.Format("Error: AVIStreamWrite, error %d",hr);
cmdio_oserror("addAudioSamples()=%d",hr);
return false;
}
return true;
}
static void audio_callback(void* cookie, WAVEHDR hdr)
{
cmdio_printf(AVI_LOG, "Audio::audio_callback(): %d", hdr.dwBytesRecorded);
AviFile::Implementation* instance=reinterpret_cast<AviFile::Implementation*>(cookie);
instance->addAudioSamples(hdr.dwBytesRecorded, hdr.lpData);
}
};
AviFile::AviFile():
p_(*new Implementation)
{
AVIFileInit();
}
AviFile::~AviFile()
{
close();
delete &p_;
}
bool AviFile::open(const char* name, bool add_date)
{
if (name)
{
strncpy(p_.fname, name, sizeof p_.fname);
}
else
strcpy(p_.fname, "avi_file.avi");
if (p_.avif)
close();
HRESULT hr = AVIFileOpen(&p_.avif, // returned file pointer
p_.fname, // file name
OF_WRITE | OF_CREATE, // mode to open file with
0); // use handler determined
// from file extension....
if (hr != AVIERR_OK)
{
cmdio_oserror("AviFile::open(%s)", p_.fname);
return false;
}
cmdio_psuccess("AviFile::open(%s)", p_.fname);
return true;
}
bool AviFile::isOpen() const
{
return p_.avif!=0;
}
bool AviFile::close()
{
p_.audioCapturer.StopCapture();
p_.videoStream.close();
p_.audioStream.close();
//addSubtitle(0, 0);
if (p_.avif)
{
AVIFileClose(p_.avif);
p_.avif=0;
cmdio_psuccess("Video saved: %s", p_.fname);
}
return true;
}
bool AviFile::openVideoStream(int rate,int w, int h, int key, int quality)
{
return p_.createVideoStream(rate, w, h,key, quality);
}
bool AviFile::addVideoFrame(const void* data)
{
//if (!p_.textStream.ready) p_.createTextStrem();
return p_.addVideoFrame(data);
}
bool AviFile::openAudioStream(int rate, int ch, int bytes_per_sample)
{
return p_.createAudioStream(rate, ch, bytes_per_sample);
}
bool AviFile::openAudioStream()
{
return p_.createAudioStream(SAMPLE_RATE, 1, BYTES_PER_SAMPLE);
}
bool AviFile::addAudioSamples(int samples, const void* data)
{
return p_.addAudioSamples(samples, data);
}
bool AviFile::audioCapture(bool enable)
{
if (enable)
return p_.audioCapturer.StartCapture();
else
{
p_.audioCapturer.StopCapture();
return true;
}
}
int AviFile::audioFlush(bool disregard)
{
return p_.audioCapturer.flush(disregard);
}
AviFile* AviFile::instance()
{
if (AviFile_instance==0)
{
AviFile_instance=new AviFile;
}
return AviFile_instance;
}
static void ascii2UTF16(char* dst, const char* src)
{
for(int i=0; src[i]; ++i)
{
dst[i*2]=src[i];
dst[i*2+1]=0;
}
}
void AviFile::addSubtitle(const char* data, unsigned int size)
{
return;
#if 0
struct txt_header_t
{
unsigned char gab2[4];
unsigned char byte0;
unsigned short ucode02;
unsigned int name_size;
char name[32];
unsigned short w04;
unsigned int txt_size;
char fake[2048];
} __attribute__((packed));
static txt_header_t txt;
if (!p_.textStream.ready)
{
bool ok=p_.createTextStrem();
if (!ok)
return;
}
txt.gab2[0]='G'; txt.gab2[1]='A'; txt.gab2[2]='B'; txt.gab2[3]='2';
txt.ucode02=2;
txt.name_size=0;
txt.txt_size=1024;
txt.w04=0x04;
txt.name_size=sizeof txt.name;
ascii2UTF16(txt.name, "TXT");
ascii2UTF16(txt.fake,
"1\n"
"00::00:10,000 --> 00:00:24,400\n"
"prova prova!!!\n\n");
LONG sampleWritten=0;
LONG bytesWritten=0;
AVIStreamWrite(p_.textStream.ps, 0, 1, &txt, sizeof txt, 0, &sampleWritten, &bytesWritten);
cmdio_pinfo("txt %d %d", sampleWritten, bytesWritten);
p_.textStream.close();
#endif
}
void AviFile::addSubtitleInstance(const char* data, unsigned int size)
{
if (!AviFile_instance)
return;
AviFile_instance->addSubtitle(data, size);
}