808 lines
18 KiB
C++
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);
|
|
}
|
|
|