2015년 10월 7일 수요일
2015년 8월 31일 월요일
윈도우/리눅스 공통 뮤텍스/쓰레드/이벤트/세마포어 라이브러리
< Mutex.h >
< Mutex.cpp >
< Mutex 사용 >
< Thread.h >
< Thread.cpp >
< Thread 사용 >
< Event.h>
< Event.cpp>
< Event 사용 >
< MySemaphore.h >
< MySemaphore.cpp >
< MySemaphore 사용 >
#ifndef __MUTEX_H__
#define __MUTEX_H__
#ifdef WIN32
#include <windows.h>
#include <process.h>
#define MUTEX HANDLE
#define PTHREAD_MUTEX_INITIALIZER CreateMutex(NULL, FALSE, NULL)
#else
#include <pthread.h>
#define MUTEX pthread_mutex_t
#endif
int MUTEX_INIT(MUTEX *mutex);
int MUTEX_LOCK(MUTEX *mutex);
int MUTEX_UNLOCK(MUTEX *mutex);
int MUTEX_DESTROY(MUTEX *mutex);
#endif
< Mutex.cpp >
#include "Mutex.h"
int MUTEX_INIT(MUTEX *mutex)
{
#ifdef WIN32
*mutex = CreateMutex(NULL, FALSE, NULL);
return *mutex == NULL ? -1 : 0;
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
return pthread_mutex_init(mutex, &attr);
#endif
}
int MUTEX_DESTROY(MUTEX *mutex)
{
#ifdef WIN32
if (*mutex) {
CloseHandle(*mutex);
*mutex = NULL;
}
return 0;
#else
return pthread_mutex_destroy(mutex);
#endif
}
int MUTEX_LOCK(MUTEX *mutex)
{
#ifdef WIN32
return WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED ? -1 : 0;
#else
return pthread_mutex_lock(mutex);
#endif
}
int MUTEX_UNLOCK(MUTEX *mutex)
{
#ifdef WIN32
return ReleaseMutex(*mutex) == 0 ? -1 : 1;
#else
return pthread_mutex_unlock(mutex);
#endif
}
< Mutex 사용 >
MUTEX mutex;
...
MUTEX_INIT(&mutex);
...
MUTEX_LOCK(&mutex);
...
MUTEX_UNLOCK(&mutex);
...
MUTEX_DESTROY(&mutex);
static MUTEX static_mutex = PTHREAD_MUTEX_INITIALIZER;
...
MUTEX_LOCK(&static_mutex );
...
MUTEX_UNLOCK(&static_mutex );
...
< Thread.h >
#ifndef __THREAD_H__
#define __THREAD_H__
#ifdef WIN32
#include <windows.h>
#include <process.h>
#define THREAD HANDLE
#define THREAD_FUNC unsigned __stdcall
#else
#include <pthread.h>
#define THREAD pthread_t
#define THREAD_FUNC void*
#endif
int THREAD_CREATE(THREAD *thread, THREAD_FUNC func(void *), void *param);
int THREAD_JOIN(THREAD *thread);
void THREAD_DESTROY(THREAD *thread);
#endif
< Thread.cpp >
#include "Thread.h"
int THREAD_CREATE(THREAD *thread, THREAD_FUNC func(void *), void *param)
{
#ifdef WIN32
*thread = (HANDLE)_beginthreadex(NULL, 0, func, param, 0, NULL);
return *thread == NULL ? -1 : 0;
#else
return pthread_create(thread, NULL, func, param);
#endif
}
int THREAD_JOIN(THREAD *thread)
{
#ifdef WIN32
return WaitForSingleObject(*thread, INFINITE) == WAIT_FAILED ? -1 : 0;
#else
int status;
return pthread_join(*thread, (void **)&status);
#endif
}
void THREAD_DESTROY(THREAD *thread)
{
#ifdef WIN32
if (*thread)
CloseHandle(*thread);
#endif
*thread = NULL;
}
< Thread 사용 >
THREAD thread;
...
static THREAD_FUNC ThreadFunc(void* lpParam) {
...
return 0;
}
THREAD_CREATE(&thread, ThreadFunc, this);
...
THREAD_JOIN(&thread);
THREAD_DESTROY(&thread);
< Event.h>
#ifndef __EVENT_H__
#define __EVENT_H__
#ifdef WIN32
#include <windows.h>
#define EVENT HANDLE
#else
#include <pthread.h>
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
bool triggered;
} mrevent;
#define EVENT mrevent
#endif
void EVENT_INIT(EVENT *event);
void EVENT_DESTROY(EVENT *event);
void EVENT_WAIT(EVENT *event);
void EVENT_SET(EVENT *event);
void EVENT_RESET(EVENT *event);
#endif
< Event.cpp>
#include "Event.h"
void EVENT_INIT(EVENT *event)
{
#ifdef WIN32
*event = CreateEvent(NULL, TRUE, FALSE, NULL);
#else
pthread_mutex_init(&event->mutex, 0);
pthread_cond_init(&event->cond, 0);
event->triggered = false;
#endif
}
void EVENT_DESTROY(EVENT *event)
{
#ifdef WIN32
CloseHandle(*event);
#else
pthread_mutex_destroy(&event->mutex);
pthread_cond_destroy(&event->cond);
#endif
}
void EVENT_WAIT(EVENT *event)
{
#ifdef WIN32
WaitForSingleObject(*event, INFINITE);
#else
pthread_mutex_lock(&event->mutex);
while (!event->triggered)
pthread_cond_wait(&event->cond, &event->mutex);
pthread_mutex_unlock(&event->mutex);
#endif
}
void EVENT_SET(EVENT *event)
{
#ifdef WIN32
SetEvent(*event);
#else
pthread_mutex_lock(&event->mutex);
event->triggered = true;
pthread_cond_signal(&event->cond);
pthread_mutex_unlock(&event->mutex);
#endif
}
void EVENT_RESET(EVENT *event)
{
#ifdef WIN32
ResetEvent(*event);
#else
pthread_mutex_lock(&event->mutex);
event->triggered = false;
pthread_mutex_unlock(&event->mutex);
#endif
}
< Event 사용 >
EVENT event;
...
EVENT_INIT(&event);
...
EVENT_SET(&event);
...
EVENT_RESET(&event);
...
EVENT_WAIT(&event);
...
EVENT_DESTROY(&event);
< MySemaphore.h >
#ifndef __MY_SEMAPHORE_H__
#define __MY_SEMAPHORE_H__
#ifdef WIN32
#include <windows.h>
#define SEMAPHORE HANDLE
#else
#include <semaphore.h>
#define SEMAPHORE sem_t
#endif
int SEM_INIT(SEMAPHORE *sem, int init, int max);
int SEM_WAIT(SEMAPHORE *sem);
int SEM_POST(SEMAPHORE *sem);
int SEM_DESTROY(SEMAPHORE *sem);
#endif
< MySemaphore.cpp >
#include "MySemaphore.h"
int SEM_INIT(SEMAPHORE *sem, int init, int max)
{
#ifdef WIN32
*sem = CreateSemaphore(NULL, init, max, NULL);
return *sem == NULL ? -1 : 0;
#else
return sem_init(sem, 0, init);
#endif
}
int SEM_DESTROY(SEMAPHORE *sem)
{
#ifdef WIN32
if (*sem) {
CloseHandle(*sem);
*sem = NULL;
}
return 0;
#else
return sem_destroy(sem);
#endif
}
int SEM_WAIT(SEMAPHORE *sem)
{
#ifdef WIN32
return WaitForSingleObject(*sem, INFINITE);
#else
return sem_wait(sem);
#endif
}
int SEM_POST(SEMAPHORE *sem)
{
#ifdef WIN32
return ReleaseSemaphore(*sem, 1, NULL) == TRUE ? 0 : -1;
#else
return sem_post(sem);
#endif
}
< MySemaphore 사용 >
SEMAPHORE sem;
...
SEM_INIT(&sem, 0, 10);
...
SEM_WAIT(&sem);
...
SEM_POST(&sem);
...
SEM_DESTROY(&sem);
2015년 8월 25일 화요일
ffmpeg muxer 를 이용한 스트림 저장 - stream record by ffmpeg muxer
< MediaBuffer.h >
< FileWriter.h >
< FileWriter.cpp >
< FFFileWriter.h >
< FFFileWriter.cpp >
< FFFileWriter 사용법 >
FFFileWriter *pWriter = new FFFileWriter();
// 해당 스트림에 맞는 비디오/오디오 속성을 입력
FileWriterOpenParam *param = new FileWriterOpenParam();
param->video_codec_id = AV_CODEC_ID_H264;
param->width = 1280;
param->height = 720;
param->frame_rate.num = 1;
param->frame_rate.den = 30;
param->gop_size = 30;
...
// 파일 열기
pWriter->open("record_file.ts", *param);
delete param;
...
// 파일 쓰기
unsigned char *buf => encoded buffer
int buf_size => encoded buffer size
MediaBuffer *pBuffer = new MediaBuffer();
pBuffer->mediaType = VideoMedia;
memcpy(pBuffer->data, buf, buf_size);
pBuffer->size = buf_size;
pBuffer->pts = pts 값 (millisecond 단위)
pBuffer->dts = dts 값 (millisecond 단위)
pWriter->write(pBuffer);
delete pBuffer;
// 파일 닫기 - 모든 스트림 저장후 마무리
pWriter->close();
delete pWriter;
typedef enum {
VideoMedia = 0x01,
AudioMedia = 0x02,
SubtitleMedia = 0x04,
} MediaType;
class MediaBuffer {
public:
MediaType mediaType;
unsigned char* data;
int size;
int keyFrame;
int64_t pts;
int64_t dts;
public:
MediaBuffer() {
data = NULL;
size = keyFrame = 0;
pts = dts = 0;
}
virtual ~MediaBuffer() {
if (data) {
delete[] data;
data = NULL;
}
}
};
< FileWriter.h >
#ifndef __FILE_WRITER_H__
#define __FILE_WRITER_H__
#include "MediaBuffer.h"
extern "C" {
#include "libavformat/avformat.h"
};
class FileWriterOpenParam {
public:
enum AVCodecID video_codec_id;
int width;
int height;
AVRational frame_rate;
unsigned int gop_size;
enum AVCodecID audio_codec_id;
enum AVSampleFormat sample_fmt;
int channels;
int sample_rate;
unsigned char* video_extradata;
int video_extradata_size;
unsigned char* audio_extradata;
int audio_extradata_size;
int filepath_format;
int buffering_time;
FileWriterOpenParam();
FileWriterOpenParam(FileWriterOpenParam *param);
virtual ~FileWriterOpenParam();
};
enum FILE_SPLIT { SPLIT_DURATION, SPLIT_LOCALTIME };
class FileWriter
{
protected:
FileWriter();
public:
virtual ~FileWriter();
public:
virtual int open(char *filepath, FileWriterOpenParam ¶m);
virtual int write(MediaBuffer *pBuffer);
virtual void close();
protected:
int64_t m_nStartPTS;
int64_t m_nStartDTS;
int m_nFormat;
unsigned char* m_pVideoExtraData;
int m_nVideoExtraDataSize;
unsigned char* m_pAudioExtraData;
int m_nAudioExtraDataSize;
};
#endif
< FileWriter.cpp >
#include "FileWriter.h"
#include "GlobalEnv.h"
FileWriterOpenParam::FileWriterOpenParam()
{
video_codec_id = AV_CODEC_ID_NONE;
width = height = 0;
frame_rate.num = 1;
frame_rate.den = 30;
gop_size = 30;
audio_codec_id = AV_CODEC_ID_NONE;
sample_fmt = AV_SAMPLE_FMT_NONE;
channels = sample_rate = 0;
video_extradata = audio_extradata = NULL;
video_extradata_size = audio_extradata_size = 0;
filepath_format = 0;
buffering_time = 0;
}
FileWriterOpenParam::FileWriterOpenParam(FileWriterOpenParam *param)
{
video_codec_id = param->video_codec_id;
width = param->width;
height = param->height;
frame_rate.num = param->frame_rate.num;
frame_rate.den = param->frame_rate.den;
gop_size = param->gop_size;
audio_codec_id = param->audio_codec_id;
sample_fmt = param->sample_fmt;
channels = param->channels;
sample_rate = param->sample_rate;
video_extradata = audio_extradata = NULL;
video_extradata_size = audio_extradata_size = 0;
filepath_format = param->filepath_format;
if (param->video_extradata_size > 0) {
video_extradata_size = param->video_extradata_size;
video_extradata = new unsigned char[video_extradata_size];
memcpy(video_extradata, param->video_extradata, video_extradata_size);
}
if (param->audio_extradata_size > 0) {
audio_extradata_size = param->audio_extradata_size;
audio_extradata = new unsigned char[audio_extradata_size];
memcpy(audio_extradata, param->audio_extradata, audio_extradata_size);
}
buffering_time = param->buffering_time;
}
FileWriterOpenParam::~FileWriterOpenParam()
{
if (video_extradata) { delete[] video_extradata; video_extradata = NULL; }
if (audio_extradata) { delete[] audio_extradata; audio_extradata = NULL; }
}
FileWriter::FileWriter() : m_nFormat(0), m_pVideoExtraData(NULL), m_nVideoExtraDataSize(0), m_pAudioExtraData(NULL), m_nAudioExtraDataSize(0)
{
}
FileWriter::~FileWriter()
{
}
int FileWriter::open(char *filepath, FileWriterOpenParam ¶m)
{
m_nStartPTS = m_nStartDTS = 0;
m_nFormat = param.filepath_format;
DXPRINTF("FileWriter %s file opened\n", filepath);
return 0;
}
void FileWriter::close()
{
DX_DELETE_OBJECT(m_pVideoExtraData);
m_nVideoExtraDataSize = 0;
DX_DELETE_OBJECT(m_pAudioExtraData);
m_nAudioExtraDataSize = 0;
DXPRINTF("FileWriter closed\n");
}
int FileWriter::write(MediaBuffer *pBuffer)
{
return 0;
}
< FFFileWriter.h >
#ifndef __FFFILE_WRITER_H__
#define __FFFILE_WRITER_H__
#include "FileWriter.h"
class FFFileWriter : public FileWriter
{
public:
FFFileWriter();
virtual ~FFFileWriter();
virtual int open(char *filepath, FileWriterOpenParam ¶m);
virtual int write(MediaBuffer *pBuffer);
virtual void close();
protected:
AVStream* addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam ¶m);
protected:
AVFormatContext* m_pFormatCtx;
AVStream* m_pVideoStream;
AVStream* m_pAudioStream;
AVPacket m_avPacket;
FILE* m_pFile;
bool m_bWriteHeader;
};
#endif
< FFFileWriter.cpp >
#include "FFFileWriter.h"
#include "GlobalEnv.h"
FFFileWriter::FFFileWriter() : m_pFormatCtx(NULL), m_pVideoStream(NULL), m_pAudioStream(NULL), m_pFile(NULL)
{
av_register_all();
m_bWriteHeader = false;
}
FFFileWriter::~FFFileWriter()
{
}
int FFFileWriter::open(char *filepath, struct FileWriterOpenParam ¶m)
{
int err;
char errbuf[128];
err = avformat_alloc_output_context2(&m_pFormatCtx, NULL, NULL, filepath);
if (!m_pFormatCtx) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avformat_alloc_output_context2 failed, err: %d, %s\n", err, errbuf);
if (err == -22) {
m_pFile = fopen(filepath, "wb");
if (m_pFile) {
FileWriter::open(filepath, param);
return 0;
}
}
return -1;
}
av_init_packet(&m_avPacket);
AVOutputFormat *fmt = m_pFormatCtx->oformat;
fmt->video_codec = param.video_codec_id;
fmt->audio_codec = param.audio_codec_id;
if (fmt->video_codec != AV_CODEC_ID_NONE)
m_pVideoStream = addStream(AVMEDIA_TYPE_VIDEO, param.video_codec_id, param);
if (fmt->audio_codec != AV_CODEC_ID_NONE)
m_pAudioStream = addStream(AVMEDIA_TYPE_AUDIO, param.audio_codec_id, param);
err = avio_open(&m_pFormatCtx->pb, filepath, AVIO_FLAG_WRITE);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avio_open failed, err: %d, %s\n", err, errbuf);
return -1;
}
err = avformat_write_header(m_pFormatCtx, NULL);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avformat_write_header failed, err: %d, %s\n", err, errbuf);
return -1;
}
m_bWriteHeader = true;
// write extra data
if (m_pVideoStream && param.video_extradata_size > 0) {
av_init_packet(&m_avPacket);
m_avPacket.stream_index = m_pVideoStream->index;
m_avPacket.data = param.video_extradata;
m_avPacket.size = param.video_extradata_size;
m_avPacket.pts = m_avPacket.dts = 0;
m_pVideoStream->cur_dts = 0;
av_write_frame(m_pFormatCtx, &m_avPacket);
}
if (m_pAudioStream && param.audio_extradata_size > 0) {
av_init_packet(&m_avPacket);
m_avPacket.stream_index = m_pAudioStream->index;
m_avPacket.data = param.audio_extradata;
m_avPacket.size = param.audio_extradata_size;
m_avPacket.pts = m_avPacket.dts = 0;
m_pVideoStream->cur_dts = 0;
av_write_frame(m_pFormatCtx, &m_avPacket);
}
FileWriter::open(filepath, param);
return 0;
}
AVStream* FFFileWriter::addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam ¶m)
{
AVCodecContext *codecCtx;
AVStream *stream = NULL;
#if 0
AVCodec *codec = avcodec_find_encoder(codec_id);
if (!codec) {
DXPRINTF("Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
}
#endif
if (mediaType == AVMEDIA_TYPE_VIDEO) {
stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->video_codec);
} else if (mediaType == AVMEDIA_TYPE_AUDIO) {
if (param.sample_fmt == AV_SAMPLE_FMT_NONE) return NULL; // cannot write
stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->audio_codec);
}
if (!stream) {
DXPRINTF("avformat_new_stream failed\n");
return NULL;
}
stream->id = m_pFormatCtx->nb_streams-1;
codecCtx = stream->codec;
codecCtx->codec_id = codec_id;
codecCtx->codec_type = mediaType;
if (mediaType == AVMEDIA_TYPE_AUDIO) {
int sample_rate = param.sample_rate == 0 ? 44100 : param.sample_rate; // to prevent divide by zero exception
//codecCtx->sample_fmt = param.sample_fmt;
codecCtx->channels = param.channels;
codecCtx->sample_rate = sample_rate;
codecCtx->time_base.num = 1;
codecCtx->time_base.den = sample_rate;
if (param.audio_extradata_size > 0) {
codecCtx->extradata = (uint8_t *)av_malloc(param.audio_extradata_size);
memcpy(codecCtx->extradata, param.audio_extradata, param.audio_extradata_size);
codecCtx->extradata_size = param.audio_extradata_size;
}
} else if (mediaType == AVMEDIA_TYPE_VIDEO) {
// to prevent divide by zero exception
int width = param.width == 0 ? 1280 : param.width;
int height = param.height == 0 ? 720 : param.height;
if (param.frame_rate.den == 0) {
param.frame_rate.den = 30;
param.frame_rate.num = 1;
param.gop_size = 30;
}
codecCtx->codec_id = codec_id;
codecCtx->width = width;
codecCtx->height = height;
codecCtx->time_base.den = param.frame_rate.den;
codecCtx->time_base.num = param.frame_rate.num;
codecCtx->gop_size = param.gop_size;
codecCtx->pix_fmt = PIX_FMT_YUV420P;
if (param.video_extradata_size > 0) {
codecCtx->extradata = (uint8_t *)av_malloc(param.video_extradata_size);
memcpy(codecCtx->extradata, param.video_extradata, param.video_extradata_size);
codecCtx->extradata_size = param.video_extradata_size;
}
}
if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
int err = avcodec_parameters_from_context(stream->codecpar, codecCtx);
return stream;
}
void FFFileWriter::close()
{
if (m_pFormatCtx) {
if (m_bWriteHeader) {
av_write_trailer(m_pFormatCtx);
m_bWriteHeader = false;
}
if (m_pVideoStream) {
if (m_pVideoStream->codec) {
if (m_pVideoStream->codec->extradata) {
av_freep(&m_pVideoStream->codec->extradata);
m_pVideoStream->codec->extradata_size = 0;
}
avcodec_close(m_pVideoStream->codec);
}
}
if (m_pAudioStream) {
if (m_pAudioStream->codec) {
if (m_pAudioStream->codec->extradata) {
av_freep(&m_pAudioStream->codec->extradata);
m_pAudioStream->codec->extradata_size = 0;
}
avcodec_close(m_pAudioStream->codec);
}
}
av_free_packet(&m_avPacket);
avio_close(m_pFormatCtx->pb);
avformat_free_context(m_pFormatCtx);
m_pFormatCtx = NULL;
}
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
FileWriter::close();
}
int FFFileWriter::write(MediaBuffer *pBuffer)
{
if (m_pFile) { // raw stream writing
if (pBuffer->mediaType == VideoMedia)
return fwrite(pBuffer->data, pBuffer->size, 1, m_pFile);
return -1;
}
AVStream *stream = NULL;
av_init_packet(&m_avPacket);
if (pBuffer->mediaType == VideoMedia) {
stream = m_pVideoStream;
if (pBuffer->keyFrame == 1)
m_avPacket.flags |= AV_PKT_FLAG_KEY;
} else if (pBuffer->mediaType == AudioMedia) {
stream = m_pAudioStream;
if (!m_pVideoStream)
m_avPacket.flags |= AV_PKT_FLAG_KEY;
}
if (!stream) return -1;
m_avPacket.stream_index = stream->index;
m_avPacket.data = pBuffer->data;
m_avPacket.size = pBuffer->size;
// timestamp
if (pBuffer->pts == -1) m_nStartPTS = 0;
if (pBuffer->dts == -1) m_nStartDTS = 0;
if (m_nStartPTS == 0) m_nStartPTS = pBuffer->pts;
if (m_nStartDTS == 0) m_nStartDTS = pBuffer->dts;
int64_t pts = pBuffer->pts - m_nStartPTS;
int64_t dts = pBuffer->dts - m_nStartDTS;
m_avPacket.pts = pts;
m_avPacket.dts = dts;
AVRational time_base = { 1, 1000 };
av_packet_rescale_ts(&m_avPacket, time_base, stream->time_base);
stream->cur_dts = m_avPacket.dts - 1;
return av_write_frame(m_pFormatCtx, &m_avPacket);
}
< FFFileWriter 사용법 >
FFFileWriter *pWriter = new FFFileWriter();
// 해당 스트림에 맞는 비디오/오디오 속성을 입력
FileWriterOpenParam *param = new FileWriterOpenParam();
param->video_codec_id = AV_CODEC_ID_H264;
param->width = 1280;
param->height = 720;
param->frame_rate.num = 1;
param->frame_rate.den = 30;
param->gop_size = 30;
...
// 파일 열기
pWriter->open("record_file.ts", *param);
delete param;
...
// 파일 쓰기
unsigned char *buf => encoded buffer
int buf_size => encoded buffer size
MediaBuffer *pBuffer = new MediaBuffer();
pBuffer->mediaType = VideoMedia;
memcpy(pBuffer->data, buf, buf_size);
pBuffer->size = buf_size;
pBuffer->pts = pts 값 (millisecond 단위)
pBuffer->dts = dts 값 (millisecond 단위)
pWriter->write(pBuffer);
delete pBuffer;
// 파일 닫기 - 모든 스트림 저장후 마무리
pWriter->close();
delete pWriter;
라벨:
ffmpeg
2015년 4월 23일 목요일
안드로이드 SurfaceView 에 ffmpeg 디코딩 영상 복사하기
1. JNI 를 통해 SurfaceView 의 Surface 객체 받기
public class DXMediaPlayer extends SurfaceView implements SurfaceHolder.Callback {
...
private native void setSurface(int idx, Surface surface);
...
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(LOG_TAG(), "surface created");
setSurface(mIdx, holder.getSurface());
}
}
ANativeWindow* m_pNativeWindow;
...
JNIEXPORT void JNICALL Java_com_dxmediaplayer_DXMediaPlayer_setSurface(JNIEnv *pEnv, jobject pObj, jint idx, jobject pSurface)
{
if (pSurface != NULL) {
if (!m_pNativeWindow)
m_pNativeWindow = ANativeWindow_fromSurface(pEnv, pSurface);
} else {
if (m_pNativeWindow)
ANativeWindow_release(m_pNativeWindow);
m_pNativeWindow = NULL;
m_nWindowWidth = m_nWindowHeight = 0;
}
}
2. ffmpeg 디코딩 후 Surface 에 복사
ffmpeg 디코딩후 YUV420 -> RGBA 변환(스케일러 사용)후 그대로 복사해주면 된다. 이때 Surface 사이즈에 맞게 스케일링을 할 필요가 없다 (안드로이드에서 스케일링을 해준다)
즉, ffmpeg 스케일러의 sws_getContext 함수에서 소스 사이즈와 데스티네이션 사이즈를 똑같이 AVFrame의 width/height로 해준다(픽셀포멧만 변환)
그리고 아래와같이 Surface에 복사
* opengl es (GLSurfaceView) 를 사용할 필요가 없다.
public class DXMediaPlayer extends SurfaceView implements SurfaceHolder.Callback {
...
private native void setSurface(int idx, Surface surface);
...
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(LOG_TAG(), "surface created");
setSurface(mIdx, holder.getSurface());
}
}
ANativeWindow* m_pNativeWindow;
...
JNIEXPORT void JNICALL Java_com_dxmediaplayer_DXMediaPlayer_setSurface(JNIEnv *pEnv, jobject pObj, jint idx, jobject pSurface)
{
if (pSurface != NULL) {
if (!m_pNativeWindow)
m_pNativeWindow = ANativeWindow_fromSurface(pEnv, pSurface);
} else {
if (m_pNativeWindow)
ANativeWindow_release(m_pNativeWindow);
m_pNativeWindow = NULL;
m_nWindowWidth = m_nWindowHeight = 0;
}
}
2. ffmpeg 디코딩 후 Surface 에 복사
ffmpeg 디코딩후 YUV420 -> RGBA 변환(스케일러 사용)후 그대로 복사해주면 된다. 이때 Surface 사이즈에 맞게 스케일링을 할 필요가 없다 (안드로이드에서 스케일링을 해준다)
즉, ffmpeg 스케일러의 sws_getContext 함수에서 소스 사이즈와 데스티네이션 사이즈를 똑같이 AVFrame의 width/height로 해준다(픽셀포멧만 변환)
그리고 아래와같이 Surface에 복사
ANativeWindow_Buffer windowBuffer;
if (ANativeWindow_lock(m_pWindow, &windowBuffer, NULL) < 0) {
DXPRINTF("ANativeWindow_lock failed\n");
} else {
if (windowBuffer.width == windowBuffer.stride) {
memcpy(windowBuffer.bits, pFrame->data, pFrame->size);
} else {
int bpp = GetBitPerPixel(pFrame->pix_fmt)>>3;
int stride = windowBuffer.stride * bpp;
uint8_t *ptr = pFrame->data;
uint8_t *dst = (uint8_t*)windowBuffer.bits;
for (int h=0; h<pFrame->dst_height; h++) {
memcpy(&dst[h*stride], ptr, stride);
ptr += pFrame->dst_width * bpp;
}
}
}
ANativeWindow_unlockAndPost(m_pWindow);
* opengl es (GLSurfaceView) 를 사용할 필요가 없다.
2015년 1월 26일 월요일
android ffmpeg build
1. make android toolchain
$ /cygdrive/c/android-ndk-r10d/build/tools/make-standalone-toolchain.sh --platform=android-18 --install-dir=c:/my-android-toolchain-18 --arch=arm --system=windows-x86_64
2. ffmpeg configure
./configure --target-os=linux --arch=arm --enable-cross-compile --cc=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi-gcc --cross-prefix=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi- --extra-cflags="-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon" --extra-ldflags="-Wl,--fix-cortex-a8" --disable-doc --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avdevice --disable-network --disable-devices --disable-filters --prefix=../ffmpeg-build
make
make install
// make 후 아래 에러 발생시 config.h 수정
$ make
CC libavfilter/allfilters.o
In file included from ./libavutil/common.h:83:0,
from ./libavutil/avutil.h:289,
from libavfilter/avfilter.h:39,
from libavfilter/allfilters.c:22:
./config.h:9:18: warning: missing terminating " character [enabled by default]
(GCC)"e CC_IDENT "gcc 4.8
^
./config.h:10:7: warning: missing terminating " character [enabled by default]
#define av_restrict restrict
^
./config.h:10:2: error: missing terminating " character
#define av_restrict restrict
^
In file included from ./libavutil/intmath.h:30:0,
from ./libavutil/common.h:84,
from ./libavutil/avutil.h:289,
from libavfilter/avfilter.h:39,
from libavfilter/allfilters.c:22:
./libavutil/arm/intmath.h:34:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'static'
static av_always_inline av_const unsigned av_clip_uint8_arm(int a)
^
common.mak:49: recipe for target 'libavfilter/allfilters.o' failed
make: *** [libavfilter/allfilters.o] Error 1
config.h 에서
#define CC_IDENT "gcc 4.8
(GCC)"
#define CC_IDENT "gcc 4.8 (GCC)"
로 수정
3. modify Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DXMediaPlayer
LOCAL_SRC_FILES := com_example_dxmediaplayertest_DXMediaPlayer.cpp \
DXMediaPlayerCtrl.cpp
LOCAL_LDLIBS := -llog -lz -ljnigraphics -landroid
LOCAL_STATIC_LIBRARIES := avformat avcodec avutil swscale swresample => order must be kept !!!
LOCAL_CFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
include $(BUILD_SHARED_LIBRARY)
$ /cygdrive/c/android-ndk-r10d/build/tools/make-standalone-toolchain.sh --platform=android-18 --install-dir=c:/my-android-toolchain-18 --arch=arm --system=windows-x86_64
2. ffmpeg configure
./configure --target-os=linux --arch=arm --enable-cross-compile --cc=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi-gcc --cross-prefix=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi- --extra-cflags="-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon" --extra-ldflags="-Wl,--fix-cortex-a8" --disable-doc --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avdevice --disable-network --disable-devices --disable-filters --prefix=../ffmpeg-build
make
make install
// make 후 아래 에러 발생시 config.h 수정
$ make
CC libavfilter/allfilters.o
In file included from ./libavutil/common.h:83:0,
from ./libavutil/avutil.h:289,
from libavfilter/avfilter.h:39,
from libavfilter/allfilters.c:22:
./config.h:9:18: warning: missing terminating " character [enabled by default]
(GCC)"e CC_IDENT "gcc 4.8
^
./config.h:10:7: warning: missing terminating " character [enabled by default]
#define av_restrict restrict
^
./config.h:10:2: error: missing terminating " character
#define av_restrict restrict
^
In file included from ./libavutil/intmath.h:30:0,
from ./libavutil/common.h:84,
from ./libavutil/avutil.h:289,
from libavfilter/avfilter.h:39,
from libavfilter/allfilters.c:22:
./libavutil/arm/intmath.h:34:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'static'
static av_always_inline av_const unsigned av_clip_uint8_arm(int a)
^
common.mak:49: recipe for target 'libavfilter/allfilters.o' failed
make: *** [libavfilter/allfilters.o] Error 1
config.h 에서
#define CC_IDENT "gcc 4.8
(GCC)"
#define CC_IDENT "gcc 4.8 (GCC)"
로 수정
3. modify Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DXMediaPlayer
LOCAL_SRC_FILES := com_example_dxmediaplayertest_DXMediaPlayer.cpp \
DXMediaPlayerCtrl.cpp
LOCAL_LDLIBS := -llog -lz -ljnigraphics -landroid
LOCAL_STATIC_LIBRARIES := avformat avcodec avutil swscale swresample => order must be kept !!!
LOCAL_CFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
include $(BUILD_SHARED_LIBRARY)
2014년 12월 4일 목요일
H.264 RFC3984 NAL 패킷전송
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년 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();
}
라벨:
wave,
wavein/waveout
피드 구독하기:
글 (Atom)