typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
typedef unsigned char u_int8_t;
typedef unsigned short u_int16_t;
typedef unsigned int u_int32_t;
typedef unsigned __int64 u_int64_t;
typedef struct
{
WORD cc: 4; /* csrc count */
WORD ext: 1; /* header extension flag */
WORD pad: 1; /* padding flag - for encryption */
WORD ver: 2; /* protocal version */
WORD pt: 7; /* payload type */
WORD mk: 1; /* marker bit - for profile */
WORD seq; /* sequence number of this packet */
DWORD ts; /* timestamp of this packet */
DWORD ssrc; /* source of packet */
} RTP_HEADER;
#define RTP_SEND_SIZE (1440)
int trimStartCode(uint8_t *buf, int len)
{
uint8_t *ptr = buf;
if (len < 4) return 0;
// trying to find 0x00 0x00 ... 0x01 pattern
// find first 0x00 0x00 bytes
if (ptr[0] == 0x00 && ptr[1] == 0x00) {
// find first 0x01
while (*ptr == 0x00 && ptr < buf+len-1)
ptr++;
if (*ptr != 0x01) { // error - invalid stream
DPRINTF("invalid stream, 0x%02x\n", *ptr);
ptr = buf;
} else {
ptr++;
}
}
return ptr-buf;
}
void RTPSender::resetRtpHeader(uint8_t payload, uint8_t marker, uint32_t timestamp)
{
RTP_HEADER *pRtpHeader = (RTP_HEADER *)m_pRtpSendBuffer;
memset(pRtpHeader, 0, sizeof(RTP_HEADER));
pRtpHeader->ver = 2;
pRtpHeader->pt = payload;
pRtpHeader->mk = marker;
pRtpHeader->ts = htonl(timestamp);
uint16_t seqNum = 0;
seqNum = m_nSequenceNum++;
if (m_nSequenceNum >= 0xffff)
m_nSequenceNum = 0;
pRtpHeader->seq = htons(seqNum);
}
uint32_t RTPSender::convertToRTPTimestamp(unsigned int frequency, __int64 timestamp)
{
__int64 rtp_timestamp = (__int64)frequency * timestamp / (__int64)1000;
return rtp_timestamp;
}
int H264Sender::sendRtpData(MediaBuffer *pBuffer)
{
RTP_HEADER *pRtpHeader = (RTP_HEADER *)m_pRtpSendBuffer;
int offset = trimStartCode(pBuffer->data, pBuffer->size);
int bytesToSend = pBuffer->size-offset;
uint8_t *pSrc = &pBuffer->data[offset];
uint8_t *pDest = (uint8_t *)m_pRtpSendBuffer;
int rtphdr_size = sizeof(RTP_HEADER);
int pktcount = 0;
const char *track = "track1";
uint32_t timestamp = convertToRTPTimestamp(m_nTimestampFrequency, pBuffer->dts);
int sendToLen;
while (bytesToSend > RTP_SEND_SIZE)
{
resetRtpHeader(m_nPayloadType, 0, timestamp);
pDest = (uint8_t *)&m_pRtpSendBuffer[rtphdr_size];
pDest[0] = (pSrc[0] & 0xE0) | 28;
if (pktcount == 0) {
memcpy(&pDest[1], pSrc, RTP_SEND_SIZE);
pDest[1] = 0x80 | (pSrc[0] & 0x1F);
sendToLen = RTP_SEND_SIZE + rtphdr_size + 1;
} else {
pDest[1] = pSrc[0] & 0x1F;
memcpy(&pDest[2], pSrc, RTP_SEND_SIZE);
sendToLen = RTP_SEND_SIZE + rtphdr_size + 2;
}
pSrc += RTP_SEND_SIZE;
m_pServerSession->sendClientRtp(track, m_pRtpSendBuffer, sendToLen);
bytesToSend -= RTP_SEND_SIZE;
pktcount++;
}
if (bytesToSend > 0) {
resetRtpHeader(m_nPayloadType, 1, timestamp);
pDest = (uint8_t *)&m_pRtpSendBuffer[rtphdr_size];
if (pktcount == 0) {
memcpy(pDest, pSrc, bytesToSend);
sendToLen = bytesToSend + rtphdr_size;
} else { // FU-A last packet
pDest[0] = 0x20 | 28;
pDest[1] = 0x40;
memcpy(&pDest[2], pSrc, bytesToSend);
sendToLen = bytesToSend + rtphdr_size + 2;
}
m_pServerSession->sendClientRtp(track, m_pRtpSendBuffer, sendToLen);
}
return 0;
}
2014년 12월 4일 목요일
H.264 RFC3984 NAL 패킷전송
2014년 11월 21일 금요일
waveIn/waveOut 함수 안전하게 사용하는 법
윈도우의 waveIn/waveOut 계열 함수는 콜백을 등록해서 호출받는 방식으로 동작하는데 콜백함수는 wave 드라이버의 쓰레드에서 호출하기 때문에 콜백함수에서 윈도우 시스템 API를 호출하면 죽거나 데드락이 걸리는 문제가 발생한다. 이것을 해결하기 위해 큐를 사용해 외부 쓰레드에서 waveIn/waveOut 결과를 처리한다.
< WaveBuffer.h >
< WaveBuffer.cpp >
< WaveIn.h >
<WaveIn.cpp >
< WaveOut.h >
< WaveOut.cpp >
< WaveOut 사용법 >
WaveOut* m_pWaveOut = new WaveOut();
...
// waveout 열기
int open(int channel, int sample_rate, int channel_layout)
{
// open waveout
WAVEFORMATEX waveformat;
memset(&waveformat, 0, sizeof(WAVEFORMATEX));
waveformat.wFormatTag = WAVE_FORMAT_PCM;
waveformat.nChannels = channel;
waveformat.nSamplesPerSec = sample_rate;
waveformat.wBitsPerSample = 16;
waveformat.nBlockAlign = waveformat.nChannels*waveformat.wBitsPerSample/8;
waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec*waveformat.nBlockAlign;
waveformat.cbSize = 0;
int ret = m_pWaveOut->open(waveformat, channel_layout);
if (ret < 0) m_pWaveOut->close();
return ret;
}
// waveout 출력 - buff : 오디오 pcm 데이터, size : 오디오 pcm 데이터 사이즈
int waveout(unsigned char *buff, int size)
{
return m_pWaveOut->waveOutAdd(buff, size);
}
// waveout 닫기
void close()
{
m_pWaveOut->close();
}
< WaveBuffer.h >
#ifndef __WAVE_BUFFER_H__
#define __WAVE_BUFFER_H__
#include <windows.h>
#include <MMSystem.h>
#include "BufferQueue.h"
class WaveBuffer {
public:
WAVEHDR* pWaveHdr;
int length;
bool bDeleteData;
WaveBuffer(WAVEHDR *hdr, int len, bool deleteData = true) {
pWaveHdr = hdr;
length = len;
bDeleteData = deleteData;
}
virtual ~WaveBuffer() {
if (pWaveHdr && bDeleteData) {
delete[] pWaveHdr->lpData;
delete pWaveHdr;
}
}
};
class WaveBufferQueue {
public:
WaveBufferQueue(bool deleteData = true);
virtual ~WaveBufferQueue();
int push_back(WAVEHDR *pWaveHdr);
WaveBuffer* pop_front();
void clear() { m_pWaveQue->clear(); }
int count() { return m_pWaveQue->count(); }
private:
BufferQueue<WaveBuffer> *m_pWaveQue;
int m_nCount;
bool m_bDeleteData;
};
#endif
BufferQueue 템플릿 클래스는 C++ double linked-list Template Queue 포스트 참조< WaveBuffer.cpp >
#include "WaveBuffer.h"
WaveBufferQueue::WaveBufferQueue(bool deleteData)
{
m_pWaveQue = new BufferQueue<WaveBuffer>(0); // 무제한 버퍼
m_bDeleteData = deleteData;
}
WaveBufferQueue::~WaveBufferQueue()
{
if (m_pWaveQue) {
delete m_pWaveQue;
m_pWaveQue = NULL;
}
}
int WaveBufferQueue::push_back(WAVEHDR *pWaveHdr)
{
WaveBuffer *wave = new WaveBuffer(pWaveHdr, pWaveHdr->dwBufferLength, m_bDeleteData);
return m_pWaveQue->push_back(wave);
}
WaveBuffer* WaveBufferQueue::pop_front()
{
return m_pWaveQue->pop_front();
}
< WaveIn.h >
#ifndef __WAVE_IN_H__
#define __WAVE_IN_H__
#include <windows.h>
#include <MMSystem.h>
#include "WaveBuffer.h"
class WaveIn
{
public:
WaveIn();
virtual ~WaveIn();
/* WaveIn API */
int open(WAVEFORMATEX waveInFormat, int buff_count, double duration);
void close();
void setWaveInFunc(void (*func)(WAVEHDR *pHdr, void *pData), void *pData)
{
waveInHandlerFunc = func;
m_pWaveInHandlerData = pData;
}
public:
void processWaveInDone(WAVEHDR *pHdr);
void processWaveInClose();
void running();
protected:
HWAVEIN m_hWaveIn;
HANDLE m_hWaveInThread;
BOOL m_bWaveInRun;
WAVEHDR* m_pWaveInHdr;
int m_nWaveInHdrCount;
int m_nWaveInHdrPrepareCount;
WaveBufferQueue* m_pWaveBufferQue;
WAVEFORMATEX m_waveInFormatEx;
void (*waveInHandlerFunc)(WAVEHDR *pHdr, void *pData);
void *m_pWaveInHandlerData;
void waveInPrintError(MMRESULT result, LPCTSTR str);
};
#endif
<WaveIn.cpp >
#include "WaveIn.h"
#include "GlobalEnv.h"
#include <assert.h>
#include <process.h>
#include <stdio.h>
#pragma comment(lib, "winmm.lib")
WaveIn::WaveIn()
{
m_hWaveIn = NULL;
m_hWaveInThread = NULL;
m_bWaveInRun = FALSE;
memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));
m_pWaveInHdr = NULL;
m_nWaveInHdrCount = 0;
m_pWaveBufferQue = NULL;
waveInHandlerFunc = NULL;
m_pWaveInHandlerData = NULL;
}
WaveIn::~WaveIn()
{
close();
}
unsigned __stdcall Thread_WaveIn(LPVOID lpParam)
{
WaveIn *pWaveIn = (WaveIn *)lpParam;
pWaveIn->running();
return 0;
}
void WaveIn::running()
{
MMRESULT mRes;
WAVEHDR *pHdr;
WaveBuffer *pWave;
while (1)
{
pWave = m_pWaveBufferQue->pop_front();
if (pWave == NULL)
{
Sleep(1);
continue;
}
pHdr = pWave->pWaveHdr;
mRes = waveInUnprepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInUnprepareHeader");
assert(FALSE);
continue;
}
m_nWaveInHdrPrepareCount--;
if (waveInHandlerFunc)
waveInHandlerFunc(pHdr, m_pWaveInHandlerData);
delete pWave;
if (m_bWaveInRun)
{
mRes = waveInPrepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInPrepareHeader");
assert(FALSE);
continue;
}
mRes = waveInAddBuffer(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInAddBuffer");
assert(FALSE);
continue;
}
m_nWaveInHdrPrepareCount++;
}
else
{
//DXPRINTF("[%s] WaveIn Hdr Count:%d\n", __FUNCTION__, m_nWaveInHdrPrepareCount);
if (m_pWaveBufferQue->count() == 0 && m_nWaveInHdrPrepareCount == 0) {
break;
}
}
}
}
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
WaveIn *pWaveIn = (WaveIn *)dwInstance;
switch(uMsg)
{
case WIM_OPEN:
DXPRINTF("WIM_OPEN\n");
break;
case WIM_CLOSE:
DXPRINTF("WIM_CLOSE\n");
pWaveIn->processWaveInClose();
break;
case WIM_DATA:
pWaveIn->processWaveInDone((WAVEHDR *)dwParam1);
break;
default:
break;
}
}
void WaveIn::processWaveInDone(WAVEHDR *pHdr)
{
if (m_pWaveBufferQue)
m_pWaveBufferQue->push_back(pHdr);
}
void WaveIn::processWaveInClose()
{
}
int WaveIn::open(WAVEFORMATEX waveInFormat, int buff_count, double duration)
{
MMRESULT mRes;
if (m_bWaveInRun)
return -1;
memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));
m_waveInFormatEx.wFormatTag = WAVE_FORMAT_PCM;
m_waveInFormatEx.nChannels = waveInFormat.nChannels;
m_waveInFormatEx.nSamplesPerSec = waveInFormat.nSamplesPerSec;
m_waveInFormatEx.wBitsPerSample = waveInFormat.wBitsPerSample;
m_waveInFormatEx.nBlockAlign = m_waveInFormatEx.nChannels*m_waveInFormatEx.wBitsPerSample/8;
m_waveInFormatEx.nAvgBytesPerSec = m_waveInFormatEx.nSamplesPerSec*m_waveInFormatEx.nBlockAlign;
m_waveInFormatEx.cbSize = 0;
mRes = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &m_waveInFormatEx,
(DWORD_PTR)waveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInOpen");
return -2;
}
/* prepare wavein header */
int buff_size = (int)(m_waveInFormatEx.nAvgBytesPerSec*duration);
buff_size *= m_waveInFormatEx.nChannels;
m_nWaveInHdrCount = buff_count;
m_pWaveInHdr = new WAVEHDR[m_nWaveInHdrCount];
for (int i=0; i<m_nWaveInHdrCount; i++)
{
memset(&m_pWaveInHdr[i], 0, sizeof(WAVEHDR));
m_pWaveInHdr[i].lpData = new char[buff_size];
m_pWaveInHdr[i].dwBufferLength = buff_size;
m_pWaveInHdr[i].dwUser = i;
mRes = waveInPrepareHeader(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInPrepareHeader");
assert(FALSE);
continue;
}
mRes = waveInAddBuffer(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes ,"waveInAddBuffer");
assert(FALSE);
continue;
}
}
m_nWaveInHdrPrepareCount = m_nWaveInHdrCount;
m_pWaveBufferQue = new WaveBufferQueue(false);
m_bWaveInRun = TRUE;
m_hWaveInThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveIn, this, 0, NULL);
mRes = waveInStart(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInStart");
return -1;
}
return 0;
}
void WaveIn::close()
{
MMRESULT mRes;
if (!m_bWaveInRun || !m_hWaveIn)
return;
m_bWaveInRun = FALSE;
WaitForSingleObject(m_hWaveInThread, INFINITE);
mRes = waveInStop(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR)
waveInPrintError(mRes, "waveInStop");
for (int i=0; i<m_nWaveInHdrCount; i++)
delete[] m_pWaveInHdr[i].lpData;
delete m_pWaveInHdr;
mRes = waveInClose(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR)
waveInPrintError(mRes ,"waveInClose");
if (m_pWaveBufferQue) {
delete m_pWaveBufferQue;
m_pWaveBufferQue = NULL;
}
CloseHandle(m_hWaveInThread);
}
void WaveIn::waveInPrintError(MMRESULT result, LPCTSTR str)
{
char errmsg[128] = {0};
waveInGetErrorText(result, errmsg, sizeof(errmsg));
DXPRINTF("%s waveInError: %d %s\n", str, result, errmsg);
}
< WaveOut.h >
#ifndef __WAVE_OUT_H__
#define __WAVE_OUT_H__
#include <windows.h>
#include <MMSystem.h>
#include <MMReg.h>
#include <stdio.h>
#include "WaveBuffer.h"
class WaveOut
{
public:
WaveOut();
virtual ~WaveOut();
/* WaveOut API */
int open(WAVEFORMATEX waveOutFormat, DWORD channel_layout);
void close();
int waveOutAdd(unsigned char *buff, int len);
void setAudioVolume(int val);
DWORD getAudioVolume();
void reset();
void setMaxWaveOutBufferCount(int maxCount) { m_nMaxWaveOutBufferCount = maxCount; }
int channels() { return m_waveOutFormatEx.Format.nChannels; }
int sampleRate() { return m_waveOutFormatEx.Format.nSamplesPerSec; }
int channelLayout() { return m_waveOutFormatEx.dwChannelMask; }
public:
void processWaveOutDone(WAVEHDR *pHdr);
void processWaveOutClose();
void running();
protected:
HWAVEOUT m_hWaveOut;
HANDLE m_hWaveOutThread;
BOOL m_bWaveOutRun;
int m_nMaxWaveOutBufferCount;
int m_nWaveOutHdrPrepareCount;
WaveBufferQueue* m_pWaveBufferQue;
DWORD m_nAudioVolume;
WAVEFORMATEXTENSIBLE m_waveOutFormatEx;
WAVEOUTCAPS m_waveOutCaps;
void waveOutPrintError(MMRESULT result, LPCTSTR str);
FILE *m_pFile; // audio dump
};
#endif
< WaveOut.cpp >
#include "WaveOut.h"
#include "GlobalEnv.h"
#include <assert.h>
#include <process.h>
#include <stdio.h>
#pragma comment(lib, "winmm.lib")
WaveOut::WaveOut()
{
m_hWaveOut = NULL;
m_hWaveOutThread = NULL;
m_bWaveOutRun = FALSE;
m_nWaveOutHdrPrepareCount = 0;
m_pWaveBufferQue = new WaveBufferQueue();
m_nAudioVolume = 0xFFFFFFFF;
ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));
ZeroMemory(&m_waveOutFormatEx, sizeof(WAVEFORMATEXTENSIBLE));
m_nMaxWaveOutBufferCount = 3;
m_pFile = NULL;
}
WaveOut::~WaveOut()
{
close();
DX_DELETE_OBJECT(m_pWaveBufferQue);
}
unsigned __stdcall Thread_WaveOut(LPVOID lpParam)
{
WaveOut *pWaveOut = (WaveOut *)lpParam;
pWaveOut->running();
return 0;
}
void WaveOut::running()
{
MMRESULT mRes;
WaveBuffer *pWave;
WAVEHDR *pHdr;
while (1)
{
pWave = m_pWaveBufferQue->pop_front();
if (pWave == NULL) {
Sleep(1);
goto skip;
}
pHdr = pWave->pWaveHdr;
mRes = waveOutUnprepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutUnprepareHeader");
assert(FALSE);
continue;
}
delete pWave;
InterlockedDecrement((LPLONG)&m_nWaveOutHdrPrepareCount);
skip:
if (!m_bWaveOutRun)
{
if (m_pWaveBufferQue->count() == 0 && m_nWaveOutHdrPrepareCount == 0) {
break;
}
}
}
}
void WaveOut::processWaveOutDone(WAVEHDR *pHdr)
{
if (m_pWaveBufferQue) {
m_pWaveBufferQue->push_back(pHdr);
}
}
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
WaveOut *pWaveOut = (WaveOut *)dwInstance;
switch(uMsg)
{
case WOM_OPEN:
DXPRINTF("WOM_OPEN\n");
break;
case WOM_CLOSE:
DXPRINTF("WOM_CLOSE\n");
break;
case WOM_DONE:
pWaveOut->processWaveOutDone((WAVEHDR *)dwParam1);
break;
default:
break;
}
}
int WaveOut::waveOutAdd(unsigned char *buff, int len)
{
MMRESULT mRes;
if (!m_bWaveOutRun)
return -1;
//DXPRINTF("wave out buffer count : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);
if (m_nMaxWaveOutBufferCount > 0) {
if (m_nWaveOutHdrPrepareCount > m_nMaxWaveOutBufferCount) {
DXPRINTF("wave out buffer overflow : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);
return 0;
}
}
WAVEHDR *pHdr = new WAVEHDR;
ZeroMemory(pHdr, sizeof(WAVEHDR));
pHdr->lpData = new char[len];
memcpy(pHdr->lpData, buff, len);
pHdr->dwBufferLength = len;
pHdr->dwFlags = 0;
mRes = waveOutPrepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutPrepareHeader");
delete[] pHdr->lpData;
delete pHdr;
assert(FALSE);
return -2;
}
mRes = waveOutWrite(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutWrite");
delete[] pHdr->lpData;
delete pHdr;
assert(FALSE);
return -3;
}
InterlockedIncrement((LPLONG)&m_nWaveOutHdrPrepareCount);
if (m_pFile) fwrite(buff, len, 1, m_pFile);
return 0;
}
int WaveOut::open(WAVEFORMATEX waveOutFormat, DWORD channel_layout)
{
MMRESULT mRes;
if (m_bWaveOutRun)
return -1;
m_waveOutFormatEx;
memset(&m_waveOutFormatEx, 0, sizeof(WAVEFORMATEXTENSIBLE));
m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_PCM;
m_waveOutFormatEx.Format.nChannels = waveOutFormat.nChannels;
m_waveOutFormatEx.Format.nSamplesPerSec = waveOutFormat.nSamplesPerSec;
m_waveOutFormatEx.Format.wBitsPerSample = waveOutFormat.wBitsPerSample;
m_waveOutFormatEx.Format.nBlockAlign = waveOutFormat.nChannels*waveOutFormat.wBitsPerSample/8;
m_waveOutFormatEx.Format.nAvgBytesPerSec = waveOutFormat.nSamplesPerSec*waveOutFormat.nBlockAlign;
if (waveOutFormat.nChannels <= 2) {
m_waveOutFormatEx.Format.cbSize = 0;
} else {
m_waveOutFormatEx.dwChannelMask = channel_layout;
m_waveOutFormatEx.Samples.wValidBitsPerSample = waveOutFormat.wBitsPerSample;
m_waveOutFormatEx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
m_waveOutFormatEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
}
mRes = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, (WAVEFORMATEX *)&m_waveOutFormatEx,
(DWORD_PTR)waveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutOpen");
return -2;
}
mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutWrite");
}
m_bWaveOutRun = TRUE;
m_hWaveOutThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveOut, this, 0, NULL);
if (!m_hWaveOutThread) {
DXPRINTF("[%s] waveout thread create error, err:%d\n", __FUNCTION__, GetLastError());
return -3;
}
ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));
mRes = waveOutGetDevCaps((UINT_PTR)m_hWaveOut, &m_waveOutCaps, sizeof(WAVEOUTCAPS));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutGetDevCaps");
}
#if 0
m_pFile = fopen("audio.wav", "wb");
#endif
return 0;
}
void WaveOut::close()
{
MMRESULT mRes;
if (!m_bWaveOutRun || !m_hWaveOut)
return;
waveOutReset(m_hWaveOut);
m_bWaveOutRun = FALSE;
WaitForSingleObject(m_hWaveOutThread, INFINITE);
mRes = waveOutClose(m_hWaveOut);
if (mRes != MMSYSERR_NOERROR)
waveOutPrintError(mRes, "waveOutClose");
CloseHandle(m_hWaveOutThread);
m_hWaveOutThread = NULL;
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
}
void WaveOut::setAudioVolume(int val)
{
m_nAudioVolume = val;
if (m_hWaveOut) {
MMRESULT mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutSetVolume");
}
}
}
DWORD WaveOut::getAudioVolume()
{
if (m_hWaveOut) {
MMRESULT mRes = waveOutGetVolume(m_hWaveOut, &m_nAudioVolume);
if (mRes == MMSYSERR_NOERROR) return m_nAudioVolume;
waveOutPrintError(mRes, "waveOutGetVolume");
}
return 0;
}
void WaveOut::reset()
{
if (m_hWaveOut)
waveOutReset(m_hWaveOut);
}
void WaveOut::waveOutPrintError(MMRESULT result, LPCTSTR str)
{
char errmsg[128] = {0};
waveOutGetErrorText(result, errmsg, sizeof(errmsg));
DXPRINTF("%s waveOutError: %d %s\n", str, result, errmsg);
}
< WaveOut 사용법 >
WaveOut* m_pWaveOut = new WaveOut();
...
// waveout 열기
int open(int channel, int sample_rate, int channel_layout)
{
// open waveout
WAVEFORMATEX waveformat;
memset(&waveformat, 0, sizeof(WAVEFORMATEX));
waveformat.wFormatTag = WAVE_FORMAT_PCM;
waveformat.nChannels = channel;
waveformat.nSamplesPerSec = sample_rate;
waveformat.wBitsPerSample = 16;
waveformat.nBlockAlign = waveformat.nChannels*waveformat.wBitsPerSample/8;
waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec*waveformat.nBlockAlign;
waveformat.cbSize = 0;
int ret = m_pWaveOut->open(waveformat, channel_layout);
if (ret < 0) m_pWaveOut->close();
return ret;
}
// waveout 출력 - buff : 오디오 pcm 데이터, size : 오디오 pcm 데이터 사이즈
int waveout(unsigned char *buff, int size)
{
return m_pWaveOut->waveOutAdd(buff, size);
}
// waveout 닫기
void close()
{
m_pWaveOut->close();
}
2014년 10월 23일 목요일
live555 visual c++ project (VS2008)
http://www.mediafire.com/download/gnh5p065adar47a/live555.zip
live.2014.10.21.tar.gz 버전
testProgs 나머지 실행프로그램들은 만들어진 프로젝트 참고하면 쉽게 만들수 있음
x64 버전도 win32 버전 그데로 사용가능
테스트는 첨부된 test.264 사용
x64 버전도 win32 버전 그데로 사용가능
테스트는 첨부된 test.264 사용
2014년 2월 12일 수요일
ffmpeg av_lockmgr_register 사용법
ffmpeg의 thread-safe 하지않은 함수들 - av_register_all, avcodec_open2, avcodec_close
등의 함수들은 보통 호출부에 뮤텍스 락을 걸어주어야 하는데 av_lockmgr_register 함수를
이용하면 이것을 간단하게 해결할 수 있다.
#include <windows.h>
extern "C" {
#include "libavformat/avformat.h"
}
static bool isInit = false;
static HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
static int lockmgr(void **mtx, enum AVLockOp op)
{
switch(op) {
case AV_LOCK_CREATE:
*mtx = CreateMutex(0, FALSE, 0);
if(!*mtx)
return 1;
return 0;
case AV_LOCK_OBTAIN:
return !!WaitForSingleObject(*mtx, INFINITE);
case AV_LOCK_RELEASE:
return !!ReleaseMutex(*mtx);
case AV_LOCK_DESTROY:
CloseHandle(*mtx);
return 0;
}
return 1;
}
void InitFFmpegLib()
{
WaitForSingleObject(hMutex, INFINITE);
if (!isInit) {
av_register_all();
if (av_lockmgr_register(lockmgr)) {
printf("Could not initialize lock manager!\n");
exit(1);
}
isInit = true;
}
ReleaseMutex(hMutex);
}
등의 함수들은 보통 호출부에 뮤텍스 락을 걸어주어야 하는데 av_lockmgr_register 함수를
이용하면 이것을 간단하게 해결할 수 있다.
#include <windows.h>
extern "C" {
#include "libavformat/avformat.h"
}
static bool isInit = false;
static HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
static int lockmgr(void **mtx, enum AVLockOp op)
{
switch(op) {
case AV_LOCK_CREATE:
*mtx = CreateMutex(0, FALSE, 0);
if(!*mtx)
return 1;
return 0;
case AV_LOCK_OBTAIN:
return !!WaitForSingleObject(*mtx, INFINITE);
case AV_LOCK_RELEASE:
return !!ReleaseMutex(*mtx);
case AV_LOCK_DESTROY:
CloseHandle(*mtx);
return 0;
}
return 1;
}
void InitFFmpegLib()
{
WaitForSingleObject(hMutex, INFINITE);
if (!isInit) {
av_register_all();
if (av_lockmgr_register(lockmgr)) {
printf("Could not initialize lock manager!\n");
exit(1);
}
isInit = true;
}
ReleaseMutex(hMutex);
}
프로그램 시작부분에서 InitFFmpegLib() 함수를 호출하면 그 다음부터 thread-unsafe 한
ffmpeg api 함수들은 자동으로 뮤텍스 락을 건다.
2014년 1월 8일 수요일
copy AVFrame using av_image_copy
AVFrame* clone_av_frame(AVFrame *frame)
{
AVFrame *new_frame = avcodec_alloc_frame();
enum AVPixelFormat fmt = (enum AVPixelFormat)frame->format;
int ret = av_image_alloc(new_frame->data, new_frame->linesize, frame->width, frame-height, fmt, 1);
if (ret < 0) {
av_free(new_frame->data[0]);
av_free(new_frame);
return 0;
}
av_image_copy(new_frame->data, new_frame->linesize, (const uint8_t **)frame->data,
frame->linesize, fmt, frame->width, frame->height);
// copy to new frame
new_frame->width = frame->width;
new_frame->height = frame->height;
new_frame->pict_type = frame->pict_type;
new_frame->format = (int)fmt;
...
return new_frame;
}
void free_av_frame(AVFrame *frame)
{
if (frame) {
av_free(frame->data[0]);
av_free(frame);
}
}
void free_av_frame(AVFrame *frame)
{
if (frame) {
av_free(frame->data[0]);
av_free(frame);
}
}
피드 구독하기:
글 (Atom)