보통 익스플로러에서 도구 -> 추가기능관리 -> (화면왼쪽아래 콤보박스) 모든 추가 기능 -> 더블클릭 -> 제거
하는데 더 확실한 방법
탐색기에서
C:\WINDOWS\Downloaded Program Files
폴더에 들어가면 설치된 엑티브엑스들 볼수있음. 선택->우클릭->제거
2011년 7월 29일 금요일
IIS 설치후 ASP.NET 엑세스 권한 설정
IIS 설치후 asp.net(asmx) 스크립트 엑세스 권한 없는경우 .net framework 디렉토리에서
아래 실행
aspnet_regiis -ga ASPNET
관련링크 : http://msdn.microsoft.com/ko-kr/library/k6h9cz8h(v=vs.80).aspx
아래 실행
aspnet_regiis -ga ASPNET
관련링크 : http://msdn.microsoft.com/ko-kr/library/k6h9cz8h(v=vs.80).aspx
ffmpeg을 이용하여 멀티플랫폼 기반 미디어 스트리밍 라이브러리를 개발하고 있습니다.
2011년 7월 19일 화요일
.NET MySQL Connector/Net - "out of sync with server" Exception 문제
linux mysql-5.0.77 서버 설치된 환경에서 윈도우 C# 으로 만든 클라이언트 접속시
Connector/Net 6.4.3 으로 접속시도하면 아래 Exception 발생
out of sync with server
2011년 7월 8일 금요일
ffmpeg 비디오 디코더(decoder) API 사용법 - how to use ffmpeg video decoder API
#define FFMPEG_0_8 // ffmpeg-1.0 이전 버전
extern "C" {
#include "libavcodec\avcodec.h"
}
#define VIDEO_CODEC_H264 0
#define VIDEO_CODEC_MPEG4 1
static HANDLE hMutex;
#ifdef FFMPEG_0_8
#ifdef FFMPEG_0_8
ReleaseMutex(hMutex);
return 0;
extern "C" {
#include "libavcodec\avcodec.h"
}
#define VIDEO_CODEC_H264 0
#define VIDEO_CODEC_MPEG4 1
// 코덱관련 변수들 선언
class VideoDecoder {
AVCodec *m_pCodec;
AVCodecContext *m_pCodecContext;
AVFrame *m_pPicture;
AVPacket m_avPacket;
...
};
static HANDLE hMutex;
static int g_numCPU = 1;
< 디코더 초기화 - initialize decoder >
void VideoDecoder::initCodec()
{
static int init = 0; // 코덱 초기화는 프로그램 돌때 한번만 해주면됨
if (init == 0) {
SYSTEM_INFO sysinfo;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
g_numCPU = sysinfo.dwNumberOfProcessors; // cpu 코어 갯수
#ifdef FFMPEG_0_8
avcodec_init();
#endif
#endif
avcodec_register_all();
hMutex = CreateMutex(NULL, FALSE, NULL);
hMutex = CreateMutex(NULL, FALSE, NULL);
init = 1;
}
}
}
< 디코더 열기 - open decoder >
int VideoDecoder::openDecoder(int codec)
{
WaitForSingleObject(hMutex, INFINITE); // 뮤텍스 처리 (thread-safe 하지않음)
av_init_packet(&m_avPacket);
if (codec == VIDEO_CODEC_H264)
m_pCodec = avcodec_find_decoder(CODEC_ID_H264); else if (codec == VIDEO_CODEC_MPEG4)
m_pCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
else {
ReleaseMutex(hMutex);
return -1;
}
#ifdef FFMPEG_0_8
m_pCodec = avcodec_find_decoder(CODEC_ID_H264); else if (codec == VIDEO_CODEC_MPEG4)
m_pCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
else {
ReleaseMutex(hMutex);
return -1;
}
#ifdef FFMPEG_0_8
m_pCodecContext = avcodec_alloc_context();
#else
m_pCodecContext = avcodec_alloc_context3(m_pCodec);
#endif
m_pCodecContext = avcodec_alloc_context3(m_pCodec);
#endif
//m_pCodecContext->thread_type = FF_THREAD_FRAME;
m_pCodecContext->thread_type = FF_THREAD_SLICE;
m_pCodecContext->thread_count = g_numCPU;
m_pCodecContext->thread_count = g_numCPU;
m_pPicture = avcodec_alloc_frame();
if (codec == VIDEO_CODEC_H264) // MPEG4('mp4v') 일때 영상깨짐
{
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; }
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; }
#ifdef FFMPEG_0_8
if (avcodec_open(m_pCodecContext, m_pCodec) < 0) {
#else
if (avcodec_open2(m_pCodecContext, m_pCodec, NULL) < 0)
#endif
ReleaseMutex(hMutex);
#else
if (avcodec_open2(m_pCodecContext, m_pCodec, NULL) < 0)
#endif
ReleaseMutex(hMutex);
return -1;
}
ReleaseMutex(hMutex);
return 0;
}
< 디코더 닫기 - close decoder >
int VideoDecoder::closeDecoder()
{
WaitForSingleObject(hMutex, INFINITE);
if (m_pCodecContext)
avcodec_close(m_pCodecContext);
av_free(m_pCodecContext);
av_free(m_pPicture);
ReleaseMutex(hMutex);
return 0;
}
< 디코더 사용 - decode >
int VideoDecoder::Decode(char *inBuf, int inLen)
{
int retLen, got_picture;
char *buf = (char *)malloc(inLen+FF_INPUT_BUFFER_PADDING_SIZE);
memset(&buf[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(buf, inBuf, inLen);
m_avPacket.data = (uint8_t *)buf;
m_avPacket.size = inLen;
char *buf = (char *)malloc(inLen+FF_INPUT_BUFFER_PADDING_SIZE);
memset(&buf[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(buf, inBuf, inLen);
m_avPacket.data = (uint8_t *)buf;
m_avPacket.size = inLen;
retLen = avcodec_decode_video2(m_pCodecContext, m_pPicture, &got_picture, &m_avPacket); // 디코딩 => m_pPicture->data[0], data[1], data[2] 이 각각 YCbCr 값을 가짐 (YUV420)
if (retLen < 0) {
goto exit;
}
if (!got_picture) { // 한 프레임을 만들지 못함 -> 한 프레임을 잘라서 주지 못할경우 여기에 대한 처리가 필요
goto exit;
}
...
exit:
if (buf)
free(buf);
return retLen;
exit:
if (buf)
free(buf);
return retLen;
}
사용법은 클래스 생성후 initCodec()(프로그램 실행처음 한번만 하면됨) -> openDecoder() -> Decode() -> closeDecoder() 해주면 된다.
ffmpeg 디코더 라이브러리 => ffmpeg 디코더 참고소스
ffmpeg 디코더 라이브러리 => ffmpeg 디코더 참고소스
ffmpeg 스케일러(scaler) 사용법 - how to use ffmpeg scaler
< VideoScaler.h >
#ifndef __VIDEO_SCALER_H__
#define __VIDEO_SCALER_H__
extern "C" {
#include "libavcodec\avcodec.h"
#include "libswscale\swscale.h"
}
#include "FFMPEGUtil.h"
class VideoScaler
{
public:
VideoScaler();
virtual ~VideoScaler();
int resetScaler(int srcWidth, int srcHeight, AVPixelFormat pixFmtSrc,
int targetWidth, int targetHeight, AVPixelFormat pixFmtDst);
void freeScaler();
int processScaler(AVFrame *frame);
int srcWidth() { return m_nSrcWidth; }
int srcHeight() { return m_nSrcHeight; }
int targetWidth() { return m_nTargetWidth; }
int targetHeight() { return m_nTargetHeight; }
int bitCount() { return GetBitPerPixel(m_pixFmtDst); }
AVPixelFormat srcPixFormat() { return m_pixFmtSrc; }
AVPixelFormat dstPixFormat() { return m_pixFmtDst; }
unsigned char* picture() { return m_pPicture; }
int size() { return m_nTargetWidth*m_nTargetHeight*(GetBitPerPixel(m_pixFmtDst)>>3); }
protected:
struct SwsContext* m_pScaler;
AVPixelFormat m_pixFmtSrc;
AVPixelFormat m_pixFmtDst;
int m_nSrcWidth, m_nSrcHeight;
int m_nTargetWidth, m_nTargetHeight;
unsigned char *m_pPicture;
};
#endif
#ifndef __VIDEO_SCALER_H__
#define __VIDEO_SCALER_H__
extern "C" {
#include "libavcodec\avcodec.h"
#include "libswscale\swscale.h"
}
#include "FFMPEGUtil.h"
class VideoScaler
{
public:
VideoScaler();
virtual ~VideoScaler();
int resetScaler(int srcWidth, int srcHeight, AVPixelFormat pixFmtSrc,
int targetWidth, int targetHeight, AVPixelFormat pixFmtDst);
void freeScaler();
int processScaler(AVFrame *frame);
int srcWidth() { return m_nSrcWidth; }
int srcHeight() { return m_nSrcHeight; }
int targetWidth() { return m_nTargetWidth; }
int targetHeight() { return m_nTargetHeight; }
int bitCount() { return GetBitPerPixel(m_pixFmtDst); }
AVPixelFormat srcPixFormat() { return m_pixFmtSrc; }
AVPixelFormat dstPixFormat() { return m_pixFmtDst; }
unsigned char* picture() { return m_pPicture; }
int size() { return m_nTargetWidth*m_nTargetHeight*(GetBitPerPixel(m_pixFmtDst)>>3); }
protected:
struct SwsContext* m_pScaler;
AVPixelFormat m_pixFmtSrc;
AVPixelFormat m_pixFmtDst;
int m_nSrcWidth, m_nSrcHeight;
int m_nTargetWidth, m_nTargetHeight;
unsigned char *m_pPicture;
};
#endif
< VideoScaler.cpp >
#include "VideoScaler.h"
VideoScaler::VideoScaler()
{
m_pScaler = NULL;
m_nSrcWidth = m_nSrcHeight = 0;
m_nTargetWidth = m_nTargetHeight = 0;
m_pPicture = NULL;
m_pixFmtSrc = m_pixFmtDst = AV_PIX_FMT_NONE;
}
VideoScaler::~VideoScaler()
{
freeScaler();
}
int VideoScaler::resetScaler(int srcWidth, int srcHeight, AVPixelFormat pixFmtSrc,
int targetWidth, int targetHeight, enum AVPixelFormat pixFmtDst)
{
if (srcWidth <= 0 || srcHeight <= 0) return -1;
if (targetWidth <= 0 || targetHeight <= 0) return -1;
if (pixFmtSrc == AV_PIX_FMT_NONE || pixFmtDst == AV_PIX_FMT_NONE) return -1;
if (m_pPicture) {
delete[] m_pPicture;
m_pPicture = NULL;
}
if (m_pScaler) {
sws_freeContext(m_pScaler);
m_pScaler = NULL;
}
int flags = SWS_BILINEAR;
//int flags = SWS_FAST_BILINEAR;
m_pScaler = sws_getContext(srcWidth, srcHeight, pixFmtSrc,
targetWidth, targetHeight, pixFmtDst, flags, NULL, NULL, NULL);
m_nSrcWidth = srcWidth;
m_nSrcHeight = srcHeight;
m_nTargetWidth = targetWidth;
m_nTargetHeight = targetHeight;
m_pixFmtSrc = pixFmtSrc;
m_pixFmtDst = pixFmtDst;
m_pPicture = new unsigned char[size()];
return 0;
}
void VideoScaler::freeScaler()
{
if (m_pScaler) {
sws_freeContext(m_pScaler);
m_pScaler = NULL;
}
if (m_pPicture) {
delete[] m_pPicture;
m_pPicture = NULL;
}
m_nSrcWidth = m_nSrcHeight = 0;
m_nTargetWidth = m_nTargetHeight = 0;
m_pixFmtSrc = m_pixFmtDst = AV_PIX_FMT_NONE;
}
int VideoScaler::processScaler(AVFrame *frame)
{
unsigned char *ptr = m_pPicture;
unsigned char *src_buf[4];
src_buf[0] = frame->data[0];
src_buf[1] = frame->data[1];
src_buf[2] = frame->data[2];
src_buf[3] = NULL;
int src_stride[4];
src_stride[0] = frame->linesize[0];
src_stride[1] = frame->linesize[1];
src_stride[2] = frame->linesize[2];
src_stride[3] = 0;
unsigned char *dst_buf[4];
dst_buf[0] = ptr;
dst_buf[1] = NULL;
dst_buf[2] = NULL;
dst_buf[3] = NULL;
int pitch = m_nTargetWidth * (GetBitPerPixel(m_pixFmtDst) >> 3);
int dst_stride[4];
dst_stride[0] = pitch;
dst_stride[1] = pitch;
dst_stride[2] = pitch;
dst_stride[3] = pitch;
int ret = -1;
if (m_pScaler) {
ret = sws_scale(m_pScaler, src_buf, src_stride, 0, m_nSrcHeight, dst_buf, dst_stride);
#if 0
FILE *fp = fopen("d:\\test.ppm", "wb");
fprintf(fp, "P6\n%d %d\n255\n", m_nTargetWidth, m_nTargetHeight);
fwrite(ptr, m_nTargetWidth*m_nTargetHeight*(m_nBitCount/8), 1, fp);
fclose(fp);
#endif
}
return ret;
}
< FFMPEGUtil.cpp >
int GetBitPerPixel(AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
return av_get_bits_per_pixel(desc);
}
< 사용 >
...
m_pScaler = new VideoScaler();
...
int scaleFrame(AVFrame *frame, int targetWidth, int targetHeight, AVPixelFormat pix_fmt)
{
if (frame->width != m_pScaler->srcWidth() || frame->height != m_pScaler->srcHeight() ||
targetWidth != m_pScaler->targetWidth() || targetHeight != m_pScaler->targetHeight() ||
frame->format != m_pScaler->srcPixFormat() || pix_fmt != m_pScaler->dstPixFormat())
{
if (m_pScaler->resetScaler(frame->width, frame->height, (AVPixelFormat)frame->format,
targetWidth, targetHeight, pix_fmt) < 0)
{
printf("failed to reset scaler\n");
return -1;
}
}
if (m_pScaler->processScaler(frame) < 0) {
printf("failed to process scale\n");
return -1;
}
...
doSomething(m_pScaler->picture(), m_pScaler->size());
}
VC++에서 ffmpeg 연동시 에러처리
libavutil/common.h:165: error: ‘UINT64_C’ was not declared in this scope
위와 같은 에러를 만나면 common.h 소스에 아래 코드추가
#ifndef INT64_C #define INT64_C(c) (c ## LL) #define UINT64_C(c) (c ## ULL) #endif
DirectDraw를 이용한 YUV -> RGB 고속 변환출력 - YUV -> RGB Converting by using DirectDraw
< DirectDraw YUV Surface 생성 - creating directdraw surfaces >
initDraw(HWND hWnd, int width, int height)
{
HRESULT hr;
g_width = width;
g_height = height;
hr = DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL);
if (FAILED(hr)) {
TRACE("failed to create directdraw device (hr:0x%x)\n", hr);
return -1;
}
DDSURFACEDESC2 ddsd;
/* creating primary surface (RGB32) */
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
/* creating yuv420 surface */
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = width;
ddsd.dwHeight = height;
memcpy(&ddsd.ddpfPixelFormat, &g_ddpfFormats[0], sizeof(DDPIXELFORMAT));
< ddraw resource release >
void release()
{
if (pDD != NULL)
{
pDD->Release();
pDD = NULL;
}
if (lpPrimary != NULL)
{
lpPrimary->Release();
lpPrimary = NULL;
}
if (lpYUVBuffer != NULL)
{
lpYUVBuffer->Release();
lpYUVBuffer = NULL;
}
}
DDPIXELFORMAT g_ddpfFormats[] =
{{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}};
...
IDirectDraw7 *pDD;
IDirectDrawSurface7 *lpPrimary;
IDirectDrawClipper *lpClipper;
IDirectDrawSurface7 *lpYUVBuffer;
IDirectDraw7 *pDD;
IDirectDrawSurface7 *lpPrimary;
IDirectDrawClipper *lpClipper;
IDirectDrawSurface7 *lpYUVBuffer;
int g_width, g_height;
initDraw(HWND hWnd, int width, int height)
{
HRESULT hr;
g_width = width;
g_height = height;
hr = DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL);
if (FAILED(hr)) {
TRACE("failed to create directdraw device (hr:0x%x)\n", hr);
return -1;
}
hr = pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
if(FAILED(hr)) {
TRACE("failed to SetCooperativeLevel (hr:0x%x)\n", hr);
return -1;
}
DDSURFACEDESC2 ddsd;
/* creating primary surface (RGB32) */
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if (hr=pDD->CreateSurface(&ddsd, &lpPrimary, NULL ) != DD_OK)
{
TRACE("failed to create create primary surface (hr:0x%x)\n", hr);
return -1;
}
/* creating clipper */
{
TRACE("failed to create create primary surface (hr:0x%x)\n", hr);
return -1;
}
/* creating clipper */
hr = pDD->CreateClipper(0, &lpClipper, NULL);
if (hr != DD_OK)
{
TRACE("failed to create create clipper (hr:0x%x)\n", hr);
pDD->Release();
return -1;
}
hr = lpClipper->SetHWnd(0, hWnd);
if (hr != DD_OK)
{
TRACE("failed to clippter sethwnd (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}
hr = lpPrimary->SetClipper(lpClipper);
if (hr != DD_OK)
{
TRACE("failed to set clipper (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}
/* creating yuv420 surface */
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = width;
ddsd.dwHeight = height;
memcpy(&ddsd.ddpfPixelFormat, &g_ddpfFormats[0], sizeof(DDPIXELFORMAT));
if ((hr=pDD->CreateSurface(&ddsd, &lpYUVBuffer, NULL)) != DD_OK)
{
TRACE("failed to create yuv buffer surface\n");
return -1;
}
...
}
< Primary Surface 로 변환해서 출력 - yuv surface -> primary surface blt >
int draw(RECT *rectTarget, AVFrame *pPicture)
{
HRESULT hr;
int w = g_width;
int h = g_height;
HRESULT hr;
int w = g_width;
int h = g_height;
...
DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if ((hr=lpYUVBuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) == DD_OK)
{
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE ptr = lpSurf;
int t_stride = pPicture->linesize[0];
int i;
for (i=0; i < h; i++) {
memcpy(ptr+i*ddsd.lPitch, pPicture->data[0]+i*t_stride, w);
}
ptr += ddsd.lPitch*h;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[2]+i*t_stride/2, w/2);
}
ptr += ddsd.lPitch*h/4;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[1]+i*t_stride/2, w/2);
}
lpYUVBuffer->Unlock(NULL);
}
RECT rectSrc;
rectSrc.top = 0;
rectSrc.left = 0;
rectSrc.right = w;
rectSrc.bottom = h;
RECT rectSrc;
rectSrc.top = 0;
rectSrc.left = 0;
rectSrc.right = w;
rectSrc.bottom = h;
hr = lpPrimary->Blt(rectTarget, lpYUVBuffer, &rectSrc, DDBLT_ASYNC, NULL);
}
< ddraw resource release >
void release()
{
if (pDD != NULL)
{
pDD->Release();
pDD = NULL;
}
if (lpPrimary != NULL)
{
lpPrimary->Release();
lpPrimary = NULL;
}
if (lpYUVBuffer != NULL)
{
lpYUVBuffer->Release();
lpYUVBuffer = NULL;
}
}
위 소스에서 AVFrame 은 ffmpeg 에서 사용하는 구조체이고 data[0], data[2], data[1] 는 각각 yuv(yuv420) 값을 가진다.
분리된(non-interleaved) yuv 값을 가진 버퍼를 yuv surface에 memcpy 로 적절하게 복사한 다음 primary surface 로 blt 하면 하드웨어 가속기를 이용한 고속복사를 통해 영상 이미지를 출력할 수 있다.
여기서 primary surface의 픽셀포멧은 RGB32 인데 다른 픽셀포멧의 surface 에다 blt 시켜서 변환시킬수도 있을것같다.
윈도우에서 리얼타임(realtime) 구현
네트웍으로 수신되는 라이브 비디오 영상을 플레이하는 플레이어를 개발하는 경우 정확하게 33ms 마다 한 프레임을 그려줘야하는데 directshow 를 쓰지않고 win32 쓰레드로 구현하는 경우 정확하게 33ms 를 지키기가 어렵다. Sleep() 함수의 경우 틱값이 10ms 이기 때문에 거의 무용지물에 가깝고 네트웍 트래픽 상태가 않좋다면 다 채널 영상의 경우 제멋데로 플레이가 되는것을 볼 수 있다. 윈도우 타이머에 관한 모든 방법을 다 사용해봤는데 윈도우 멀티미디어 타이머를 사용하는 아래 방법이 가장 적절한 방법이었고 HD 다채널(9채널이상) 상황에서도 타이밍이 정확하게 동작하는 것을 볼수 있었다.
static void CALLBACK TimeProc(UINT _timerId, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
// KLog log;
CstreamMedia* rtspClient = (CstreamMedia*)dwUser;
rtspClient->tcount++;
if (rtspClient->frameQueue.count >= MAX_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg/2;
else if (rtspClient->frameQueue.count <= MIN_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg;
if (rtspClient->tcount >= rtspClient->timerCount) {
//log.WriteFileLog("[%s] q count: %d\n", rtspClient->szURL, rtspClient->frameQueue.count);
SetEvent(rtspClient->hTimerEvent);
rtspClient->tcount = 0;
}
}
DWORD WINAPI timerThread( LPVOID lpParam )
{
CstreamMedia* rtspClient = (CstreamMedia*)lpParam;
int sleepTime = 33;
int count = 0;
long last_time = 0, curr_time = 0, diff_time;
KLog log;
if (rtspClient->videoFps > 0)
sleepTime = 1000/rtspClient->videoFps;
err("timerThread begin\n");
TIMECAPS tc;
UINT wTimerRes;
// 타이머의 최소/최대 해상도를 얻어옵니다.
if (timeGetDevCaps (&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
// 에러발생
return -1;
}
if (timeBeginPeriod(1) != TIMERR_NOERROR) {
log.WriteFileLog("TIMERR_NOERROR\n");
}
// 1ms의 타이머 이벤트를 시작합니다.
UINT interval = 1; // 단위: ms
DWORD timerID = timeSetEvent (1, 1, TimeProc, (DWORD)rtspClient, TIME_PERIODIC);
if (timerID == NULL) {
// 에러발생
return -1;
}
//log.WriteFileLog("tcount: %d\n", tcount);
SetEvent(rtspClient->hTimerThreadReady);
while (rtspClient->m_recvThreadFlag)
{
Sleep(1);
}
// 타이머를 해제 합니다.
if (timeKillEvent (timerID) != TIMERR_NOERROR) {
// 에러발생
return -1;
}
timeEndPeriod(1);
rtspClient->frameQueue.reset();
err("timerThread end\n");
return 0;
}
간단히 설명하면
1. timeBeginPeriod(1) 해서 윈도우 틱값을 1ms 로 변경.
2. timeSetEvent 함수로 1ms 마다 주기적으로 TimeProc 콜백함수를 호출하도록 등록.
3. TimeProc 함수에서 tcount 값을 1씩 증가.
4. TimeProc 함수에서 tcount 값이 원하는 시간값(33)이 되면 SetEvent 함수로 실제 일을 하는 (decoding/draw) 쓰레드(WaitForSingleObject로 기다리는)를 깨워줌.
위 코드를 조금만 수정하면 리얼타임에 관련된 코드에 적용가능할듯 합니다.
static void CALLBACK TimeProc(UINT _timerId, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
// KLog log;
CstreamMedia* rtspClient = (CstreamMedia*)dwUser;
rtspClient->tcount++;
if (rtspClient->frameQueue.count >= MAX_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg/2;
else if (rtspClient->frameQueue.count <= MIN_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg;
if (rtspClient->tcount >= rtspClient->timerCount) {
//log.WriteFileLog("[%s] q count: %d\n", rtspClient->szURL, rtspClient->frameQueue.count);
SetEvent(rtspClient->hTimerEvent);
rtspClient->tcount = 0;
}
}
DWORD WINAPI timerThread( LPVOID lpParam )
{
CstreamMedia* rtspClient = (CstreamMedia*)lpParam;
int sleepTime = 33;
int count = 0;
long last_time = 0, curr_time = 0, diff_time;
KLog log;
if (rtspClient->videoFps > 0)
sleepTime = 1000/rtspClient->videoFps;
err("timerThread begin\n");
TIMECAPS tc;
UINT wTimerRes;
// 타이머의 최소/최대 해상도를 얻어옵니다.
if (timeGetDevCaps (&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
// 에러발생
return -1;
}
if (timeBeginPeriod(1) != TIMERR_NOERROR) {
log.WriteFileLog("TIMERR_NOERROR\n");
}
// 1ms의 타이머 이벤트를 시작합니다.
UINT interval = 1; // 단위: ms
DWORD timerID = timeSetEvent (1, 1, TimeProc, (DWORD)rtspClient, TIME_PERIODIC);
if (timerID == NULL) {
// 에러발생
return -1;
}
//log.WriteFileLog("tcount: %d\n", tcount);
SetEvent(rtspClient->hTimerThreadReady);
while (rtspClient->m_recvThreadFlag)
{
Sleep(1);
}
// 타이머를 해제 합니다.
if (timeKillEvent (timerID) != TIMERR_NOERROR) {
// 에러발생
return -1;
}
timeEndPeriod(1);
rtspClient->frameQueue.reset();
err("timerThread end\n");
return 0;
}
간단히 설명하면
1. timeBeginPeriod(1) 해서 윈도우 틱값을 1ms 로 변경.
2. timeSetEvent 함수로 1ms 마다 주기적으로 TimeProc 콜백함수를 호출하도록 등록.
3. TimeProc 함수에서 tcount 값을 1씩 증가.
4. TimeProc 함수에서 tcount 값이 원하는 시간값(33)이 되면 SetEvent 함수로 실제 일을 하는 (decoding/draw) 쓰레드(WaitForSingleObject로 기다리는)를 깨워줌.
위 코드를 조금만 수정하면 리얼타임에 관련된 코드에 적용가능할듯 합니다.
msys/minGW ffmpeg 빌드후 VC++에서 static 링크 에러처리
"error LNK2019: unresolved external symbol __imp____lc_codepage referenced in function _mbrlen libmingwex.a
error LNK2001: unresolved external symbol __imp____lc_codepage libmingwex.a
< 해결방법 (출처 : http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=1&t=891) >
1. Make sure you have got VS2005 or VS2008 in your path.
2. Go to mingw/lib where you have libmingwex.a
3. use command "lib -remove:mbrtowc.o libmingwex.a"
4. use command "lib -remove:wcrtomb.o libmingwex.lib" note the ending .lib
5. link with libmingwex.lib and
error LNK2001: unresolved external symbol __imp____lc_codepage libmingwex.a
< 해결방법 (출처 : http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=1&t=891) >
1. Make sure you have got VS2005 or VS2008 in your path.
2. Go to mingw/lib where you have libmingwex.a
3. use command "lib -remove:mbrtowc.o libmingwex.a"
4. use command "lib -remove:wcrtomb.o libmingwex.lib" note the ending .lib
5. link with libmingwex.lib and
VC++ win32 프로젝트에서 메모리 릭(memory leak) 위치 출력
디버그 모드로 하고 메모리릭 의심되는 위치마다 아래 코드 삽입
#ifdef _DEBUG
//#define _CRTDBG_MAP_ALLOC
//#define _CRTDBG_MAP_ALLOC_NEW
#include <crtdbg.h>
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif
#ifdef _DEBUG
//#define _CRTDBG_MAP_ALLOC
//#define _CRTDBG_MAP_ALLOC_NEW
#include <crtdbg.h>
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif
주로 library 프로젝트에서 메모리 릭 발생시 디버그 창에 그냥 헥사코드만 찍어주는데
위 코드 삽입하면 위치까지 출력해줌
WinSock 헤더와 링크
winsock 헤더위치 => C:\Program Files\Microsoft SDKs\Windows\v7.0\Include\WinSock.h
winsock 링크 => #pragma comment(lib, "wsock32.lib")
winsock 링크 => #pragma comment(lib, "wsock32.lib")
DirectShow Media Type guid 모아놓은 파일위치
C:\Program Files\Microsoft SDKs\Windows\v7.0\Include\uuids.h
ActiveX 에러 로그 보기
도구 -> 인터넷옵션 -> 설정 -> 파일보기
해서 해당 파일 텍스트 에디터로 보면됨.
해서 해당 파일 텍스트 에디터로 보면됨.
javascript 에서 ActiveX 메소드/프로퍼티 엑세스할때 경고 메시지박스 뜨는것 막기
< 액티브액스에 IObjectSafety 인터페이스 추가 >
=> ie에서 스크립트가 activex object 메소드/프로퍼티 액세스할때 메시지박스 뜨는것 막아줌
=> 출처 : http://ancdesign.tistory.com/18
[ActiveXCtrl.h]
#include <objsafe.h>
class ActiveXCtrl
{
...
// IObjectSafety
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)
STDMETHOD_(HRESULT, GetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions
);
STDMETHOD_(HRESULT, SetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions
);
END_INTERFACE_PART(ObjSafe);
...
};
[ActiveXCtrl.cpp]
// Interface map for IObjectSafety
BEGIN_INTERFACE_MAP( CActiveXCtrl, COleControl )
INTERFACE_PART(CActiveXCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////
// IObjectSafety member functions
ULONG FAR EXPORT CActiveXCtrl::XObjSafe::AddRef()
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CActiveXCtrl::XObjSafe::Release()
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CActiveXCtrl::XObjSafe::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
const DWORD dwSupportedBits =
INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
const DWORD dwNotSupportedBits = ~ dwSupportedBits;
HRESULT STDMETHODCALLTYPE
CActiveXCtrl::XObjSafe::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
HRESULT retval = ResultFromScode(S_OK);
// does interface exist?
IUnknown FAR* punkInterface;
retval = pThis->ExternalQueryInterface(&riid, (void * *)&punkInterface);
if (retval != E_NOINTERFACE) { // interface exists
punkInterface->Release(); // release it--just checking!
}
// we support both kinds of safety and have always both set,
// regardless of interface
*pdwSupportedOptions = *pdwEnabledOptions = dwSupportedBits;
return retval; // E_NOINTERFACE if QI failed
}
HRESULT STDMETHODCALLTYPE
CActiveXCtrl::XObjSafe::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
// does interface exist?
IUnknown FAR* punkInterface;
pThis->ExternalQueryInterface(&riid, (void**)&punkInterface);
if (punkInterface) { // interface exists
punkInterface->Release(); // release it--just checking!
}
else { // interface doesn't exist
return ResultFromScode(E_NOINTERFACE);
}
// can't set bits we don't support
if (dwOptionSetMask & dwNotSupportedBits) {
return ResultFromScode(E_FAIL);
}
// can't set bits we do support to zero
dwEnabledOptions &= dwSupportedBits;
// (we already know there are no extra bits in mask )
if ((dwOptionSetMask & dwEnabledOptions) != dwOptionSetMask) {
return ResultFromScode(E_FAIL);
}
// don't need to change anything since we're always safe
return ResultFromScode(S_OK);
}
//---------------------------------------------------------------------------------------
=> ie에서 스크립트가 activex object 메소드/프로퍼티 액세스할때 메시지박스 뜨는것 막아줌
=> 출처 : http://ancdesign.tistory.com/18
[ActiveXCtrl.h]
#include <objsafe.h>
class ActiveXCtrl
{
...
// IObjectSafety
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)
STDMETHOD_(HRESULT, GetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions
);
STDMETHOD_(HRESULT, SetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions
);
END_INTERFACE_PART(ObjSafe);
...
};
[ActiveXCtrl.cpp]
// Interface map for IObjectSafety
BEGIN_INTERFACE_MAP( CActiveXCtrl, COleControl )
INTERFACE_PART(CActiveXCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////
// IObjectSafety member functions
ULONG FAR EXPORT CActiveXCtrl::XObjSafe::AddRef()
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CActiveXCtrl::XObjSafe::Release()
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CActiveXCtrl::XObjSafe::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
const DWORD dwSupportedBits =
INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
const DWORD dwNotSupportedBits = ~ dwSupportedBits;
HRESULT STDMETHODCALLTYPE
CActiveXCtrl::XObjSafe::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
HRESULT retval = ResultFromScode(S_OK);
// does interface exist?
IUnknown FAR* punkInterface;
retval = pThis->ExternalQueryInterface(&riid, (void * *)&punkInterface);
if (retval != E_NOINTERFACE) { // interface exists
punkInterface->Release(); // release it--just checking!
}
// we support both kinds of safety and have always both set,
// regardless of interface
*pdwSupportedOptions = *pdwEnabledOptions = dwSupportedBits;
return retval; // E_NOINTERFACE if QI failed
}
HRESULT STDMETHODCALLTYPE
CActiveXCtrl::XObjSafe::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions)
{
METHOD_PROLOGUE(CActiveXCtrl, ObjSafe)
// does interface exist?
IUnknown FAR* punkInterface;
pThis->ExternalQueryInterface(&riid, (void**)&punkInterface);
if (punkInterface) { // interface exists
punkInterface->Release(); // release it--just checking!
}
else { // interface doesn't exist
return ResultFromScode(E_NOINTERFACE);
}
// can't set bits we don't support
if (dwOptionSetMask & dwNotSupportedBits) {
return ResultFromScode(E_FAIL);
}
// can't set bits we do support to zero
dwEnabledOptions &= dwSupportedBits;
// (we already know there are no extra bits in mask )
if ((dwOptionSetMask & dwEnabledOptions) != dwOptionSetMask) {
return ResultFromScode(E_FAIL);
}
// don't need to change anything since we're always safe
return ResultFromScode(S_OK);
}
//---------------------------------------------------------------------------------------
DirectShow 링크 에러관련 체크
에러 메시지: error LNK2019: unresolved external symbol "public: __thiscall CTransInPlaceFilter::CTransInPlaceFilter(wchar_t const *,struct IUnknown *,struct _GUID const &,long *,bool)" (??0CTransInPlaceFilter@@QAE@PB_WPAUIUnknown@@ABU_GUID@@PAJ_N@Z) referenced in function "public: __thiscall CVideoFrameFilter::CVideoFrameFilter(wchar_t *,struct IUnknown *,long *)" (??0CVideoFrameFilter@@QAE@PA_WPAUIUnknown@@PAJ@Z)
=> 위와같은 링크에러시 BaseClasses와 필터 프로젝트의 속성에서 Character Set(예: Use Multi-Byte Character Set)이 서로 맞는지 확인.
에러 메시지: error LNK2001: unresolved external symbol "class CFactoryTemplate * g_Templates" (?g_Templates@@3PAVCFactoryTemplate@@A)
=> 위와같은 링크에러 발생시 아래 코드 추가 (출처 : http://www.vbforums.com/archive/index.php/t-296274.html)
extern "C"
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllGetClassObject(rclsid, riid, ppv);
}
extern "C"
STDAPI DllCanUnloadNow(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllCanUnloadNow();
}
에러 메시지: error LNK2019: unresolved external symbol __imp__timeGetTime@0 referenced in function "void __stdcall DbgInitialise(struct HINSTANCE__ *)" (?DbgInitialise@@YGXPAUHINSTANCE__@@@Z)
=> 프로젝트 속성 additional dependencies에 winmm.lib 추가
에러 메시지: regsvr32 로 dll 등록시 "g_hInst != 0 ...baseclasses\dllsetup.cpp" 메시지 박스 뜰때
=> 아래 코드추가 (출처 : http://social.msdn.microsoft.com/forums/en-US/windowsdirectshowdevelopment/thread/15323770-9485-4284-8487-017845f592c0/)
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint(reinterpret_cast<HINSTANCE>(hDllHandle), dwReason, lpReserved);
}
=> 위와같은 링크에러시 BaseClasses와 필터 프로젝트의 속성에서 Character Set(예: Use Multi-Byte Character Set)이 서로 맞는지 확인.
에러 메시지: error LNK2001: unresolved external symbol "class CFactoryTemplate * g_Templates" (?g_Templates@@3PAVCFactoryTemplate@@A)
=> 위와같은 링크에러 발생시 아래 코드 추가 (출처 : http://www.vbforums.com/archive/index.php/t-296274.html)
extern "C"
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllGetClassObject(rclsid, riid, ppv);
}
extern "C"
STDAPI DllCanUnloadNow(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllCanUnloadNow();
}
에러 메시지: error LNK2019: unresolved external symbol __imp__timeGetTime@0 referenced in function "void __stdcall DbgInitialise(struct HINSTANCE__ *)" (?DbgInitialise@@YGXPAUHINSTANCE__@@@Z)
=> 프로젝트 속성 additional dependencies에 winmm.lib 추가
에러 메시지: regsvr32 로 dll 등록시 "g_hInst != 0 ...baseclasses\dllsetup.cpp" 메시지 박스 뜰때
=> 아래 코드추가 (출처 : http://social.msdn.microsoft.com/forums/en-US/windowsdirectshowdevelopment/thread/15323770-9485-4284-8487-017845f592c0/)
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint(reinterpret_cast<HINSTANCE>(hDllHandle), dwReason, lpReserved);
}
ffdshow setting 값 변경 등
ffmpeg-mt 에서 비디오 디코더 h264 로 설정하는법
=> 윈도 레지스트리 HKCU\Software\GNU\ffdshow에 값:h264 데이터:21 써주면 됨.
ffdshow 레지스트리 디폴트값 수정위치
=> src/settings/TglobalSettings.cpp
void TglobalSettingsDecVideo::reg_op_codec(TregOp &t,TregOp *t2)
void TglobalSettingsDecAudio::reg_op_codec(TregOp &t,TregOp *t2)
에서 _reg_op_codec() 함수 마지막 인자값이 디폴트
=> 윈도 레지스트리 HKCU\Software\GNU\ffdshow에 값:h264 데이터:21 써주면 됨.
ffdshow 레지스트리 디폴트값 수정위치
=> src/settings/TglobalSettings.cpp
void TglobalSettingsDecVideo::reg_op_codec(TregOp &t,TregOp *t2)
void TglobalSettingsDecAudio::reg_op_codec(TregOp &t,TregOp *t2)
에서 _reg_op_codec() 함수 마지막 인자값이 디폴트
프로그램에서 ffdshow 클래스 생성 실패 때문에 수정한 곳
=> TffdshowDec::NonDelegatingQueryInterface(REFIID riid,void **ppv)
에서 whitelist, blacklist 체크하는 부분 막아줌
ffmpeg을 이용하여 멀티플랫폼 기반 미디어 스트리밍 라이브러리를 개발하고 있습니다.
ActiveX 등 Component 등록여부 확인
윈도우 레지스트리 에디터에서 해당 component guid 등록되었나 확인
HKLM\SOFTWARE\Classes\CLSID\XXXX-XXX...
HKLM\SOFTWARE\Classes\CLSID\XXXX-XXX...
ffmpeg을 이용하여 멀티플랫폼 기반 미디어 스트리밍 라이브러리를 개발하고 있습니다.
VC++ 에서 문자열 관련 함수들 찾기
unicode<->multibyte 문자열 관련 함수 vc\include\tchar.h
.NET Framework 버전 확인
윈도우 레지스트리 에디트에서
HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP
확인할것
HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP
확인할것
ffmpeg을 이용하여 멀티플랫폼 기반 미디어 스트리밍 라이브러리를 개발하고 있습니다.
__RPC__out_xcount_part 에러처리
syntax error : identifier '__RPC__out_xcount_part' => directx include 버전문제
피드 구독하기:
글 (Atom)