Initial setup for CLion

This commit is contained in:
2026-03-28 16:54:11 +11:00
parent 239cc02591
commit 7b4134133c
1136 changed files with 811916 additions and 0 deletions

214
MediaClient/media/alsa.cpp Normal file
View File

@@ -0,0 +1,214 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "alsa.h"
BOOL alsa_get_device_name(int index, char * filename, int len)
{
int card = -1, count = 0, ret;
snd_ctl_card_info_t * info;
snd_ctl_card_info_alloca(&info);
ret = snd_card_next(&card);
while (ret == 0 && card != -1)
{
char name[16];
snd_ctl_t * ctl;
snprintf(name, sizeof(name), "hw:%d", card);
if (snd_ctl_open(&ctl, name, SND_CTL_NONBLOCK) < 0)
{
ret = snd_card_next(&card);
continue;
}
if (snd_ctl_card_info(ctl, info) < 0)
{
snd_ctl_close(ctl);
ret = snd_card_next(&card);
continue;
}
snd_ctl_close(ctl);
if (index == count)
{
strncpy(filename, name, len);
return TRUE;
}
ret = snd_card_next(&card);
}
return FALSE;
}
BOOL alsa_open_device(ALSACTX * p_alsa, snd_pcm_stream_t mode, uint32 * sample_rate, int channels)
{
int res, flags;
snd_pcm_format_t format;
snd_pcm_t *h;
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t buffer_size, period_size = 0;
if (NULL == p_alsa)
{
return FALSE;
}
flags = SND_PCM_NONBLOCK;
format = SND_PCM_FORMAT_S16_LE;
p_alsa->framesize = 16 / 8 * channels;
res = snd_pcm_open(&h, p_alsa->devname, mode, flags);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot open audio device %s (%s)\r\n", p_alsa->devname, snd_strerror(res));
return FALSE;
}
snd_pcm_hw_params_alloca(&hw_params);
res = snd_pcm_hw_params_any(h, hw_params);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot initialize hardware parameter structure (%s)\r\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_access(h, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set access type (%s)\r\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_format(h, hw_params, format);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set sample format %d (%s)\r\n", format, snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_rate_near(h, hw_params, sample_rate, 0);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set sample rate (%s)\r\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_channels(h, hw_params, channels);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set channel count to %d (%s)\r\n", channels, snd_strerror(res));
goto fail;
}
snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
buffer_size = (buffer_size > ALSA_BUFFER_SIZE_MAX ? ALSA_BUFFER_SIZE_MAX : buffer_size);
res = snd_pcm_hw_params_set_buffer_size_near(h, hw_params, &buffer_size);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set ALSA buffer size (%s)\r\n", snd_strerror(res));
goto fail;
}
snd_pcm_hw_params_get_period_size_min(hw_params, &period_size, NULL);
if (!period_size)
{
period_size = buffer_size / 4;
}
if (period_size < 64)
{
period_size = 64;
}
res = snd_pcm_hw_params_set_period_size_near(h, hw_params, &period_size, NULL);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set ALSA period size (%s)\r\n", snd_strerror(res));
goto fail;
}
p_alsa->period_size = period_size;
res = snd_pcm_hw_params(h, hw_params);
if (res < 0)
{
log_print(HT_LOG_ERR, "cannot set parameters (%s)\r\n", snd_strerror(res));
goto fail;
}
p_alsa->handler = h;
return TRUE;
fail:
snd_pcm_close(h);
return FALSE;
}
void alsa_close_device(ALSACTX * p_alsa)
{
if (p_alsa->handler)
{
if (snd_pcm_stream(p_alsa->handler) == SND_PCM_STREAM_PLAYBACK)
{
snd_pcm_nonblock(p_alsa->handler, 0);
snd_pcm_drain(p_alsa->handler);
}
snd_pcm_close(p_alsa->handler);
p_alsa->handler = NULL;
}
}
int alsa_xrun_recover(snd_pcm_t * handler, int err)
{
if (err == -EPIPE)
{
err = snd_pcm_prepare(handler);
if (err < 0)
{
log_print(HT_LOG_ERR, "snd_pcm_prepare failed: %s\r\n", snd_strerror(err));
}
else
{
return 0;
}
}
else if (err == -ESTRPIPE)
{
log_print(HT_LOG_ERR, "-ESTRPIPE... Unsupported!\n");
}
return err;
}

59
MediaClient/media/alsa.h Normal file
View File

@@ -0,0 +1,59 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef ALSA_H
#define ALSA_H
#include "sys_inc.h"
#include <alsa/asoundlib.h>
/***************************************************************************************/
#define ALSA_BUFFER_SIZE_MAX 131072
/***************************************************************************************/
typedef struct
{
char devname[32]; // device name
snd_pcm_t * handler; // device handler
int period_size; // preferred size for reads and writes, in frames
int framesize; // bytes per sample * channels
} ALSACTX;
#ifdef __cplusplus
extern "C" {
#endif
BOOL alsa_get_device_name(int index, char * filename, int len);
BOOL alsa_open_device(ALSACTX * p_alsa, snd_pcm_stream_t mode, uint32 * sample_rate, int channels);
void alsa_close_device(ALSACTX * p_alsa);
int alsa_xrun_recover(snd_pcm_t * handler, int err);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,119 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_capture.h"
#include "lock.h"
/***************************************************************************************/
CAudioCapture * CAudioCapture::m_pInstance[] = {NULL, NULL, NULL, NULL};
void * CAudioCapture::m_pInstMutex = sys_os_create_mutex();
/***************************************************************************************/
CAudioCapture::CAudioCapture()
{
m_nDevIndex = 0;
m_nRefCnt = 0;
m_nChannels = 2;
m_nSampleRate = 8000;
m_nBitrate = 0;
m_nSampleSize = 16;
m_pMutex = sys_os_create_mutex();
m_bInited = FALSE;
m_bCapture = FALSE;
m_hCapture = 0;
}
CAudioCapture::~CAudioCapture()
{
sys_os_destroy_sig_mutex(m_pMutex);
}
CAudioCapture * CAudioCapture::getInstance(int devid)
{
return NULL;
}
void CAudioCapture::freeInstance(int devid)
{
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
{
return;
}
if (m_pInstance[devid])
{
sys_os_mutex_enter(m_pInstMutex);
if (m_pInstance[devid])
{
m_pInstance[devid]->m_nRefCnt--;
if (m_pInstance[devid]->m_nRefCnt <= 0)
{
delete m_pInstance[devid];
m_pInstance[devid] = NULL;
}
}
sys_os_mutex_leave(m_pInstMutex);
}
}
char * CAudioCapture::getAuxSDPLine(int rtp_pt)
{
return m_encoder.getAuxSDPLine(rtp_pt);
}
int CAudioCapture::getDeviceNums()
{
return 0;
}
BOOL CAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
{
return FALSE;
}
BOOL CAudioCapture::startCapture()
{
return FALSE;
}
void CAudioCapture::stopCapture(void)
{
}
void CAudioCapture::addCallback(AudioDataCallback pCallback, void * pUserdata)
{
m_encoder.addCallback(pCallback, pUserdata);
}
void CAudioCapture::delCallback(AudioDataCallback pCallback, void * pUserdata)
{
m_encoder.delCallback(pCallback, pUserdata);
}

View File

@@ -0,0 +1,87 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _AUDIO_CAPTURE_H
#define _AUDIO_CAPTURE_H
/***************************************************************************************/
#include "audio_encoder.h"
/***************************************************************************************/
#define MAX_AUDIO_DEV_NUMS 8
#define DEF_AUDIO_CAP_DEV (MAX_AUDIO_DEV_NUMS-1)
/***************************************************************************************/
class CAudioCapture
{
public:
virtual ~CAudioCapture();
// get audio capture devcie nubmers
static int getDeviceNums();
// get single instance
static CAudioCapture * getInstance(int devid);
// free instance
virtual void freeInstance(int devid);
virtual BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
virtual BOOL startCapture();
virtual char * getAuxSDPLine(int rtp_pt);
virtual void addCallback(AudioDataCallback pCallback, void * pUserdata);
virtual void delCallback(AudioDataCallback pCallback, void * pUserdata);
virtual BOOL isCapturing() {return m_bCapture;}
protected:
CAudioCapture();
CAudioCapture(CAudioCapture &obj);
virtual void stopCapture();
public:
int m_nDevIndex;
int m_nRefCnt; // single instance ref count
static void * m_pInstMutex; // instance mutex
static CAudioCapture * m_pInstance[MAX_AUDIO_DEV_NUMS];
protected:
int m_nChannels; // channel number
int m_nSampleRate; // sample rate
int m_nBitrate; // bitrate
int m_nSampleSize; // sample size
CAudioEncoder m_encoder; // audio encoder
void * m_pMutex; // mutex
BOOL m_bInited; // inited flag
BOOL m_bCapture; // capturing flag
pthread_t m_hCapture; // capture thread handler
};
#endif

View File

@@ -0,0 +1,176 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "lock.h"
#include "audio_capture_android.h"
void dataCallback(uint8 * pdata, int len, void * puser)
{
CAAudioCapture * pthis = (CAAudioCapture *) puser;
pthis->dataCallback(pdata, len);
}
CAAudioCapture::CAAudioCapture()
: CAudioCapture()
, m_pInput(0)
{
}
CAAudioCapture::~CAAudioCapture()
{
stopCapture();
}
CAudioCapture * CAAudioCapture::getInstance(int devid)
{
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
{
return NULL;
}
sys_os_mutex_enter(m_pInstMutex);
if (NULL == m_pInstance[devid])
{
m_pInstance[devid] = new CAAudioCapture;
if (m_pInstance[devid])
{
m_pInstance[devid]->m_nRefCnt++;
m_pInstance[devid]->m_nDevIndex = devid;
}
}
else
{
m_pInstance[devid]->m_nRefCnt++;
}
sys_os_mutex_leave(m_pInstMutex);
return m_pInstance[devid];
}
int CAAudioCapture::getDeviceNums()
{
return 1;
}
void CAAudioCapture::listDevice()
{
}
int CAAudioCapture::getDeviceIndex(const char * name)
{
return 0;
}
BOOL CAAudioCapture::getDeviceName(int index, char * name, int namesize)
{
strncpy(name, "Default", namesize);
return TRUE;
}
BOOL CAAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
{
CLock lock(m_pMutex);
if (m_bInited)
{
return TRUE;
}
m_nChannels = channels;
m_nSampleRate = samplerate;
m_nBitrate = bitrate;
AudioEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcChannels = 2;
params.SrcSamplefmt = AV_SAMPLE_FMT_S16;
params.SrcSamplerate = samplerate;
params.DstChannels = channels;
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
params.DstSamplerate = samplerate;
params.DstBitrate = bitrate;
params.DstCodec = codec;
if (m_encoder.init(&params) == FALSE)
{
return FALSE;
}
m_bInited = TRUE;
return TRUE;
}
BOOL CAAudioCapture::startCapture()
{
CLock lock(m_pMutex);
if (!m_bInited)
{
return FALSE;
}
if (m_pInput)
{
return TRUE;
}
m_pInput = new GlesInput();
if (NULL == m_pInput)
{
return FALSE;
}
m_pInput->setCallback(::dataCallback, this);
if (!m_pInput->start(m_nSampleRate))
{
log_print(HT_LOG_ERR, "%s, start audio recoding failed\r\n", __FUNCTION__);
return FALSE;
}
return TRUE;
}
void CAAudioCapture::stopCapture(void)
{
CLock lock(m_pMutex);
if (m_pInput)
{
delete m_pInput;
m_pInput = NULL;
}
m_bInited = FALSE;
}
void CAAudioCapture::dataCallback(uint8 * pdata, int len)
{
m_encoder.encode(pdata, len);
}

View File

@@ -0,0 +1,61 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_CAPTURE_ANDROID_H
#define AUDIO_CAPTURE_ANDROID_H
#include "sys_inc.h"
#include "audio_capture.h"
#include "gles_input.h"
class CAAudioCapture : public CAudioCapture
{
public:
// get audio capture devcie nubmers
static int getDeviceNums();
// list avaivalbe video capture device
static void listDevice();
// get device index by device name
static int getDeviceIndex(const char * name);
// get device name by device index
static BOOL getDeviceName(int index, char * name, int namesize);
// get single instance
static CAudioCapture * getInstance(int devid);
BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
BOOL startCapture();
void dataCallback(uint8 * pdata, int len);
private:
CAAudioCapture();
CAAudioCapture(CAAudioCapture &obj);
~CAAudioCapture();
void stopCapture();
private:
GlesInput * m_pInput;
};
#endif

View File

@@ -0,0 +1,57 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_CAPTURE_AVF_H
#define AUDIO_CAPTURE_AVF_H
typedef struct
{
uint8 *data[4];
int linesize[4];
int samplerate;
int channels;
int format;
int samples;
} avf_audio_data;
typedef void (*avf_audio_callback)(avf_audio_data * data, void * userdata);
#ifdef __cplusplus
extern "C" {
#endif
int avf_audio_device_nums();
void avf_audio_device_list();
int avf_audio_device_get_index(const char * name);
BOOL avf_audio_device_get_name(int index, char * name, int namesize);
void * avf_audio_init(int device_index, int samplerate, int channels);
void avf_audio_uninit(void * ctx);
void avf_audio_set_callback(void * ctx, avf_audio_callback cb, void * userdata);
int avf_audio_get_samplerate(void * ctx);
int avf_audio_get_channels(void * ctx);
int avf_audio_get_samplefmt(void * ctx);
BOOL avf_audio_read(void * ctx, int * samples);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,425 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_capture_linux.h"
#include "lock.h"
/***************************************************************************************/
static void * audioCaptureThread(void * argv)
{
CLAudioCapture *capture = (CLAudioCapture *)argv;
capture->captureThread();
return NULL;
}
/***************************************************************************************/
CLAudioCapture::CLAudioCapture() : CAudioCapture()
{
memset(&m_alsa, 0, sizeof(m_alsa));
m_alsa.period_size = 64;
m_alsa.framesize = 16 / 8 * 2;
m_pData = NULL;
}
CLAudioCapture::~CLAudioCapture()
{
stopCapture();
}
CAudioCapture * CLAudioCapture::getInstance(int devid)
{
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
{
return NULL;
}
sys_os_mutex_enter(m_pInstMutex);
if (NULL == m_pInstance[devid])
{
m_pInstance[devid] = new CLAudioCapture();
if (m_pInstance[devid])
{
m_pInstance[devid]->m_nRefCnt++;
m_pInstance[devid]->m_nDevIndex = devid;
}
}
else
{
m_pInstance[devid]->m_nRefCnt++;
}
sys_os_mutex_leave(m_pInstMutex);
return m_pInstance[devid];
}
int CLAudioCapture::getDeviceNums()
{
int card = -1, count = 0, ret;
snd_ctl_card_info_t * info;
snd_ctl_card_info_alloca(&info);
ret = snd_card_next(&card);
while (ret == 0 && card != -1)
{
char name[16];
snd_ctl_t * ctl;
snprintf(name, sizeof(name), "hw:%d", card);
if (snd_ctl_open(&ctl, name, SND_CTL_NONBLOCK) < 0)
{
ret = snd_card_next(&card);
continue;
}
if (snd_ctl_card_info(ctl, info) < 0)
{
snd_ctl_close(ctl);
ret = snd_card_next(&card);
continue;
}
snd_ctl_close(ctl);
if (++count >= MAX_AUDIO_DEV_NUMS)
{
break;
}
ret = snd_card_next(&card);
}
return count > MAX_AUDIO_DEV_NUMS ? MAX_AUDIO_DEV_NUMS : count;
}
void CLAudioCapture::listDevice()
{
int card = -1, count = 0, ret;
snd_ctl_card_info_t * info;
printf("\r\nAvailable audio capture device : \r\n\r\n");
snd_ctl_card_info_alloca(&info);
ret = snd_card_next(&card);
while (ret == 0 && card != -1)
{
char name[16];
snd_ctl_t * ctl;
snprintf(name, sizeof(name), "hw:%d", card);
if (snd_ctl_open(&ctl, name, SND_CTL_NONBLOCK) < 0)
{
ret = snd_card_next(&card);
continue;
}
if (snd_ctl_card_info(ctl, info) < 0)
{
snd_ctl_close(ctl);
ret = snd_card_next(&card);
continue;
}
printf("index : %d, name : %s\r\n", count, snd_ctl_card_info_get_name(info));
snd_ctl_close(ctl);
if (++count >= MAX_AUDIO_DEV_NUMS)
{
break;
}
ret = snd_card_next(&card);
}
}
int CLAudioCapture::getDeviceIndex(const char * name)
{
int card = -1, count = 0, index = 0, ret;
snd_ctl_card_info_t * info;
snd_ctl_card_info_alloca(&info);
ret = snd_card_next(&card);
while (ret == 0 && card != -1)
{
char cname[16];
snd_ctl_t * ctl;
snprintf(cname, sizeof(cname), "hw:%d", card);
if (snd_ctl_open(&ctl, cname, SND_CTL_NONBLOCK) < 0)
{
ret = snd_card_next(&card);
continue;
}
if (snd_ctl_card_info(ctl, info) < 0)
{
snd_ctl_close(ctl);
ret = snd_card_next(&card);
continue;
}
if (strcasecmp(snd_ctl_card_info_get_name(info), name) == 0)
{
index = count;
snd_ctl_close(ctl);
break;
}
snd_ctl_close(ctl);
if (++count >= MAX_AUDIO_DEV_NUMS)
{
break;
}
ret = snd_card_next(&card);
}
return index;
}
BOOL CLAudioCapture::getDeviceName(int index, char * name, int namesize)
{
BOOL res = FALSE;
int card = -1, count = 0, ret;
snd_ctl_card_info_t * info;
snd_ctl_card_info_alloca(&info);
ret = snd_card_next(&card);
while (ret == 0 && card != -1)
{
char cname[16];
snd_ctl_t * ctl;
snprintf(cname, sizeof(cname), "hw:%d", card);
if (snd_ctl_open(&ctl, cname, SND_CTL_NONBLOCK) < 0)
{
ret = snd_card_next(&card);
continue;
}
if (snd_ctl_card_info(ctl, info) < 0)
{
snd_ctl_close(ctl);
ret = snd_card_next(&card);
continue;
}
if (index == count)
{
res = TRUE;
strncpy(name, snd_ctl_card_info_get_name(info), namesize);
snd_ctl_close(ctl);
break;
}
snd_ctl_close(ctl);
if (++count >= MAX_AUDIO_DEV_NUMS)
{
break;
}
ret = snd_card_next(&card);
}
return res;
}
BOOL CLAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
{
CLock lock(m_pMutex);
if (m_bInited)
{
return TRUE;
}
if (alsa_get_device_name(m_nDevIndex, m_alsa.devname, sizeof(m_alsa.devname)) == FALSE)
{
log_print(HT_LOG_ERR, "get device name failed. %d\r\n", m_nDevIndex);
return FALSE;
}
if (alsa_open_device(&m_alsa, SND_PCM_STREAM_CAPTURE, (uint32 *)&samplerate, 2) == FALSE)
{
log_print(HT_LOG_ERR, "open device (%s) failed\r\n", m_alsa.devname);
return FALSE;
}
m_pData = (uint8 *)malloc(ALSA_BUFFER_SIZE_MAX);
if (NULL == m_pData)
{
return FALSE;
}
memset(m_pData, 0, ALSA_BUFFER_SIZE_MAX);
AudioEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcChannels = 2;
params.SrcSamplefmt = AV_SAMPLE_FMT_S16;
params.SrcSamplerate = samplerate;
params.DstChannels = channels;
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
params.DstSamplerate = samplerate;
params.DstBitrate = bitrate;
params.DstCodec = codec;
if (m_encoder.init(&params) == FALSE)
{
return FALSE;
}
m_nChannels = channels;
m_nSampleRate = samplerate;
m_nBitrate = bitrate;
m_bInited = TRUE;
return TRUE;
}
BOOL CLAudioCapture::capture()
{
int res = 0;
if (NULL == m_alsa.handler)
{
return FALSE;
}
while (m_bCapture)
{
res = snd_pcm_wait(m_alsa.handler, 100);
if (res < 0)
{
if (alsa_xrun_recover(m_alsa.handler, res) < 0)
{
return FALSE;
}
}
res = snd_pcm_readi(m_alsa.handler, m_pData, m_alsa.period_size);
if (res == -EAGAIN)
{
continue;
}
else if (res < 0)
{
if (alsa_xrun_recover(m_alsa.handler, res) < 0)
{
return FALSE;
}
}
else if (res > 0)
{
break;
}
}
return m_encoder.encode(m_pData, res * m_alsa.framesize);
}
void CLAudioCapture::captureThread()
{
while (m_bCapture)
{
if (capture())
{
}
else
{
usleep(10*1000);
}
}
m_hCapture = 0;
log_print(HT_LOG_INFO, "%s, exit\r\n", __FUNCTION__);
}
BOOL CLAudioCapture::startCapture()
{
CLock lock(m_pMutex);
if (!m_bInited)
{
return FALSE;
}
if (m_bCapture)
{
return TRUE;
}
m_bCapture = TRUE;
m_hCapture = sys_os_create_thread((void *)audioCaptureThread, this);
return TRUE;
}
void CLAudioCapture::stopCapture(void)
{
CLock lock(m_pMutex);
m_bCapture = FALSE;
while (m_hCapture)
{
usleep(10*1000);
}
alsa_close_device(&m_alsa);
if (m_pData)
{
free(m_pData);
m_pData = NULL;
}
m_bInited = FALSE;
}

View File

@@ -0,0 +1,68 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _AUDIO_CAPTURE_LINUX_H
#define _AUDIO_CAPTURE_LINUX_H
/***************************************************************************************/
#include "audio_capture.h"
#include <alsa/asoundlib.h>
#include "alsa.h"
/***************************************************************************************/
class CLAudioCapture : public CAudioCapture
{
public:
// get audio capture devcie nubmers
static int getDeviceNums();
// list avaivalbe audio capture device
static void listDevice();
// get device index by device name
static int getDeviceIndex(const char * name);
// get device name by device index
static BOOL getDeviceName(int index, char * name, int namesize);
// get single instance
static CAudioCapture * getInstance(int devid);
BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
BOOL startCapture();
void captureThread();
private:
CLAudioCapture();
CLAudioCapture(CLAudioCapture &obj);
~CLAudioCapture();
void stopCapture();
BOOL capture();
private:
ALSACTX m_alsa;
uint8 * m_pData;
};
#endif // _AUDIO_CAPTURE_LINUX_H

View File

@@ -0,0 +1,275 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_capture_mac.h"
#include "lock.h"
/***************************************************************************************/
static void avfAudioCallback(avf_audio_data * data, void * userdata)
{
CMAudioCapture *capture = (CMAudioCapture *)userdata;
capture->audioCallBack(data);
}
static void * audioCaptureThread(void * argv)
{
CMAudioCapture *capture = (CMAudioCapture *)argv;
capture->captureThread();
return NULL;
}
/***************************************************************************************/
CMAudioCapture::CMAudioCapture() : CAudioCapture()
{
m_pCapture = NULL;
}
CMAudioCapture::~CMAudioCapture()
{
stopCapture();
}
CAudioCapture * CMAudioCapture::getInstance(int devid)
{
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
{
return NULL;
}
sys_os_mutex_enter(m_pInstMutex);
if (NULL == m_pInstance[devid])
{
m_pInstance[devid] = new CMAudioCapture;
if (m_pInstance[devid])
{
m_pInstance[devid]->m_nRefCnt++;
m_pInstance[devid]->m_nDevIndex = devid;
}
}
else
{
m_pInstance[devid]->m_nRefCnt++;
}
sys_os_mutex_leave(m_pInstMutex);
return m_pInstance[devid];
}
int CMAudioCapture::getDeviceNums()
{
int count = avf_audio_device_nums();
return count > MAX_AUDIO_DEV_NUMS ? MAX_AUDIO_DEV_NUMS : count;
}
void CMAudioCapture::listDevice()
{
avf_audio_device_list();
}
int CMAudioCapture::getDeviceIndex(const char * name)
{
int index = avf_audio_device_get_index(name);
return index > MAX_AUDIO_DEV_NUMS ? 0 : index;
}
BOOL CMAudioCapture::getDeviceName(int index, char * name, int namesize)
{
return avf_audio_device_get_name(index, name, namesize);
}
BOOL CMAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
{
CLock lock(m_pMutex);
if (m_bInited)
{
return TRUE;
}
m_pCapture = avf_audio_init(m_nDevIndex, samplerate, 2);
if (NULL == m_pCapture)
{
log_print(HT_LOG_ERR, "%s, avf_audio_init failed\r\n", __FUNCTION__);
return FALSE;
}
avf_audio_set_callback(m_pCapture, avfAudioCallback, this);
AudioEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcChannels = avf_audio_get_channels(m_pCapture);
params.SrcSamplefmt = (AVSampleFormat)avf_audio_get_samplefmt(m_pCapture);
params.SrcSamplerate = avf_audio_get_samplerate(m_pCapture);
params.DstChannels = channels;
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
params.DstSamplerate = samplerate;
params.DstBitrate = bitrate;
params.DstCodec = codec;
if (m_encoder.init(&params) == FALSE)
{
return FALSE;
}
m_nChannels = params.SrcChannels;
m_nSampleRate = params.SrcSamplerate;
m_nBitrate = bitrate;
m_bInited = TRUE;
return TRUE;
}
void CMAudioCapture::audioCallBack(avf_audio_data * data)
{
int i;
AVFrame frame;
memset(&frame, 0, sizeof(frame));
for (i = 0; i < 4; i++)
{
frame.data[i] = data->data[i];
frame.linesize[i] = data->linesize[i];
}
frame.extended_data = frame.data;
frame.sample_rate = data->samplerate;
frame.channels = data->channels;
frame.format = data->format;
frame.nb_samples = data->samples;
frame.channel_layout = av_get_default_channel_layout(data->channels);
frame.key_frame = 1;
m_encoder.encode(&frame);
}
BOOL CMAudioCapture::capture(int *samples)
{
if (!m_bInited)
{
return FALSE;
}
BOOL ret = avf_audio_read(m_pCapture, samples);
return ret;
}
void CMAudioCapture::captureThread()
{
int samples = 0;
int samplerate = m_nSampleRate;
int64 cur_delay = 0;
int64 pre_delay = 0;
uint32 cur_time = 0;
uint32 pre_time = 0;
while (m_bCapture)
{
if (capture(&samples))
{
cur_time = sys_os_get_ms();
cur_delay = 1000000.0 / samplerate * samples;
if (pre_time > 0)
{
cur_delay += pre_delay - (cur_time - pre_time) * 1000;
if (cur_delay < 1000)
{
cur_delay = 0;
}
}
pre_time = cur_time;
pre_delay = cur_delay;
if (cur_delay > 0)
{
usleep(cur_delay);
}
}
else
{
usleep(10*1000);
}
}
m_hCapture = 0;
log_print(HT_LOG_INFO, "%s, exit\r\n", __FUNCTION__);
}
BOOL CMAudioCapture::startCapture()
{
CLock lock(m_pMutex);
if (!m_bInited)
{
return FALSE;
}
if (m_bCapture)
{
return TRUE;
}
// m_bCapture = TRUE;
// m_hCapture = sys_os_create_thread((void *)audioCaptureThread, this);
return TRUE;
}
void CMAudioCapture::stopCapture(void)
{
CLock lock(m_pMutex);
m_bCapture = FALSE;
while (m_hCapture)
{
usleep(10*1000);
}
if (m_pCapture)
{
avf_audio_uninit(m_pCapture);
m_pCapture = NULL;
}
m_bInited = FALSE;
}

View File

@@ -0,0 +1,67 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_CAPTURE_MAC_H
#define AUDIO_CAPTURE_MAC_H
/***************************************************************************************/
#include "audio_capture.h"
#include "audio_capture_avf.h"
/***************************************************************************************/
class CMAudioCapture : public CAudioCapture
{
public:
// get audio capture devcie nubmers
static int getDeviceNums();
// list avaivalbe video capture device
static void listDevice();
// get device index by device name
static int getDeviceIndex(const char * name);
// get device name by device index
static BOOL getDeviceName(int index, char * name, int namesize);
// get single instance
static CAudioCapture * getInstance(int devid);
BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
BOOL startCapture();
void captureThread();
void audioCallBack(avf_audio_data * data);
private:
CMAudioCapture();
CMAudioCapture(CMAudioCapture &obj);
~CMAudioCapture();
void stopCapture();
BOOL capture(int *samples);
private:
void * m_pCapture;
};
#endif

View File

@@ -0,0 +1,627 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_capture_win.h"
#include "lock.h"
#include <Functiondiscoverykeys_devpkey.h>
#include <mmreg.h>
/**************************************************************************************/
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
const IID IID_IMMEndpoint = __uuidof(IMMEndpoint);
/***************************************************************************************/
static void * audioCaptureThread(void * argv)
{
CWAudioCapture *capture = (CWAudioCapture *)argv;
capture->captureThread();
return NULL;
}
/***************************************************************************************/
CWAudioCapture::CWAudioCapture()
: CAudioCapture()
, m_pAudioClient(NULL)
, m_pCaptureClient(NULL)
{
}
CWAudioCapture::~CWAudioCapture()
{
stopCapture();
}
CAudioCapture * CWAudioCapture::getInstance(int devid)
{
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
{
return NULL;
}
sys_os_mutex_enter(m_pInstMutex);
if (NULL == m_pInstance[devid])
{
m_pInstance[devid] = new CWAudioCapture;
if (m_pInstance[devid])
{
m_pInstance[devid]->m_nRefCnt++;
m_pInstance[devid]->m_nDevIndex = devid;
}
}
else
{
m_pInstance[devid]->m_nRefCnt++;
}
sys_os_mutex_leave(m_pInstMutex);
return m_pInstance[devid];
}
int CWAudioCapture::getDeviceNums()
{
UINT count = 0;
HRESULT hr;
IMMDeviceCollection * pEndpoints = NULL;
hr = getDeviceCollection(eAll, &pEndpoints);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, getDeviceCollection failed. hr=%ld", __FUNCTION__, hr);
return 0;
}
hr = pEndpoints->GetCount(&count);
pEndpoints->Release();
return count > MAX_AUDIO_DEV_NUMS ? MAX_AUDIO_DEV_NUMS : count;
}
void CWAudioCapture::listDevice()
{
UINT i, count = 0;
HRESULT hr;
IMMDeviceCollection * pEndpoints = NULL;
printf("\r\nAvailable audio capture device : \r\n\r\n");
hr = getDeviceCollection(eAll, &pEndpoints);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, getDeviceCollection failed. hr=%ld", __FUNCTION__, hr);
return;
}
hr = pEndpoints->GetCount(&count);
for (i = 0; i < count; i++)
{
IMMDevice * pDevice = NULL;
hr = pEndpoints->Item(i, &pDevice);
if (FAILED(hr))
{
log_print(HT_LOG_WARN, "%s, pEndpoints->Item failed. hr=%ld", __FUNCTION__, hr);
continue;
}
PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);
hr = getDeviceName(pDevice, &varName);
if (FAILED(hr))
{
pDevice->Release();
log_print(HT_LOG_WARN, "%s, getDeviceName failed. hr=%ld", __FUNCTION__, hr);
continue;
}
printf("index : %d, name : %ws\r\n", i, varName.pwszVal);
PropVariantClear(&varName);
pDevice->Release();
}
pEndpoints->Release();
}
int CWAudioCapture::getDeviceIndex(const char * name)
{
UINT i, count = 0, index = 0, size;
HRESULT hr;
IMMDeviceCollection * pEndpoints = NULL;
wchar_t * wszname = NULL;
hr = getDeviceCollection(eAll, &pEndpoints);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, getDeviceCollection failed. hr=%ld", __FUNCTION__, hr);
return 0;
}
size = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
wszname = (wchar_t *)malloc(size * sizeof(wchar_t));
if (wszname)
{
MultiByteToWideChar(CP_ACP, 0, name, -1, wszname, size);
}
hr = pEndpoints->GetCount(&count);
for (i = 0; i < count && i < MAX_AUDIO_DEV_NUMS; i++)
{
IMMDevice * pDevice = NULL;
hr = pEndpoints->Item(i, &pDevice);
if (FAILED(hr))
{
log_print(HT_LOG_WARN, "%s, pEndpoints->Item failed. hr=%ld", __FUNCTION__, hr);
continue;
}
PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);
hr = getDeviceName(pDevice, &varName);
if (FAILED(hr))
{
pDevice->Release();
log_print(HT_LOG_WARN, "%s, getDeviceName failed. hr=%ld", __FUNCTION__, hr);
continue;
}
if (wszname != NULL && _wcsicmp(varName.pwszVal, wszname) == 0)
{
index = i;
PropVariantClear(&varName);
pDevice->Release();
break;
}
PropVariantClear(&varName);
pDevice->Release();
}
pEndpoints->Release();
if (wszname)
{
free(wszname);
}
return index;
}
BOOL CWAudioCapture::getDeviceName(int index, char * name, int namesize)
{
UINT count = 0;
HRESULT hr;
IMMDevice * pDevice = NULL;
IMMDeviceCollection * pEndpoints = NULL;
PROPVARIANT varName;
hr = getDeviceCollection(eAll, &pEndpoints);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, getDeviceCollection failed. hr=%ld", __FUNCTION__, hr);
return FALSE;
}
hr = pEndpoints->GetCount(&count);
if (FAILED(hr))
{
pEndpoints->Release();
log_print(HT_LOG_ERR, "%s, GetCount failed. hr=%ld", __FUNCTION__, hr);
return FALSE;
}
if (index < 0 || (UINT)index >= count || index >= MAX_AUDIO_DEV_NUMS)
{
pEndpoints->Release();
log_print(HT_LOG_ERR, "%s, invalid index %d, count=%d\r\n", __FUNCTION__, index, count);
return FALSE;
}
hr = pEndpoints->Item(index, &pDevice);
if (FAILED(hr))
{
pEndpoints->Release();
log_print(HT_LOG_ERR, "%s, pEndpoints->Item failed. hr=%ld", __FUNCTION__, hr);
return FALSE;
}
pEndpoints->Release();
// Initialize container for property value.
PropVariantInit(&varName);
hr = getDeviceName(pDevice, &varName);
if (FAILED(hr))
{
pDevice->Release();
log_print(HT_LOG_WARN, "%s, getDeviceName failed. hr=%ld", __FUNCTION__, hr);
return FALSE;
}
WideCharToMultiByte(CP_ACP, 0, varName.pwszVal, -1, name, namesize, NULL, NULL);
PropVariantClear(&varName);
pDevice->Release();
return TRUE;
}
BOOL CWAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
{
CLock lock(m_pMutex);
if (m_bInited)
{
return TRUE;
}
int samplefmt;
DWORD flags = 0;
HRESULT hr;
IMMDevice * pDevice = NULL;
WAVEFORMATEX * pwfx = NULL;
hr = getDeviceByIndex(m_nDevIndex, &pDevice);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, getDeviceByIndex failed. index=%d, hr=%ld",
__FUNCTION__, m_nDevIndex, hr);
return FALSE;
}
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, pDevice->Activate failed. hr=%ld", __FUNCTION__, hr);
goto done;
}
hr = m_pAudioClient->GetMixFormat(&pwfx);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, m_pAudioClient->GetMixFormat failed. hr=%ld", __FUNCTION__, hr);
goto done;
}
samplefmt = getSampleFmt(pwfx);
if (samplefmt == AV_SAMPLE_FMT_NONE)
{
log_print(HT_LOG_ERR, "%s, getSampleFmt failed", __FUNCTION__);
goto done;
}
if (getDataFlow(pDevice) == eRender)
{
flags = AUDCLNT_STREAMFLAGS_LOOPBACK;
}
hr = m_pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED,
flags,
1000000, // 100ms
0,
pwfx,
NULL);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, m_pAudioClient->Initialize failed. hr=%ld", __FUNCTION__, hr);
goto done;
}
hr = m_pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&m_pCaptureClient);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, m_pAudioClient->GetService failed. hr=%ld", __FUNCTION__, hr);
goto done;
}
AudioEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcChannels = pwfx->nChannels;
params.SrcSamplefmt = (AVSampleFormat) samplefmt;
params.SrcSamplerate = pwfx->nSamplesPerSec;
params.DstChannels = channels;
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
params.DstSamplerate = samplerate;
params.DstBitrate = bitrate;
params.DstCodec = codec;
if (m_encoder.init(&params) == FALSE)
{
log_print(HT_LOG_ERR, "%s, init encoder failed", __FUNCTION__);
goto done;
}
m_nChannels = channels;
m_nSampleRate = samplerate;
m_nBitrate = bitrate;
m_nSampleSize = pwfx->wBitsPerSample * pwfx->nChannels;
m_bInited = TRUE;
done:
if (pDevice)
{
pDevice->Release();
}
if (pwfx)
{
CoTaskMemFree(pwfx);
}
if (!m_bInited)
{
if (m_pAudioClient)
{
m_pAudioClient->Release();
m_pAudioClient = NULL;
}
if (m_pCaptureClient)
{
m_pCaptureClient->Release();
m_pCaptureClient = NULL;
}
}
return m_bInited;
}
BOOL CWAudioCapture::startCapture()
{
CLock lock(m_pMutex);
if (!m_bInited)
{
return FALSE;
}
if (m_bCapture)
{
return TRUE;
}
HRESULT hr = m_pAudioClient->Start();
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, m_pAudioClient->Start failed. hr=%ld", __FUNCTION__, hr);
return FALSE;
}
m_bCapture = TRUE;
m_hCapture = sys_os_create_thread((void *)audioCaptureThread, this);
return (m_hCapture ? TRUE : FALSE);
}
void CWAudioCapture::stopCapture(void)
{
CLock lock(m_pMutex);
m_bCapture = FALSE;
while (m_hCapture)
{
usleep(10*1000);
}
if (m_pAudioClient)
{
m_pAudioClient->Stop();
m_pAudioClient->Release();
m_pAudioClient = NULL;
}
if (m_pCaptureClient)
{
m_pCaptureClient->Release();
m_pCaptureClient = NULL;
}
m_bInited = FALSE;
}
BOOL CWAudioCapture::capture()
{
HRESULT hr;
UINT32 packetLength = 0;
hr = m_pCaptureClient->GetNextPacketSize(&packetLength);
if (FAILED(hr) || packetLength == 0)
{
return FALSE;
}
BYTE * data;
DWORD flags;
UINT32 size;
hr = m_pCaptureClient->GetBuffer(&data, &size, &flags, NULL, NULL);
if (FAILED(hr))
{
return FALSE;
}
m_encoder.encode(data, size * (m_nSampleSize / 8));
hr = m_pCaptureClient->ReleaseBuffer(size);
return TRUE;
}
void CWAudioCapture::captureThread()
{
while (m_bCapture)
{
capture();
usleep(10*1000);
}
m_hCapture = 0;
}
HRESULT CWAudioCapture::getDeviceCollection(EDataFlow dataFlow, IMMDeviceCollection ** ppEndpoints)
{
HRESULT hr;
IMMDeviceEnumerator * pEnumerator = NULL;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
if (FAILED(hr))
{
return hr;
}
hr = pEnumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATE_ACTIVE, ppEndpoints);
pEnumerator->Release();
return hr;
}
HRESULT CWAudioCapture::getDeviceName(IMMDevice * pDevice, PROPVARIANT * pName)
{
HRESULT hr;
IPropertyStore * pProps = NULL;
hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
if (FAILED(hr))
{
return hr;
}
// Get the endpoint's friendly-name property.
hr = pProps->GetValue(PKEY_Device_FriendlyName, pName);
pProps->Release();
return hr;
}
HRESULT CWAudioCapture::getDeviceByIndex(int index, IMMDevice ** ppDevice)
{
UINT count = 0;
HRESULT hr;
IMMDeviceCollection * pEndpoints = NULL;
hr = getDeviceCollection(eAll, &pEndpoints);
if (FAILED(hr))
{
return hr;
}
hr = pEndpoints->GetCount(&count);
if ((UINT) index >= count)
{
pEndpoints->Release();
return E_INVALIDARG;
}
hr = pEndpoints->Item(index, ppDevice);
pEndpoints->Release();
return hr;
}
int CWAudioCapture::getSampleFmt(WAVEFORMATEX * pwfx)
{
int fmt = AV_SAMPLE_FMT_NONE;
if ((pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (pwfx->wBitsPerSample == 32))
{
fmt = AV_SAMPLE_FMT_FLT;
}
else if ((pwfx->wFormatTag == WAVE_FORMAT_PCM) && (pwfx->wBitsPerSample == 16))
{
fmt = AV_SAMPLE_FMT_S16;
}
else if ((pwfx->wFormatTag == WAVE_FORMAT_PCM) && (pwfx->wBitsPerSample == 32))
{
fmt = AV_SAMPLE_FMT_S32;
}
else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
{
const WAVEFORMATEXTENSIBLE * ext = (const WAVEFORMATEXTENSIBLE *) pwfx;
if (IsEqualGUID(ext->SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) && (pwfx->wBitsPerSample == 32))
{
fmt = AV_SAMPLE_FMT_FLT;
}
else if (IsEqualGUID(ext->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) && (pwfx->wBitsPerSample == 16))
{
fmt = AV_SAMPLE_FMT_S16;
}
else if (IsEqualGUID(ext->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) && (pwfx->wBitsPerSample == 32))
{
fmt = AV_SAMPLE_FMT_S32;
}
}
return fmt;
}
int CWAudioCapture::getDataFlow(IMMDevice * pDevice)
{
HRESULT hr;
EDataFlow dataflow = eCapture;
IMMEndpoint * pEndpoint;
hr = pDevice->QueryInterface(IID_IMMEndpoint, (void **)&pEndpoint);
if (FAILED(hr))
{
return dataflow;
}
hr = pEndpoint->GetDataFlow(&dataflow);
pEndpoint->Release();
return dataflow;
}

View File

@@ -0,0 +1,76 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_CAPTURE_WIN_H
#define AUDIO_CAPTURE_WIN_H
/***************************************************************************************/
#include "media_format.h"
#include "audio_capture.h"
#include <mmdeviceapi.h>
#include <audioclient.h>
/***************************************************************************************/
class CWAudioCapture : public CAudioCapture
{
public:
// get audio capture devcie nubmers
static int getDeviceNums();
// list avaivalbe audio capture device
static void listDevice();
// get device index by device name
static int getDeviceIndex(const char * name);
// get device name by device index
static BOOL getDeviceName(int index, char * name, int namesize);
// get single instance
static CAudioCapture * getInstance(int devid);
BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
BOOL startCapture();
void captureThread();
private:
CWAudioCapture();
CWAudioCapture(CWAudioCapture &obj);
~CWAudioCapture();
void stopCapture();
BOOL capture();
static HRESULT getDeviceCollection(EDataFlow dataFlow, IMMDeviceCollection ** ppEndpoints);
static HRESULT getDeviceName(IMMDevice * pDevice, PROPVARIANT * pName);
HRESULT getDeviceByIndex(int index, IMMDevice ** ppDevice);
int getSampleFmt(WAVEFORMATEX * pwfx);
int getDataFlow(IMMDevice * pDevice);
private:
IAudioClient * m_pAudioClient;
IAudioCaptureClient * m_pCaptureClient;
};
#endif

View File

@@ -0,0 +1,246 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_decoder.h"
#include "avcodec_mutex.h"
#include "media_codec.h"
CAudioDecoder::CAudioDecoder()
{
m_bInited = FALSE;
m_nSamplerate = 0;
m_nChannels = 0;
m_pCodec = NULL;
m_pContext = NULL;
m_pFrame = NULL;
m_pResampleFrame = NULL;
m_pSwrCtx = NULL;
m_nDstFormat = AV_SAMPLE_FMT_S16;
m_pCallback = NULL;
m_pUserdata = NULL;
}
CAudioDecoder::~CAudioDecoder()
{
uninit();
}
BOOL CAudioDecoder::init(enum AVCodecID codec, int samplerate, int channels, int bitpersample)
{
m_pCodec = avcodec_find_decoder(codec);
if (m_pCodec == NULL)
{
log_print(HT_LOG_ERR, "%s, m_pCodec is NULL\r\n", __FUNCTION__);
return FALSE;
}
m_pContext = avcodec_alloc_context3(m_pCodec);
if (NULL == m_pContext)
{
log_print(HT_LOG_ERR, "%s, avcodec_alloc_context3 failed\r\n", __FUNCTION__);
return FALSE;
}
m_pContext->sample_rate = samplerate;
m_pContext->channels = channels;
m_pContext->channel_layout = av_get_default_channel_layout(channels);
if (AV_CODEC_ID_ADPCM_G726 == codec)
{
if (0 == bitpersample)
{
bitpersample = 2;
}
m_pContext->bits_per_coded_sample = bitpersample;
m_pContext->bit_rate = bitpersample * 8000;
}
if (avcodec_thread_open(m_pContext, m_pCodec, NULL) < 0)
{
log_print(HT_LOG_ERR, "%s, avcodec_thread_open failed\r\n", __FUNCTION__);
return FALSE;
}
if (m_pContext->sample_fmt != m_nDstFormat)
{
m_pSwrCtx = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(channels), m_nDstFormat, samplerate,
av_get_default_channel_layout(channels), m_pContext->sample_fmt, samplerate, 0, NULL);
swr_init(m_pSwrCtx);
}
m_pFrame = av_frame_alloc();
if (NULL == m_pFrame)
{
log_print(HT_LOG_ERR, "%s, av_frame_alloc failed\r\n", __FUNCTION__);
return FALSE;
}
m_nSamplerate = samplerate;
m_nChannels = channels;
m_bInited = TRUE;
return TRUE;
}
BOOL CAudioDecoder::init(int codec, int samplerate, int channels, int bitpersample)
{
return init(to_audio_avcodecid(codec), samplerate, channels, bitpersample);
}
BOOL CAudioDecoder::decode(uint8 * data, int len, int64_t pts)
{
AVPacket packet;
av_init_packet(&packet);
packet.data = data;
packet.size = len;
packet.pts = packet.dts = pts;
return decode(&packet);
}
BOOL CAudioDecoder::decode(AVPacket *pkt)
{
int ret;
if (!m_bInited)
{
return FALSE;
}
/* send the packet with the compressed data to the decoder */
ret = avcodec_send_packet(m_pContext, pkt);
if (ret < 0)
{
log_print(HT_LOG_ERR, "%s, error submitting the packet to the decoder\r\n", __FUNCTION__);
return FALSE;
}
/* read all the output frames (in general there may be any number of them */
while (ret >= 0)
{
ret = avcodec_receive_frame(m_pContext, m_pFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
return TRUE;
}
else if (ret < 0)
{
log_print(HT_LOG_ERR, "%s, error during decoding\r\n", __FUNCTION__);
return FALSE;
}
render(m_pFrame);
av_frame_unref(m_pFrame);
}
return TRUE;
}
void CAudioDecoder::flush()
{
if (NULL == m_pContext ||
NULL == m_pContext->codec ||
!(m_pContext->codec->capabilities | AV_CODEC_CAP_DELAY))
{
return;
}
decode(NULL);
}
void CAudioDecoder::uninit()
{
flush();
if (m_pContext)
{
avcodec_thread_close(m_pContext);
avcodec_free_context(&m_pContext);
}
if (m_pFrame)
{
av_frame_free(&m_pFrame);
}
if (m_pResampleFrame)
{
av_frame_free(&m_pResampleFrame);
}
if (m_pSwrCtx)
{
swr_free(&m_pSwrCtx);
}
m_bInited = FALSE;
}
void CAudioDecoder::render(AVFrame * frame)
{
if (NULL == frame)
{
return;
}
if (m_pSwrCtx)
{
if (NULL == m_pResampleFrame)
{
m_pResampleFrame = av_frame_alloc();
if (NULL == m_pResampleFrame)
{
return;
}
}
m_pResampleFrame->sample_rate = m_nSamplerate;
m_pResampleFrame->format = m_nDstFormat;
m_pResampleFrame->channels = m_nChannels;
m_pResampleFrame->channel_layout = av_get_default_channel_layout(m_nChannels);
int swrret = swr_convert_frame(m_pSwrCtx, m_pResampleFrame, frame);
if (swrret == 0)
{
if (m_pCallback)
{
m_pResampleFrame->pts = frame->pts;
m_pResampleFrame->pkt_dts = frame->pkt_dts;
m_pCallback(m_pResampleFrame, m_pUserdata);
}
}
av_frame_unref(m_pResampleFrame);
}
else if (m_pCallback)
{
m_pCallback(frame, m_pUserdata);
}
}

View File

@@ -0,0 +1,73 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_DECODER_H
#define AUDIO_DECODER_H
#include "sys_inc.h"
#include "media_format.h"
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include <libswresample/swresample.h>
}
typedef void (*ADCB)(AVFrame * frame, void * userdata);
class CAudioDecoder
{
public:
CAudioDecoder();
~CAudioDecoder();
BOOL init(int codec, int samplerate, int channels, int bitpersample = 0);
BOOL init(enum AVCodecID codec, int samplerate, int channels, int bitpersample = 0);
void uninit();
BOOL decode(uint8 * data, int len, int64_t pts = AV_NOPTS_VALUE);
BOOL decode(AVPacket *pkt);
void setCallback(ADCB pCallback, void * pUserdata) {m_pCallback = pCallback; m_pUserdata = pUserdata;}
private:
void flush();
void render(AVFrame * frame);
private:
BOOL m_bInited;
int m_nSamplerate;
int m_nChannels;
const AVCodec * m_pCodec;
AVCodecContext* m_pContext;
AVFrame * m_pFrame;
AVFrame * m_pResampleFrame;
SwrContext * m_pSwrCtx;
AVSampleFormat m_nDstFormat;
ADCB m_pCallback;
void * m_pUserdata;
};
#endif

View File

@@ -0,0 +1,577 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_encoder.h"
#include "media_format.h"
#include "avcodec_mutex.h"
#include "media_codec.h"
CAudioEncoder::CAudioEncoder()
{
m_nCodecId = AV_CODEC_ID_NONE;
m_bInited = FALSE;
memset(&m_EncoderParams, 0, sizeof(AudioEncoderParam));
m_pCodecCtx = NULL;
m_pFrame = NULL;
m_pResampleFrame = NULL;
m_pSwrCtx = NULL;
m_pPkt = NULL;
memset(&m_AudioBuffer, 0, sizeof(AudioBuffer));
m_pCallbackMutex = sys_os_create_mutex();
m_pCallbackList = h_list_create(FALSE);
}
CAudioEncoder::~CAudioEncoder()
{
uninit();
h_list_free_container(m_pCallbackList);
sys_os_destroy_sig_mutex(m_pCallbackMutex);
}
int CAudioEncoder::computeBitrate(AVCodecID codec, int samplerate, int channels, int quality)
{
int bitrate;
if (m_nCodecId == AV_CODEC_ID_ADPCM_G726)
{
bitrate = 16000; // G726 16kbit/s
}
else if (m_nCodecId == AV_CODEC_ID_ADPCM_G722)
{
bitrate = 64000; // G722 64kbit/s
}
else if (m_nCodecId == AV_CODEC_ID_PCM_ALAW || m_nCodecId == AV_CODEC_ID_PCM_MULAW)
{
bitrate = samplerate * channels * 8;
}
else if (m_nCodecId == AV_CODEC_ID_AAC)
{
bitrate = samplerate * channels * 16 / 7;
}
else
{
bitrate = samplerate * channels;
}
return bitrate;
}
BOOL CAudioEncoder::init(AudioEncoderParam * params)
{
memcpy(&m_EncoderParams, params, sizeof(AudioEncoderParam));
m_nCodecId = to_audio_avcodecid(params->DstCodec);
if (AV_CODEC_ID_AAC == m_nCodecId)
{
// the ffmepg AAC encoder only support AV_SAMPLE_FMT_FLTP
m_EncoderParams.DstSamplefmt = AV_SAMPLE_FMT_FLTP;
}
const AVCodec * pCodec = avcodec_find_encoder(m_nCodecId);
if (pCodec == NULL)
{
log_print(HT_LOG_ERR, "avcodec_find_encoder failed, %d\r\n", m_nCodecId);
return FALSE;
}
m_pCodecCtx = avcodec_alloc_context3(pCodec);
if (m_pCodecCtx == NULL)
{
log_print(HT_LOG_ERR, "avcodec_alloc_context3 failed\r\n");
return FALSE;
}
m_pCodecCtx->codec_id = m_nCodecId;
m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
m_pCodecCtx->qblur = 0.5f;
m_pCodecCtx->time_base.num = 1;
m_pCodecCtx->time_base.den = m_EncoderParams.DstSamplerate;
m_pCodecCtx->sample_rate = m_EncoderParams.DstSamplerate;
m_pCodecCtx->channels = m_EncoderParams.DstChannels;
m_pCodecCtx->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
m_pCodecCtx->sample_fmt = m_EncoderParams.DstSamplefmt;
if (m_EncoderParams.DstBitrate > 0)
{
m_pCodecCtx->bit_rate = m_EncoderParams.DstBitrate * 1000;
}
else
{
m_pCodecCtx->bit_rate = computeBitrate(m_nCodecId, m_EncoderParams.DstSamplerate, m_EncoderParams.DstChannels, 80);
}
m_pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
av_opt_set(m_pCodecCtx->priv_data, "preset", "superfast", 0);
av_opt_set(m_pCodecCtx->priv_data, "tune", "zerolatency", 0);
if (AV_CODEC_ID_AAC == m_nCodecId)
{
m_pCodecCtx->profile = FF_PROFILE_AAC_LOW; // AAC -LC
}
else if (AV_CODEC_ID_ADPCM_G726 == m_nCodecId)
{
m_pCodecCtx->bits_per_coded_sample = m_pCodecCtx->bit_rate / 8000;
m_pCodecCtx->bit_rate = m_pCodecCtx->bits_per_coded_sample * 8000;
}
if (avcodec_thread_open(m_pCodecCtx, pCodec, NULL) < 0)
{
log_print(HT_LOG_ERR, "avcodec_thread_open failed, audio encoder\r\n");
return FALSE;
}
m_pFrame = av_frame_alloc();
if (NULL == m_pFrame)
{
return FALSE;
}
if (m_EncoderParams.SrcSamplerate != m_EncoderParams.DstSamplerate ||
m_EncoderParams.SrcChannels != m_EncoderParams.DstChannels ||
m_EncoderParams.SrcSamplefmt != m_EncoderParams.DstSamplefmt)
{
m_pSwrCtx = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(m_EncoderParams.DstChannels), m_EncoderParams.DstSamplefmt, m_EncoderParams.DstSamplerate,
av_get_default_channel_layout(m_EncoderParams.SrcChannels), m_EncoderParams.SrcSamplefmt, m_EncoderParams.SrcSamplerate, 0, NULL);
swr_init(m_pSwrCtx);
}
if (m_pCodecCtx->frame_size == 0)
{
m_pCodecCtx->frame_size = 1024;
}
m_AudioBuffer.tlen = 64 * m_pCodecCtx->frame_size * m_EncoderParams.DstChannels * av_get_bytes_per_sample(m_EncoderParams.DstSamplefmt);
m_AudioBuffer.data[0] = (uint8 *)av_malloc(m_AudioBuffer.tlen);
m_AudioBuffer.data[1] = (uint8 *)av_malloc(m_AudioBuffer.tlen);
m_AudioBuffer.size[0] = 0;
m_AudioBuffer.size[1] = 0;
m_AudioBuffer.samples = 0;
/* packet for holding encoded output */
m_pPkt = av_packet_alloc();
if (!m_pPkt)
{
log_print(HT_LOG_ERR, "could not allocate the packet\r\n");
return FALSE;
}
m_bInited = TRUE;
return TRUE;
}
void CAudioEncoder::uninit()
{
flush();
if (m_pCodecCtx)
{
avcodec_thread_close(m_pCodecCtx);
avcodec_free_context(&m_pCodecCtx);
}
if (m_pFrame)
{
av_frame_free(&m_pFrame);
}
if (m_pResampleFrame)
{
av_frame_free(&m_pResampleFrame);
}
if (m_pSwrCtx)
{
swr_free(&m_pSwrCtx);
}
if (m_pPkt)
{
av_packet_free(&m_pPkt);
}
if (m_AudioBuffer.data[0])
{
av_freep(&m_AudioBuffer.data[0]);
}
if (m_AudioBuffer.data[1])
{
av_freep(&m_AudioBuffer.data[1]);
}
m_bInited = FALSE;
}
BOOL CAudioEncoder::bufferFrame(AVFrame * pFrame)
{
BOOL ret = TRUE;
int samplesize = av_get_bytes_per_sample((AVSampleFormat)pFrame->format);
int size = pFrame->nb_samples * samplesize;
assert(m_AudioBuffer.size[0] + size <= m_AudioBuffer.tlen);
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
{
memcpy(m_AudioBuffer.data[0]+m_AudioBuffer.size[0], pFrame->data[0], size);
m_AudioBuffer.size[0] += size;
memcpy(m_AudioBuffer.data[1]+m_AudioBuffer.size[1], pFrame->data[1], size);
m_AudioBuffer.size[1] += size;
}
else
{
memcpy(m_AudioBuffer.data[0]+m_AudioBuffer.size[0], pFrame->data[0], size * m_EncoderParams.DstChannels);
m_AudioBuffer.size[0] += size * m_EncoderParams.DstChannels;
}
m_AudioBuffer.samples += pFrame->nb_samples;
while (m_AudioBuffer.samples >= m_pCodecCtx->frame_size)
{
int linesize;
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
{
linesize = samplesize * m_pCodecCtx->frame_size;
}
else
{
linesize = samplesize * m_pCodecCtx->frame_size * m_EncoderParams.DstChannels;
}
m_pFrame->data[0] = m_AudioBuffer.data[0];
m_pFrame->data[1] = m_AudioBuffer.data[1];
m_pFrame->linesize[0] = linesize;
m_pFrame->linesize[1] = linesize;
m_pFrame->nb_samples = m_pCodecCtx->frame_size;
m_pFrame->format = m_EncoderParams.DstSamplefmt;
m_pFrame->key_frame = 1;
m_pFrame->sample_rate = m_EncoderParams.DstSamplerate;
m_pFrame->channels = m_EncoderParams.DstChannels;
m_pFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
ret = encodeInternal(m_pFrame);
m_AudioBuffer.size[0] -= linesize;
if (m_AudioBuffer.size[0] > 0)
{
memmove(m_AudioBuffer.data[0], m_AudioBuffer.data[0]+linesize, m_AudioBuffer.size[0]);
}
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
{
m_AudioBuffer.size[1] -= linesize;
if (m_AudioBuffer.size[1] > 0)
{
memmove(m_AudioBuffer.data[1], m_AudioBuffer.data[1]+linesize, m_AudioBuffer.size[1]);
}
}
m_AudioBuffer.samples -= m_pCodecCtx->frame_size;
}
return ret;
}
void CAudioEncoder::flush()
{
if (NULL == m_pCodecCtx ||
NULL == m_pCodecCtx->codec ||
!(m_pCodecCtx->codec->capabilities | AV_CODEC_CAP_DELAY))
{
return;
}
encodeInternal(NULL);
}
BOOL CAudioEncoder::encodeInternal(AVFrame * pFrame)
{
int ret;
/* send the frame for encoding */
ret = avcodec_send_frame(m_pCodecCtx, pFrame);
if (ret < 0)
{
log_print(HT_LOG_ERR, "error sending the frame to the encoder\r\n");
return FALSE;
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0)
{
ret = avcodec_receive_packet(m_pCodecCtx, m_pPkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
return TRUE;
}
else if (ret < 0)
{
log_print(HT_LOG_ERR, "error encoding audio frame\r\n");
return FALSE;
}
if (m_pPkt->data && m_pPkt->size > 0)
{
procData(m_pPkt->data, m_pPkt->size, pFrame ? pFrame->nb_samples : 0);
}
else
{
log_print(HT_LOG_WARN, "%s, data is null\r\n", __FUNCTION__);
}
av_packet_unref(m_pPkt);
}
return TRUE;
}
BOOL CAudioEncoder::encode(uint8 * data, int size)
{
if (!m_bInited)
{
return FALSE;
}
m_pFrame->data[0] = data;
m_pFrame->linesize[0] = size;
m_pFrame->nb_samples = size / (m_EncoderParams.SrcChannels * av_get_bytes_per_sample(m_EncoderParams.SrcSamplefmt));
m_pFrame->format = m_EncoderParams.SrcSamplefmt;
m_pFrame->key_frame = 1;
m_pFrame->sample_rate = m_EncoderParams.SrcSamplerate;
m_pFrame->channels = m_EncoderParams.SrcChannels;
m_pFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.SrcChannels);
return encode(m_pFrame);
}
BOOL CAudioEncoder::encode(AVFrame * pFrame)
{
BOOL ret = TRUE;
if (!m_bInited)
{
return FALSE;
}
if (m_pSwrCtx)
{
if (NULL == m_pResampleFrame)
{
m_pResampleFrame = av_frame_alloc();
if (NULL == m_pResampleFrame)
{
return FALSE;
}
}
m_pResampleFrame->sample_rate = m_EncoderParams.DstSamplerate;
m_pResampleFrame->format = m_EncoderParams.DstSamplefmt;
m_pResampleFrame->channels = m_EncoderParams.DstChannels;
m_pResampleFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
int swrret = swr_convert_frame(m_pSwrCtx, m_pResampleFrame, pFrame);
if (swrret == 0)
{
ret = bufferFrame(m_pResampleFrame);
}
else
{
ret = FALSE;
}
av_frame_unref(m_pResampleFrame);
}
else
{
ret = bufferFrame(pFrame);
}
return ret;
}
void CAudioEncoder::procData(uint8 * data, int size, int nbsamples)
{
AudioEncoderCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (AudioEncoderCB *) p_node->p_data;
if (p_cb->pCallback != NULL)
{
p_cb->pCallback(data, size, nbsamples, p_cb->pUserdata);
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
}
BOOL CAudioEncoder::isCallbackExist(AudioDataCallback pCallback, void *pUserdata)
{
BOOL exist = FALSE;
AudioEncoderCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (AudioEncoderCB *) p_node->p_data;
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
{
exist = TRUE;
break;
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
return exist;
}
void CAudioEncoder::addCallback(AudioDataCallback pCallback, void *pUserdata)
{
if (isCallbackExist(pCallback, pUserdata))
{
return;
}
AudioEncoderCB * p_cb = (AudioEncoderCB *) malloc(sizeof(AudioEncoderCB));
if (NULL == p_cb)
{
return;
}
p_cb->pCallback = pCallback;
p_cb->pUserdata = pUserdata;
p_cb->bFirst = TRUE;
sys_os_mutex_enter(m_pCallbackMutex);
h_list_add_at_back(m_pCallbackList, p_cb);
sys_os_mutex_leave(m_pCallbackMutex);
}
void CAudioEncoder::delCallback(AudioDataCallback pCallback, void *pUserdata)
{
AudioEncoderCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (AudioEncoderCB *) p_node->p_data;
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
{
free(p_cb);
h_list_remove(m_pCallbackList, p_node);
break;
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
}
char * CAudioEncoder::getAACAuxSDPLine(int rtp_pt)
{
if (NULL == m_pCodecCtx || m_pCodecCtx->extradata_size == 0)
{
return NULL;
}
char const* fmtpFmt =
"a=fmtp:%d "
"streamtype=5;profile-level-id=1;"
"mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;"
"config=";
uint32 fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max char len */
+ 2*m_pCodecCtx->extradata_size; /* 2*, because each byte prints as 2 chars */
char* fmtp = new char[fmtpFmtSize+1];
memset(fmtp, 0, fmtpFmtSize+1);
sprintf(fmtp, fmtpFmt, rtp_pt);
char* endPtr = &fmtp[strlen(fmtp)];
for (int i = 0; i < m_pCodecCtx->extradata_size; ++i)
{
sprintf(endPtr, "%02X", m_pCodecCtx->extradata[i]);
endPtr += 2;
}
return fmtp;
}
char * CAudioEncoder::getAuxSDPLine(int rtp_pt)
{
if (m_nCodecId == AV_CODEC_ID_AAC)
{
return getAACAuxSDPLine(rtp_pt);
}
return NULL;
}
BOOL CAudioEncoder::getExtraData(uint8 ** extradata, int * extralen)
{
if (m_pCodecCtx && m_pCodecCtx->extradata_size > 0)
{
*extradata = m_pCodecCtx->extradata;
*extralen = m_pCodecCtx->extradata_size;
return TRUE;
}
return FALSE;
}

View File

@@ -0,0 +1,111 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _AUDIO_ENCODER_H
#define _AUDIO_ENCODER_H
#include "linked_list.h"
extern "C"
{
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
typedef void (*AudioDataCallback)(uint8 *data, int size, int nbsamples, void *pUserdata);
typedef struct
{
AudioDataCallback pCallback; // callback function pointer
void * pUserdata; // user data
BOOL bFirst; //
} AudioEncoderCB;
/**
* Some encoders require a fixed frame size
*/
typedef struct
{
int tlen; // buffer total length
uint8 * data[2]; // audio data buffer
int size[2]; // audio data size
int samples; // audio sample numbers
} AudioBuffer;
typedef struct
{
int SrcSamplerate; // source sample rate
int SrcChannels; // source channels
AVSampleFormat SrcSamplefmt; // source sample format, see AVSampleFormat
int DstCodec; // output codec
int DstSamplerate; // output sample rate
int DstChannels; // output channels
int DstBitrate; // output bitrate, unit is bits per second
AVSampleFormat DstSamplefmt; // output sample format, see AVSampleFormat
} AudioEncoderParam;
class CAudioEncoder
{
public:
CAudioEncoder();
~CAudioEncoder();
BOOL init(AudioEncoderParam * params);
void uninit();
BOOL encode(uint8 * data, int size);
BOOL encode(AVFrame * pFrame);
void addCallback(AudioDataCallback pCallback, void *pUserdata);
void delCallback(AudioDataCallback pCallback, void *pUserdata);
char * getAuxSDPLine(int rtp_pt);
BOOL getExtraData(uint8 ** extradata, int * extralen);
private:
void flush();
BOOL encodeInternal(AVFrame * pFrame);
void procData(uint8 * data, int size, int nbsamples);
BOOL isCallbackExist(AudioDataCallback pCallback, void *pUserdata);
char * getAACAuxSDPLine(int rtp_pt);
BOOL bufferFrame(AVFrame * pFrame);
int computeBitrate(AVCodecID codec, int samplerate, int channels, int quality);
private:
AVCodecID m_nCodecId;
BOOL m_bInited;
AudioEncoderParam m_EncoderParams;
AVCodecContext * m_pCodecCtx;
AVFrame * m_pFrame;
AVFrame * m_pResampleFrame;
SwrContext * m_pSwrCtx;
AudioBuffer m_AudioBuffer;
AVPacket * m_pPkt;
void * m_pCallbackMutex;
LINKED_LIST * m_pCallbackList;
};
#endif // _AUDIO_ENCODER_H

View File

@@ -0,0 +1,68 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_play.h"
CAudioPlay::CAudioPlay()
{
m_bInited = FALSE;
m_nSamplerate = 8000;
m_nChannels = 1;
}
CAudioPlay::~CAudioPlay(void)
{
}
BOOL CAudioPlay::startPlay(int samplerate, int channels)
{
m_nSamplerate = samplerate;
m_nChannels = channels;
return FALSE;
}
void CAudioPlay::stopPlay(void)
{
m_bInited = FALSE;
}
BOOL CAudioPlay::setVolume(int volume)
{
return FALSE;
}
int CAudioPlay::getVolume()
{
return 0;
}
void CAudioPlay::playAudio(uint8 * data, int size)
{
if (!m_bInited)
{
return;
}
}

View File

@@ -0,0 +1,53 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _AUDIO_PLAY_H
#define _AUDIO_PLAY_H
#include "sys_inc.h"
#include "media_format.h"
#define HTVOLUME_MIN 0
#define HTVOLUME_MAX 255
class CAudioPlay
{
public:
CAudioPlay();
virtual ~CAudioPlay();
public:
virtual BOOL startPlay(int samplerate, int channels);
virtual void stopPlay();
virtual BOOL setVolume(int volume);
virtual int getVolume();
virtual void playAudio(uint8 * pData, int len);
protected:
BOOL m_bInited;
int m_nSamplerate;
int m_nChannels;
};
#endif

View File

@@ -0,0 +1,41 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_PLAY_AVF_H
#define AUDIO_PLAY_AVF_H
typedef void (*avf_audio_play_callback)(void * buff, uint32 size, void * userdata);
#ifdef __cplusplus
extern "C" {
#endif
void * avf_audio_play_init(int samplerate, int channels);
void avf_audio_play_uninit(void * ctx);
void avf_audio_play_set_callback(void * ctx, avf_audio_play_callback cb, void * userdata);
BOOL avf_audio_play_set_volume(void * ctx, double volume);
double avf_audio_play_get_volume(void * ctx);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,439 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_play_avf.h"
#include <CoreAudio/CoreAudio.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>
/***************************************************************************************/
typedef struct
{
BOOL play_flag;
pthread_t play_thread;
AudioQueueRef audio_queue;
int num_audio_buffers;
AudioQueueBufferRef * audio_buffer;
void * buffer;
uint32 buffer_offset;
uint32 buffer_size;
AudioStreamBasicDescription strdesc;
AudioDeviceID device_id;
int samplerate;
uint16 channels;
uint16 samples;
int samplefmt;
avf_audio_play_callback callback;
void * userdata;
void * mutex_cb;
} AVFAudioPlayContext;
/***************************************************************************************/
BOOL avf_audio_play_prepare_device(AVFAudioPlayContext * context)
{
AudioDeviceID devid;
OSStatus result = noErr;
uint32 size = 0;
uint32 alive = 0;
pid_t pid = 0;
AudioObjectPropertyAddress addr = {
0,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMain
};
size = sizeof(AudioDeviceID);
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
0, NULL, &size, &devid);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioObjectGetPropertyData failed\r\n", __FUNCTION__);
return FALSE;
}
addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
addr.mScope = kAudioDevicePropertyScopeOutput;
size = sizeof (alive);
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioObjectGetPropertyData failed\r\n", __FUNCTION__);
return FALSE;
}
if (!alive)
{
log_print(HT_LOG_ERR, "%s, requested device exists, but isn't alive\r\n", __FUNCTION__);
return FALSE;
}
addr.mSelector = kAudioDevicePropertyHogMode;
size = sizeof (pid);
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
/* some devices don't support this property, so errors are fine here. */
if ((result == noErr) && (pid != -1))
{
log_print(HT_LOG_ERR, "%s, requested device is being hogged\r\n", __FUNCTION__);
return FALSE;
}
context->device_id = devid;
return TRUE;
}
int avf_audio_play_assign_device(AVFAudioPlayContext * context)
{
const AudioObjectPropertyAddress prop = {
kAudioDevicePropertyDeviceUID,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMain
};
OSStatus result;
CFStringRef devuid;
uint32 devuidsize = sizeof (devuid);
result = AudioObjectGetPropertyData(context->device_id, &prop, 0, NULL, &devuidsize, &devuid);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioObjectGetPropertyData failed\r\n", __FUNCTION__);
return 0;
}
result = AudioQueueSetProperty(context->audio_queue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueSetProperty failed\r\n", __FUNCTION__);
return 0;
}
return 1;
}
void avf_audio_play_cb(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
{
AVFAudioPlayContext * context = (AVFAudioPlayContext *) inUserData;
uint32 remaining = inBuffer->mAudioDataBytesCapacity;
uint8 *ptr = (uint8 *) inBuffer->mAudioData;
while (remaining > 0)
{
uint32 len;
if (context->buffer_offset >= context->buffer_size)
{
sys_os_mutex_enter(context->mutex_cb);
if (context->callback)
{
context->callback(context->buffer, context->buffer_size, context->userdata);
}
sys_os_mutex_leave(context->mutex_cb);
context->buffer_offset = 0;
}
len = context->buffer_size - context->buffer_offset;
if (len > remaining)
{
len = remaining;
}
memcpy(ptr, (char *)context->buffer + context->buffer_offset, len);
ptr = ptr + len;
remaining -= len;
context->buffer_offset += len;
}
AudioQueueEnqueueBuffer(context->audio_queue, inBuffer, 0, NULL);
inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
}
int avf_audio_play_prepare_queue(AVFAudioPlayContext * context)
{
int i;
OSStatus result;
const AudioStreamBasicDescription *strdesc = &context->strdesc;
result = AudioQueueNewOutput(strdesc, avf_audio_play_cb, context, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &context->audio_queue);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueNewOutput failed\r\n", __FUNCTION__);
return 0;
}
if (!avf_audio_play_assign_device(context))
{
log_print(HT_LOG_ERR, "%s, avf_audio_play_assign_device failed\r\n", __FUNCTION__);
return 0;
}
AudioChannelLayout layout;
memset(&layout, 0, sizeof(layout));
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
result = AudioQueueSetProperty(context->audio_queue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueSetProperty failed\r\n", __FUNCTION__);
return 0;
}
uint32 size = strdesc->mBitsPerChannel / 8;
size *= context->channels;
size *= context->samples;
/* Allocate a sample buffer */
context->buffer_size = size;
context->buffer_offset = context->buffer_size;
context->buffer = malloc(context->buffer_size);
if (context->buffer == NULL)
{
log_print(HT_LOG_ERR, "%s, malloc failed\r\n", __FUNCTION__);
return 0;
}
/* Make sure we can feed the device a minimum amount of time */
double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
const double msecs = (context->samples / ((double) context->samplerate)) * 1000.0;
int num_audio_buffers = 2;
if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS)
{
/* use more buffers if we have a VERY small sample set. */
num_audio_buffers = ((int)ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
}
context->num_audio_buffers = num_audio_buffers;
context->audio_buffer = (AudioQueueBufferRef *) calloc(1, sizeof(AudioQueueBufferRef) * num_audio_buffers);
if (context->audio_buffer == NULL)
{
log_print(HT_LOG_ERR, "%s, calloc failed\r\n", __FUNCTION__);
return 0;
}
for (i = 0; i < num_audio_buffers; i++)
{
result = AudioQueueAllocateBuffer(context->audio_queue, size, &context->audio_buffer[i]);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueAllocateBuffer failed\r\n", __FUNCTION__);
return 0;
}
memset(context->audio_buffer[i]->mAudioData, 0, context->audio_buffer[i]->mAudioDataBytesCapacity);
context->audio_buffer[i]->mAudioDataByteSize = context->audio_buffer[i]->mAudioDataBytesCapacity;
result = AudioQueueEnqueueBuffer(context->audio_queue, context->audio_buffer[i], 0, NULL);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueEnqueueBuffer failed\r\n", __FUNCTION__);
return 0;
}
}
result = AudioQueueStart(context->audio_queue, NULL);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueStart failed\r\n", __FUNCTION__);
return 0;
}
/* We're running! */
return 1;
}
void * avf_audio_play_thread(void * argv)
{
AVFAudioPlayContext * context = (AVFAudioPlayContext *) argv;
const int rc = avf_audio_play_prepare_queue(context);
if (!rc)
{
log_print(HT_LOG_ERR, "%s, avf_audio_play_prepare_queue failed\r\n", __FUNCTION__);
return NULL;
}
while (context->play_flag)
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
}
context->play_thread = 0;
return NULL;
}
void * avf_audio_play_init(int samplerate, int channels)
{
AVFAudioPlayContext * context = (AVFAudioPlayContext *)malloc(sizeof(AVFAudioPlayContext));
if (NULL == context)
{
return NULL;
}
memset(context, 0, sizeof(AVFAudioPlayContext));
context->samplerate = samplerate;
context->channels = channels;
context->samples = 1024;
context->samplefmt = 1; // AV_SAMPLE_FMT_S16
AudioStreamBasicDescription *strdesc = &context->strdesc;
memset(strdesc, 0, sizeof(AudioStreamBasicDescription));
strdesc->mFormatID = kAudioFormatLinearPCM;
strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
strdesc->mChannelsPerFrame = channels;
strdesc->mSampleRate = samplerate;
strdesc->mFramesPerPacket = 1;
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
strdesc->mBitsPerChannel = 16;
strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8;
strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
if (!avf_audio_play_prepare_device(context))
{
log_print(HT_LOG_ERR, "%s, avf_prepare_device failed!\r\n", __FUNCTION__);
goto fail;
}
context->play_flag = TRUE;
context->play_thread = sys_os_create_thread((void *)avf_audio_play_thread, (void *)context);
if (!context->play_thread)
{
goto fail;
}
context->mutex_cb = sys_os_create_mutex();
return context;
fail:
avf_audio_play_uninit((void *)context);
return NULL;
}
void avf_audio_play_uninit(void * ctx)
{
if (NULL == ctx)
{
return;
}
AVFAudioPlayContext * context = (AVFAudioPlayContext *)ctx;
if (context->audio_queue)
{
AudioQueueDispose(context->audio_queue, 1);
}
context->play_flag = FALSE;
while (context->play_thread)
{
usleep(200*1000);
}
free(context->audio_buffer);
free(context->buffer);
sys_os_destroy_sig_mutex(context->mutex_cb);
free(context);
}
void avf_audio_play_set_callback(void * ctx, avf_audio_play_callback cb, void * userdata)
{
if (NULL == ctx)
{
return;
}
AVFAudioPlayContext * context = (AVFAudioPlayContext *)ctx;
sys_os_mutex_enter(context->mutex_cb);
context->callback = cb;
context->userdata = userdata;
sys_os_mutex_leave(context->mutex_cb);
}
BOOL avf_audio_play_set_volume(void * ctx, double volume)
{
if (NULL == ctx)
{
return FALSE;
}
AVFAudioPlayContext * context = (AVFAudioPlayContext *)ctx;
if (!context->audio_queue)
{
return FALSE;
}
OSStatus result = AudioQueueSetParameter(context->audio_queue, kAudioQueueParam_Volume, volume);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueSetParameter failed\r\n", __FUNCTION__);
return FALSE;
}
return TRUE;
}
double avf_audio_play_get_volume(void * ctx)
{
if (NULL == ctx)
{
return 0;
}
double volume = 0;
AVFAudioPlayContext * context = (AVFAudioPlayContext *)ctx;
if (!context->audio_queue)
{
return 0;
}
OSStatus result = AudioQueueGetParameter(context->audio_queue, kAudioQueueParam_Volume, (float *)&volume);
if (result != noErr)
{
log_print(HT_LOG_ERR, "%s, AudioQueueGetParameter failed\r\n", __FUNCTION__);
return 0;
}
return volume;
}

View File

@@ -0,0 +1,181 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_play_mac.h"
void audioPlayCb(void * buff, uint32 size, void * userdata)
{
CMAudioPlay * player = (CMAudioPlay *) userdata;
player->playCallback(buff, size);
}
CMAudioPlay::CMAudioPlay() : CAudioPlay()
, m_pMutex(NULL)
, m_pPlayer(NULL)
, m_pBuffer(NULL)
, m_nBufferSize(0)
, m_nOffset(0)
{
}
CMAudioPlay::~CMAudioPlay()
{
stopPlay();
}
BOOL CMAudioPlay::startPlay(int samplerate, int channels)
{
m_pPlayer = avf_audio_play_init(samplerate, channels);
if (NULL == m_pPlayer)
{
log_print(HT_LOG_ERR, "%s, avf_audio_play_init failed\r\n", __FUNCTION__);
return FALSE;
}
m_nBufferSize = 8192;
m_pBuffer = (uint8 *) malloc(m_nBufferSize);
if (NULL == m_pBuffer)
{
log_print(HT_LOG_ERR, "%s, malloc failed\r\n", __FUNCTION__);
return FALSE;
}
avf_audio_play_set_callback(m_pPlayer, audioPlayCb, this);
m_pMutex = sys_os_create_mutex();
m_nSamplerate = samplerate;
m_nChannels = channels;
m_bInited = TRUE;
return TRUE;
}
void CMAudioPlay::stopPlay()
{
avf_audio_play_uninit(m_pPlayer);
sys_os_mutex_enter(m_pMutex);
if (m_pBuffer)
{
free(m_pBuffer);
m_pBuffer = NULL;
}
m_nOffset = 0;
sys_os_mutex_leave(m_pMutex);
sys_os_destroy_sig_mutex(m_pMutex);
m_bInited = FALSE;
}
void CMAudioPlay::playAudio(uint8 * data, int size)
{
if (!m_bInited)
{
return;
}
while (1)
{
sys_os_mutex_enter(m_pMutex);
if (m_nOffset + size <= m_nBufferSize)
{
memcpy(m_pBuffer + m_nOffset, data, size);
m_nOffset += size;
sys_os_mutex_leave(m_pMutex);
break;
}
else
{
sys_os_mutex_leave(m_pMutex);
usleep(10*1000);
continue;
}
}
}
void CMAudioPlay::playCallback(void * buff, uint32 size)
{
sys_os_mutex_enter(m_pMutex);
if (m_nOffset >= size)
{
memcpy(buff, m_pBuffer, size);
m_nOffset -= size;
if (m_nOffset > 0)
{
memmove(m_pBuffer, m_pBuffer+size, m_nOffset);
}
}
else
{
memset(buff, 0, size);
}
sys_os_mutex_leave(m_pMutex);
}
BOOL CMAudioPlay::setVolume(int volume)
{
if (m_pPlayer)
{
double db = (double) volume / (HTVOLUME_MAX - HTVOLUME_MIN);
if (db < 0)
{
db = 0.0;
}
else if (db > 1.0)
{
db = 1.0;
}
return avf_audio_play_set_volume((void *)m_pPlayer, db);
}
return FALSE;
}
int CMAudioPlay::getVolume()
{
if (m_pPlayer)
{
double volume = avf_audio_play_get_volume((void *)m_pPlayer);
int nv = (HTVOLUME_MAX - HTVOLUME_MIN) * volume + HTVOLUME_MIN;
return nv;
}
return HTVOLUME_MIN;
}

View File

@@ -0,0 +1,53 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_PLAY_MAC_H
#define AUDIO_PLAY_MAC_H
#include "sys_inc.h"
#include "linked_list.h"
#include "media_format.h"
#include "audio_play.h"
#include "audio_play_avf.h"
class CMAudioPlay : public CAudioPlay
{
public:
CMAudioPlay();
~CMAudioPlay();
BOOL startPlay(int samplerate, int channels);
void stopPlay();
BOOL setVolume(int volume);
int getVolume();
void playAudio(uint8 * pData, int len);
void playCallback(void * buff, uint32 size);
private:
void * m_pMutex; // mutex
void * m_pPlayer;
uint8 * m_pBuffer;
uint32 m_nBufferSize;
uint32 m_nOffset;
};
#endif

View File

@@ -0,0 +1,157 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_play_qt.h"
#include <QMutexLocker>
#include <QMediaDevices>
CQAudioPlay::CQAudioPlay(QObject * parent) : QObject(parent), CAudioPlay()
, m_pSink(NULL)
, m_pBuffer(NULL)
{
}
CQAudioPlay::~CQAudioPlay()
{
stopPlay();
}
BOOL CQAudioPlay::startPlay(int samplerate, int channels)
{
QAudioFormat format;
format.setSampleRate(samplerate);
format.setChannelCount(channels);
format.setSampleFormat(QAudioFormat::Int16);
QAudioDevice device(QMediaDevices::defaultAudioOutput());
if (!device.isFormatSupported(format))
{
log_print(HT_LOG_ERR, "Raw audio format not supported by backend, cannot play audio\r\n");
return FALSE;
}
m_nSamplerate = samplerate;
m_nChannels = channels;
m_pSink = new QAudioSink(device, format, NULL);
m_pSink->setBufferSize(8192);
m_pBuffer = m_pSink->start();
if (NULL == m_pBuffer)
{
log_print(HT_LOG_ERR, "%s, audio output start failed\r\n", __FUNCTION__);
return FALSE;
}
m_bInited = TRUE;
return TRUE;
}
void CQAudioPlay::stopPlay()
{
QMutexLocker locker(&m_mutex);
if (m_pSink)
{
m_pSink->stop();
delete m_pSink;
m_pSink = NULL;
}
m_pBuffer = NULL;
m_bInited = FALSE;
}
void CQAudioPlay::playAudio(uint8 * data, int size)
{
if (!m_bInited)
{
return;
}
QMutexLocker locker(&m_mutex);
int rlen = m_pBuffer->write((char *)data, size);
while (rlen < size)
{
if (rlen < 0)
{
break; // error
}
else
{
size -= rlen;
data += rlen;
}
rlen = m_pBuffer->write((char *)data, size);
}
}
BOOL CQAudioPlay::setVolume(int volume)
{
QMutexLocker locker(&m_mutex);
if (m_pSink)
{
double db = (double) volume / (HTVOLUME_MAX - HTVOLUME_MIN);
if (db < 0)
{
db = 0.0;
}
else if (db > 1.0)
{
db = 1.0;
}
log_print(HT_LOG_DBG, "%s, volume=%d, db=%f\r\n", __FUNCTION__, volume, db);
m_pSink->setVolume(db);
return TRUE;
}
return FALSE;
}
int CQAudioPlay::getVolume()
{
double volume = HTVOLUME_MIN;
QMutexLocker locker(&m_mutex);
if (m_pSink)
{
volume = m_pSink->volume();
}
int nv = (HTVOLUME_MAX - HTVOLUME_MIN) * volume + HTVOLUME_MIN;
return nv;
}

View File

@@ -0,0 +1,56 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_PLAY_QT_H
#define AUDIO_PLAY_QT_H
#include "sys_inc.h"
#include "linked_list.h"
#include "media_format.h"
#include "audio_play.h"
#include <QObject>
#include <QAudioFormat>
#include <QAudioSink>
#include <QBuffer>
#include <QMutex>
class CQAudioPlay : public QObject, public CAudioPlay
{
Q_OBJECT
public:
CQAudioPlay(QObject * parent = NULL);
~CQAudioPlay();
BOOL startPlay(int samplerate, int channels);
void stopPlay();
BOOL setVolume(int volume);
int getVolume();
void playAudio(uint8 * pData, int len);
private:
QAudioSink * m_pSink;
QIODevice * m_pBuffer;
QMutex m_mutex;
};
#endif

View File

@@ -0,0 +1,349 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "audio_play_win.h"
/**************************************************************************************/
#pragma comment(lib, "dsound.lib")
/**************************************************************************************/
CWAudioPlay::CWAudioPlay()
: CAudioPlay()
, m_pDSound8(NULL)
, m_pDSoundBuffer(NULL)
, m_pMutex(NULL)
, m_pAudioBuff(NULL)
, m_nAudioBuffLen(0)
, m_nLastChunk(0)
, m_nBufferNums(8)
, m_nSampleNums(1024)
, m_nSpecSize(0)
{
m_pMutex = sys_os_create_mutex();
}
CWAudioPlay::~CWAudioPlay(void)
{
stopPlay();
sys_os_destroy_sig_mutex(m_pMutex);
m_pMutex = NULL;
}
BOOL CWAudioPlay::startPlay(int samplerate, int channels)
{
HRESULT ret = DirectSoundCreate8(NULL, &m_pDSound8, NULL);
if (FAILED(ret))
{
log_print(HT_LOG_ERR, "%s, DirectSoundCreate8 failed\r\n", __FUNCTION__);
return FALSE;
}
ret = m_pDSound8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_NORMAL);
if (FAILED(ret))
{
stopPlay();
log_print(HT_LOG_ERR, "%s, SetCooperativeLevel failed\r\n", __FUNCTION__);
return FALSE;
}
WAVEFORMATEX format;
memset(&format, 0, sizeof(WAVEFORMATEX));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = channels;
format.wBitsPerSample = 16;
format.nSamplesPerSec = samplerate;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec;
format.cbSize = 0;
DSBUFFERDESC buf_desc;
memset(&buf_desc, 0, sizeof(DSBUFFERDESC));
m_nSpecSize = m_nSampleNums * channels * 2;
buf_desc.dwSize = sizeof(buf_desc);
buf_desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME;
buf_desc.dwBufferBytes = m_nBufferNums * m_nSpecSize;
buf_desc.dwReserved = 0;
buf_desc.lpwfxFormat = &format;
ret = m_pDSound8->CreateSoundBuffer(&buf_desc, &m_pDSoundBuffer, NULL);
if (FAILED(ret))
{
stopPlay();
log_print(HT_LOG_ERR, "%s, CreateSoundBuffer failed\r\n", __FUNCTION__);
return FALSE;
}
m_pDSoundBuffer->SetFormat(&format);
void * buf1 = NULL;
DWORD len1 = 0;
void * buf2 = NULL;
DWORD len2 = 0;
/* Silence the initial audio buffer */
ret = m_pDSoundBuffer->Lock(0, buf_desc.dwBufferBytes, &buf1, &len1, &buf2, &len2, DSBLOCK_ENTIREBUFFER);
if (ret == DS_OK)
{
memset(buf1, 0, len1);
m_pDSoundBuffer->Unlock(buf1, len1, buf2, len2);
}
m_pAudioBuff = (uint8 *) malloc(m_nSpecSize);
if (NULL == m_pAudioBuff)
{
stopPlay();
log_print(HT_LOG_ERR, "%s, memory malloc failed\r\n", __FUNCTION__);
return FALSE;
}
m_nSamplerate = samplerate;
m_nChannels = channels;
m_bInited = TRUE;
return TRUE;
}
void CWAudioPlay::stopPlay()
{
m_bInited = FALSE;
sys_os_mutex_enter(m_pMutex);
if (m_pDSoundBuffer)
{
m_pDSoundBuffer->Stop();
m_pDSoundBuffer->Release();
m_pDSoundBuffer = NULL;
}
if (m_pDSound8)
{
m_pDSound8->Release();
m_pDSound8 = NULL;
}
if (m_pAudioBuff)
{
free(m_pAudioBuff);
m_pAudioBuff = NULL;
}
sys_os_mutex_leave(m_pMutex);
m_nAudioBuffLen = 0;
}
BOOL CWAudioPlay::setVolume(int volume)
{
if (m_pDSoundBuffer)
{
double db = (double) volume / (HTVOLUME_MAX - HTVOLUME_MIN);
if (db < 0)
{
db = -db;
}
int nv = (DSBVOLUME_MAX - DSBVOLUME_MIN) * db + DSBVOLUME_MIN;
HRESULT hr = m_pDSoundBuffer->SetVolume(nv);
if (DS_OK == hr)
{
return TRUE;
}
}
return FALSE;
}
int CWAudioPlay::getVolume()
{
if (m_pDSoundBuffer)
{
long volume = 0;
HRESULT ret = m_pDSoundBuffer->GetVolume(&volume);
if (SUCCEEDED(ret))
{
double db = (double) volume / (DSBVOLUME_MAX - DSBVOLUME_MIN);
if (db < 0)
{
db = -db;
}
int nv = (HTVOLUME_MAX - HTVOLUME_MIN) * db + HTVOLUME_MIN;
return nv;
}
}
return HTVOLUME_MIN;
}
void CWAudioPlay::playAudio1(uint8 * data, int size)
{
DWORD cursor = 0;
DWORD junk = 0;
HRESULT result = DS_OK;
DWORD rawlen = 0;
void * bufptr = NULL;
/* Figure out which blocks to fill next */
result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor);
if (result == DSERR_BUFFERLOST)
{
m_pDSoundBuffer->Restore();
result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor);
}
if (result != DS_OK)
{
return;
}
cursor /= m_nSpecSize;
m_nLastChunk = cursor;
cursor = (cursor + 1) % m_nBufferNums;
cursor *= m_nSpecSize;
/* Lock the audio buffer */
result = m_pDSoundBuffer->Lock(cursor, m_nSpecSize, &bufptr, &rawlen, NULL, &junk, 0);
if (result == DSERR_BUFFERLOST)
{
m_pDSoundBuffer->Restore();
result = m_pDSoundBuffer->Lock(cursor, m_nSpecSize, &bufptr, &rawlen, NULL, &junk, 0);
}
if (result != DS_OK)
{
return;
}
if (bufptr && rawlen > 0)
{
memcpy(bufptr, data, rawlen);
}
m_pDSoundBuffer->Unlock(bufptr, rawlen, NULL, 0);
waitDevice();
}
void CWAudioPlay::playAudio(uint8 * data, int size)
{
if (!m_bInited)
{
return;
}
sys_os_mutex_enter(m_pMutex);
if (NULL == m_pDSoundBuffer)
{
sys_os_mutex_leave(m_pMutex);
return;
}
while (m_nAudioBuffLen + size >= (uint32)m_nSpecSize)
{
memcpy(m_pAudioBuff + m_nAudioBuffLen, data, m_nSpecSize - m_nAudioBuffLen);
playAudio1(m_pAudioBuff, m_nSpecSize);
size -= m_nSpecSize - m_nAudioBuffLen;
data += m_nSpecSize - m_nAudioBuffLen;
m_nAudioBuffLen = 0;
}
if (size > 0)
{
memcpy(m_pAudioBuff + m_nAudioBuffLen, data, size);
m_nAudioBuffLen += size;
}
sys_os_mutex_leave(m_pMutex);
}
void CWAudioPlay::waitDevice()
{
DWORD status = 0;
DWORD cursor = 0;
DWORD junk = 0;
HRESULT result = DS_OK;
result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor);
if (result != DS_OK)
{
if (result == DSERR_BUFFERLOST)
{
m_pDSoundBuffer->Restore();
}
return;
}
while ((cursor / m_nSpecSize) == m_nLastChunk)
{
usleep(1000);
/* Try to restore a lost sound buffer */
m_pDSoundBuffer->GetStatus(&status);
if (status & DSBSTATUS_BUFFERLOST)
{
m_pDSoundBuffer->Restore();
m_pDSoundBuffer->GetStatus(&status);
if ((status & DSBSTATUS_BUFFERLOST))
{
break;
}
}
if (!(status & DSBSTATUS_PLAYING))
{
result = m_pDSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
if (result == DS_OK)
{
continue;
}
return;
}
/* Find out where we are playing */
result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor);
if (result != DS_OK)
{
return;
}
}
}

View File

@@ -0,0 +1,61 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AUDIO_PLAY_WIN_H
#define AUDIO_PLAY_WIN_H
#include "media_format.h"
#include "audio_play.h"
#include <dsound.h>
class CWAudioPlay : public CAudioPlay
{
public:
CWAudioPlay();
~CWAudioPlay();
public:
BOOL startPlay(int samplerate, int channels);
void stopPlay();
BOOL setVolume(int volume);
int getVolume();
void playAudio(uint8 * pData, int len);
private:
void playAudio1(uint8 * data, int size);
void waitDevice();
private:
LPDIRECTSOUND8 m_pDSound8;
LPDIRECTSOUNDBUFFER m_pDSoundBuffer;
void * m_pMutex;
uint8 * m_pAudioBuff;
uint32 m_nAudioBuffLen;
int m_nLastChunk;
int m_nBufferNums;
int m_nSampleNums;
int m_nSpecSize;
};
#endif

View File

@@ -0,0 +1,48 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "avcodec_mutex.h"
void * g_avcodec_mutex = sys_os_create_mutex();
int avcodec_thread_open(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
int ret;
sys_os_mutex_enter(g_avcodec_mutex);
ret = avcodec_open2(avctx, codec, options);
sys_os_mutex_leave(g_avcodec_mutex);
return ret;
}
int avcodec_thread_close(AVCodecContext *avctx)
{
int ret;
sys_os_mutex_enter(g_avcodec_mutex);
ret = avcodec_close(avctx);
sys_os_mutex_leave(g_avcodec_mutex);
return ret;
}

View File

@@ -0,0 +1,41 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AVCODEC_MUTEX_H
#define AVCODEC_MUTEX_H
extern "C" {
#include "libavcodec/avcodec.h"
}
#ifdef __cplusplus
extern "C" {
#endif
int avcodec_thread_open(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
int avcodec_thread_close(AVCodecContext *avctx);
#ifdef __cplusplus
}
#endif
#endif

188
MediaClient/media/avi.h Normal file
View File

@@ -0,0 +1,188 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AVI_H
#define AVI_H
#include "format.h"
/* Flags in avih */
#define AVIF_HASINDEX 0x00000010 // Index at end of file?
#define AVIF_ISINTERLEAVED 0x00000100 // is interleaved?
#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames?
#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame.
#pragma pack(push)
#pragma pack(1)
typedef struct avi_riff
{
uint32 riff;
uint32 len;
uint32 type;
} AVIRIFF;
typedef struct avi_pkt
{
uint32 type; // packet type : PACKET_TYPE_UNKNOW, PACKET_TYPE_VIDEO,PACKET_TYPE_AUDIO
uint8 * rbuf; // buffer pointer
uint32 mlen; // buffer size
uint8 * dbuf; // data pointer
uint32 len; // packet length
} AVIPKT;
typedef struct avi_main_header
{
uint32 dwMicroSecPerFrame; // render time for per frame, unit is ns
uint32 dwMaxBytesPerSec; // maximum data transfer rate
uint32 dwPaddingGranularity; // The length of the record block needs to be a multiple of this value, usually 2048
uint32 dwFlags; // Special attributes of AVI files, such as whether to include index blocks, whether audio and video data is interleaved
uint32 dwTotalFrames; // The total number of frames in the file
uint32 dwInitialFrames; // How much frames is needed before starting to play
uint32 dwStreams; // The number of data streams contained in the file
uint32 dwSuggestedBufferSize; // The recommended size of the buffer, usually the sum of the data needed to store an image and synchronize the sound
uint32 dwWidth; // Image width
uint32 dwHeight; // Image height
uint32 dwReserved[4]; // Reserved
} AVIMHDR;
typedef struct avi_stream_header
{
uint32 fccType; // 4 bytes, indicating the type of data stream, vids for video data stream, auds audio data stream
uint32 fccHandler; // 4 bytes, indicating the driver code for data stream decompression
uint32 dwFlags; // Data stream attribute
uint16 wPriority; // Play priority of this stream
uint16 wLanguage; // Audio language code
uint32 dwInitialFrames; // How much frames is needed before starting to play
uint32 dwScale; // The amount of data, the size of each video or the sample size of the audio
uint32 dwRate; // dwScale / dwRate = Number of samples per second
uint32 dwStart; // The location where the data stream starts playing, in dwScale
uint32 dwLength; // The amount of data in the data stream, in dwScale
uint32 dwSuggestedBufferSize; // Recommended buffer size
uint32 dwQuality; // Decompress quality parameters, the larger the value, the better the quality
uint32 dwSampleSize; // Sample size of the audio
struct
{
short left;
short top;
short right;
short bottom;
} rcFrame;
} AVISHDR;
typedef struct bitmap_info_header
{
uint32 biSize;
int biWidth;
int biHeight;
uint16 biPlanes;
uint16 biBitCount;
uint32 biCompression;
uint32 biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
uint32 biClrUsed;
uint32 biClrImportant;
} BMPHDR;
typedef struct wave_format
{
uint16 wFormatTag; // format type
uint16 nChannels; // number of channels (i.e. mono, stereo...)
uint32 nSamplesPerSec; // sample rate
uint32 nAvgBytesPerSec; // for buffer estimation
uint16 nBlockAlign; // block size of data
uint16 wBitsPerSample; // number of bits per sample of mono data
uint16 cbSize; // the count in bytes of the size of
// extra information (after cbSize)
} WAVEFMT;
#pragma pack(pop)
typedef struct avi_file_context
{
uint32 ctxf_read : 1; // Read mode
uint32 ctxf_write : 1; // Write mode
uint32 ctxf_idx : 1; // File has index
uint32 ctxf_audio : 1; // Has audio stream
uint32 ctxf_video : 1; // Has video stream
uint32 ctxf_idx_m : 1; // Index data write mode: = 1, memory mode; = 0, temporary file
uint32 ctxf_calcfps: 1; // Calc video framerate
uint32 ctxf_nalu : 1; // For H.26x, VPS, SPS,PPS written flag
uint32 ctxf_iframe : 1; // For H.26x, key frame written flag
uint32 ctxf_res : 23;
AVIMHDR avi_hdr; // AVI main header
AVISHDR str_v; // Video stream header
BMPHDR bmp; // BITMAP format
AVISHDR str_a; // Audio stream header
WAVEFMT wave; // WAVE format
FILE * fp; // Read and write file handle
uint32 flen; // Total file length, used when reading
char filename[256]; // File full path
void * mutex; // Write, close mutex
uint32 v_fps; // Video frame rate
char v_fcc[4]; // Video compression standard, "H264","H265","JPEG","MP4V"
int v_width; // Video width
int v_height; // Video height
uint8 * v_extra; // Video extra data
int v_extra_len; // Video extra data length
int a_rate; // Sampling frequency
uint16 a_fmt; // Audio compression standard
int a_chns; // Number of audio channels
uint8 * a_extra; // Audio extra data
int a_extra_len; // Audio extra data length
int i_movi; // Where the real data starts
int i_movi_end; // End of data, where the index starts
int i_riff; // After the index, the length of the entire file
int pkt_offset; // Packet offset when reading
int index_offset; // Index position when reading
int back_index; // The first read index position when reading in reverse order, usually sps/vps
int i_frame_video; // Video frame read and write count
int i_frame_audio; // Audio frame read and write count
uint32 audio_total_bytes; // Audio total bytes
uint32 prev_ts; // previous timestamp
uint32 v_s_time; // Video start recording time = first packet write time
uint32 v_e_time; // The time when video package was recently written
uint32 a_s_time; // Audio start recording time = first packet write time
uint32 a_e_time; // The time when audio package was recently written
int i_idx_max; // The maximum number of indexes currently allocated
int i_idx; // Current index number (video index + audio index)
int * idx; // Index array
FILE * idx_fp; // Index temporary file
int idx_fix[128]; // Index file data is enough to write once for one sector
int idx_fix_off; // The index data has been stored in the offset of idx_fix
} AVICTX;
#endif // AVI_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef AVI_WRITE_H
#define AVI_WRITE_H
#include "sys_inc.h"
#include "avi.h"
#ifdef __cplusplus
extern "C" {
#endif
void avi_free_idx(AVICTX * p_ctx);
int avi_write_idx(AVICTX * p_ctx);
void avi_set_dw(void * p, uint32 dw);
int avi_end(AVICTX * p_ctx);
AVICTX* avi_write_open(const char * filename);
int avi_write_video(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts, int b_key);
int avi_write_nalu(AVICTX * p_ctx, uint8 * vps, int vps_len, uint8 * sps, int sps_len, uint8 * pps, int pps_len);
int avi_write_audio(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts);
void avi_write_close(AVICTX * p_ctx);
void avi_set_video_info(AVICTX * p_ctx, int fps, int width, int height, const char fcc[4]);
void avi_set_video_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len);
void avi_set_audio_info(AVICTX * p_ctx, int chns, int rate, uint16 fmt);
void avi_set_audio_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len);
void avi_build_video_hdr(AVICTX * p_ctx);
void avi_build_audio_hdr(AVICTX * p_ctx);
int avi_write_header(AVICTX * p_ctx);
int avi_update_header(AVICTX * p_ctx);
int avi_calc_fps(AVICTX * p_ctx);
uint64 avi_get_file_length(AVICTX * p_ctx);
uint32 avi_get_media_time(AVICTX * p_ctx);
#ifdef __cplusplus
}
#endif
#endif // AVI_WRITE_H

View File

@@ -0,0 +1,917 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "file_player.h"
#include "utils.h"
#include "media_codec.h"
void * readThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->readThread();
return NULL;
}
void * videoThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->videoThread();
return NULL;
}
void * audioThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->audioThread();
return NULL;
}
CFilePlayer::CFilePlayer()
: CVideoPlayer()
, m_nAudioIndex(-1)
, m_nVideoIndex(-1)
, m_nDuration(0)
, m_nCurPos(0)
, m_bSeek(0)
, m_dSeekPos(0)
, m_nNalLength(0)
, m_pFormatContext(NULL)
, m_hReadThread(0)
, m_hVideoThread(0)
, m_hAudioThread(0)
, m_pVideoQueue(NULL)
, m_pAudioQueue(NULL)
{
}
CFilePlayer::~CFilePlayer()
{
close();
}
BOOL CFilePlayer::openFile(const char * filename)
{
if (avformat_open_input(&m_pFormatContext, filename, NULL, NULL) != 0)
{
log_print(HT_LOG_ERR, "avformat_open_input failed, %s\r\n", filename);
return FALSE;
}
avformat_find_stream_info(m_pFormatContext, NULL);
if (m_pFormatContext->duration != AV_NOPTS_VALUE)
{
m_nDuration = m_pFormatContext->duration;
}
// find audio & video stream index
for (uint32 i=0; i < m_pFormatContext->nb_streams; i++)
{
if (m_pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_nVideoIndex = i;
if (m_nDuration < m_pFormatContext->streams[i]->duration)
{
m_nDuration = m_pFormatContext->streams[i]->duration;
}
}
else if (m_pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_nAudioIndex = i;
if (m_nDuration < m_pFormatContext->streams[i]->duration)
{
m_nDuration = m_pFormatContext->streams[i]->duration;
}
}
}
m_nDuration /= 1000; // to millisecond
// has video stream
if (m_nVideoIndex != -1)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
if (codecpar->extradata[0] == 1)
{
// Store right nal length size that will be used to parse all other nals
m_nNalLength = (codecpar->extradata[4] & 0x03) + 1;
}
}
const AVBitStreamFilter * bsfc = av_bsf_get_by_name("h264_mp4toannexb");
if (bsfc)
{
int ret;
AVBSFContext *bsf;
av_bsf_alloc(bsfc, &bsf);
ret = avcodec_parameters_copy(bsf->par_in, codecpar);
if (ret < 0)
{
return FALSE;
}
ret = av_bsf_init(bsf);
if (ret < 0)
{
return FALSE;
}
ret = avcodec_parameters_copy(codecpar, bsf->par_out);
if (ret < 0)
{
return FALSE;
}
av_bsf_free(&bsf);
}
}
else if (codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
if (codecpar->extradata[0] || codecpar->extradata[1] || codecpar->extradata[2] > 1)
{
m_nNalLength = 4;
}
}
const AVBitStreamFilter * bsfc = av_bsf_get_by_name("hevc_mp4toannexb");
if (bsfc)
{
int ret;
AVBSFContext *bsf;
av_bsf_alloc(bsfc, &bsf);
ret = avcodec_parameters_copy(bsf->par_in, codecpar);
if (ret < 0)
{
return FALSE;
}
ret = av_bsf_init(bsf);
if (ret < 0)
{
return FALSE;
}
ret = avcodec_parameters_copy(codecpar, bsf->par_out);
if (ret < 0)
{
return FALSE;
}
av_bsf_free(&bsf);
}
}
}
return TRUE;
}
BOOL CFilePlayer::open(std::string fileName)
{
BOOL ret = FALSE;
close();
CVideoPlayer::open(fileName);
ret = openFile(fileName.c_str());
if (ret)
{
if (m_nVideoIndex >= 0)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
openVideo(codecpar->codec_id, codecpar->extradata, codecpar->extradata_size);
}
if (m_nAudioIndex >= 0)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
openAudio(codecpar->codec_id, codecpar->sample_rate, codecpar->channels, codecpar->bits_per_coded_sample);
}
}
return ret;
}
void CFilePlayer::close()
{
m_bPlaying = FALSE;
HTPACKET packet;
memset(&packet, 0, sizeof(packet));
clearQueue(m_pAudioQueue);
clearQueue(m_pVideoQueue);
hqBufPut(m_pAudioQueue, (char *)&packet);
hqBufPut(m_pVideoQueue, (char *)&packet);
while (m_hAudioThread)
{
usleep(100*1000);
}
while (m_hVideoThread)
{
usleep(100*1000);
}
while (m_hReadThread)
{
usleep(100*1000);
}
clearQueue(m_pAudioQueue);
clearQueue(m_pVideoQueue);
hqDelete(m_pAudioQueue);
m_pAudioQueue = NULL;
hqDelete(m_pVideoQueue);
m_pVideoQueue = NULL;
if (m_pFormatContext)
{
avformat_close_input(&m_pFormatContext);
}
CVideoPlayer::close();
}
BOOL CFilePlayer::play()
{
m_bPaused = FALSE;
if (m_bPlaying)
{
return TRUE;
}
m_bPlaying = TRUE;
if (m_nVideoIndex >= 0)
{
m_pVideoQueue = hqCreate(30, sizeof(HTPACKET), HQ_PUT_WAIT | HQ_GET_WAIT);
m_hVideoThread = sys_os_create_thread((void *)::videoThread, this);
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
playVideo(codecpar->extradata, codecpar->extradata_size, 0, 0);
}
}
}
if (m_nAudioIndex >= 0)
{
m_pAudioQueue = hqCreate(30, sizeof(HTPACKET), HQ_PUT_WAIT | HQ_GET_WAIT);
m_hAudioThread = sys_os_create_thread((void *)::audioThread, this);
}
m_hReadThread = sys_os_create_thread((void *)::readThread, this);
// Start the video decoder thread — required for getImage() to return frames.
// Without this, the decoder stays stopped and g_frameQueue remains empty.
// (Matches CRtspPlayer::play() which calls StartVideoDecoder() after rtsp_start().)
CVideoPlayer::StartVideoDecoder();
return TRUE;
}
void CFilePlayer::stop()
{
close();
}
BOOL CFilePlayer::pause()
{
m_bPaused = TRUE;
return m_bPaused;
}
BOOL CFilePlayer::seek(int pos)
{
if (pos < 0 || pos > 100)
{
return FALSE;
}
m_bSeek = 1;
m_dSeekPos = m_nDuration / 100 * pos;
return TRUE;
}
void CFilePlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CFilePlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
int CFilePlayer::getVideoCodec()
{
int codec = VIDEO_CODEC_NONE;
if (m_nVideoIndex >= 0 && m_pFormatContext)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
codec = to_video_codec(codecpar->codec_id);
}
return codec;
}
int CFilePlayer::getAudioCodec()
{
int codec = AUDIO_CODEC_NONE;
if (m_nAudioIndex >= 0 && m_pFormatContext)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
codec = to_audio_codec(codecpar->codec_id);
}
return codec;
}
void CFilePlayer::videoData(uint8 * data, int size, int64 pts, int waitnext)
{
HTPACKET packet;
packet.data = (uint8 *) malloc(size);
if (packet.data)
{
memcpy(packet.data, data, size);
packet.size = size;
packet.ts = pts;
packet.waitnext = waitnext;
if (!hqBufPut(m_pVideoQueue, (char *)&packet))
{
free(packet.data);
}
}
}
void CFilePlayer::audioData(uint8 * data, int size, int64 pts)
{
HTPACKET packet;
packet.data = (uint8 *) malloc(size);
if (packet.data)
{
memcpy(packet.data, data, size);
packet.size = size;
packet.ts = pts;
packet.waitnext = 0;
if (!hqBufPut(m_pAudioQueue, (char *)&packet))
{
free(packet.data);
}
}
}
BOOL CFilePlayer::readFrame()
{
int rret = 0;
AVPacket pkt;
if (NULL == m_pFormatContext)
{
return FALSE;
}
av_init_packet(&pkt);
pkt.data = 0;
pkt.size = 0;
rret = av_read_frame(m_pFormatContext, &pkt);
if (AVERROR_EOF == rret)
{
rret = av_seek_frame(m_pFormatContext, 0, m_pFormatContext->streams[0]->start_time, 0);
if (rret < 0)
{
rret = av_seek_frame(m_pFormatContext, 0, 0, AVSEEK_FLAG_BYTE | AVSEEK_FLAG_BACKWARD);
if (rret < 0)
{
return FALSE;
}
}
if (av_read_frame(m_pFormatContext, &pkt) != 0)
{
return FALSE;
}
}
else if (0 != rret)
{
return FALSE;
}
int64 pts = AV_NOPTS_VALUE;
if (pkt.pts != AV_NOPTS_VALUE)
{
pts = pkt.pts;
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
pts -= m_pFormatContext->start_time;
}
}
else if (pkt.dts != AV_NOPTS_VALUE)
{
pts = pkt.dts;
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
pts -= m_pFormatContext->start_time;
}
}
if (pkt.stream_index == m_nVideoIndex)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (m_nNalLength)
{
uint8 * data = pkt.data;
int size = pkt.size;
while (pkt.size >= m_nNalLength)
{
int len = 0;
int nal_length = m_nNalLength;
uint8 * pdata = pkt.data;
while (nal_length--)
{
len = (len << 8) | *pdata++;
}
if (len > pkt.size - m_nNalLength || len <= 0)
{
log_print(HT_LOG_DBG, "len=%d, pkt.size=%d\r\n", len, pkt.size);
break;
}
nal_length = m_nNalLength;
pkt.data[nal_length-1] = 1;
nal_length--;
while (nal_length--)
{
pkt.data[nal_length] = 0;
}
pkt.data += len + m_nNalLength;
pkt.size -= len + m_nNalLength;
}
videoData(data, size, pts, 1);
}
else if (pkt.data[0] == 0 && pkt.data[1] == 0 && pkt.data[2] == 0 && pkt.data[3] == 1)
{
videoData(pkt.data, pkt.size, pts, 1);
}
else if (pkt.data[0] == 0 && pkt.data[1] == 0 && pkt.data[2] == 1)
{
videoData(pkt.data, pkt.size, pts, 1);
}
else
{
log_print(HT_LOG_ERR, "%s, unknown format\r\n", __FUNCTION__);
}
}
else
{
videoData(pkt.data, pkt.size, pts, 1);
}
}
else if (pkt.stream_index == m_nAudioIndex)
{
audioData(pkt.data, pkt.size, pts);
}
av_packet_unref(&pkt);
return TRUE;
}
void CFilePlayer::readThread()
{
while (m_bPlaying)
{
if (m_bPaused)
{
usleep(100*1000);
continue;
}
if (m_bSeek)
{
if (seekStream(m_dSeekPos))
{
clearQueue(m_pVideoQueue);
clearQueue(m_pAudioQueue);
}
m_bSeek = 0;
}
if (!readFrame())
{
break;
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hReadThread = 0;
}
void CFilePlayer::videoThread()
{
HTPACKET packet;
int64 pts = 0;
int64 cur_delay = 0;
int64 pre_delay = 0;
uint32 cur_time = 0;
uint32 pre_time = 0;
int timeout = 1000000.0 / getFramerate();
while (m_bPlaying)
{
if (m_bPaused)
{
pre_time = 0;
usleep(100*1000);
continue;
}
if (hqBufGet(m_pVideoQueue, (char *)&packet))
{
if (NULL == packet.data || 0 == packet.size)
{
break;
}
if (packet.ts != AV_NOPTS_VALUE)
{
AVRational q = {1, AV_TIME_BASE};
pts = av_rescale_q (packet.ts, m_pFormatContext->streams[m_nVideoIndex]->time_base, q);
pts /= 1000;
m_nCurPos = pts;
}
playVideo(packet.data, packet.size, pts, 0);
free(packet.data);
if (packet.waitnext)
{
cur_time = sys_os_get_ms();
cur_delay = timeout;
if (pre_time > 0)
{
cur_delay += pre_delay - (cur_time - pre_time) * 1000;
if (cur_delay < 1000)
{
cur_delay = 0;
}
}
pre_time = cur_time;
pre_delay = cur_delay;
if (cur_delay > 0)
{
usleep(cur_delay);
}
}
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hVideoThread = 0;
}
void CFilePlayer::audioThread()
{
HTPACKET packet;
int64 pts = 0;
while (m_bPlaying)
{
if (m_bPaused)
{
usleep(100*1000);
continue;
}
if (hqBufGet(m_pAudioQueue, (char *)&packet))
{
if (NULL == packet.data || 0 == packet.size)
{
break;
}
if (packet.ts != AV_NOPTS_VALUE)
{
AVRational q = {1, AV_TIME_BASE};
pts = av_rescale_q (packet.ts, m_pFormatContext->streams[m_nAudioIndex]->time_base, q);
pts /= 1000;
m_nCurPos = pts;
}
playAudio(packet.data, packet.size, pts, 0);
free(packet.data);
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hAudioThread = 0;
}
void CFilePlayer::clearQueue(HQUEUE * queue)
{
HTPACKET packet;
while (!hqBufIsEmpty(queue))
{
if (hqBufGet(queue, (char *)&packet))
{
if (packet.data != NULL && packet.size != 0)
{
free(packet.data);
}
}
else
{
// should be not to here
log_print(HT_LOG_ERR, "%s, hqBufGet failed\r\n", __FUNCTION__);
break;
}
}
}
BOOL CFilePlayer::seekStream(double pos)
{
if (pos < 0)
{
return FALSE;
}
if (pos == m_nCurPos)
{
return TRUE;
}
int stream = -1;
int64 seekpos = pos * 1000;
if (m_nAudioIndex >= 0)
{
stream = m_nAudioIndex;
}
else if (m_nVideoIndex >= 0)
{
stream = m_nVideoIndex;
}
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
seekpos += m_pFormatContext->start_time;
}
if (stream >= 0)
{
AVRational q = {1, AV_TIME_BASE};
seekpos = av_rescale_q(seekpos, q, m_pFormatContext->streams[stream]->time_base);
}
if (av_seek_frame(m_pFormatContext, stream, seekpos, AVSEEK_FLAG_BACKWARD) < 0)
{
return FALSE;
}
// Accurate seek to the specified position
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = 0;
pkt.size = 0;
while (av_read_frame(m_pFormatContext, &pkt) == 0)
{
if (pkt.stream_index != stream)
{
av_packet_unref(&pkt);
continue;
}
if (pkt.pts != AV_NOPTS_VALUE)
{
if (pkt.pts < seekpos)
{
av_packet_unref(&pkt);
continue;
}
else
{
break;
}
}
else if (pkt.dts != AV_NOPTS_VALUE)
{
if (pkt.dts < seekpos)
{
av_packet_unref(&pkt);
continue;
}
else
{
break;
}
}
else
{
break;
}
av_packet_unref(&pkt);
}
av_packet_unref(&pkt);
m_nCurPos = pos;
return TRUE;
}
double CFilePlayer::getFramerate()
{
double framerate = 25;
if (m_nVideoIndex != -1)
{
if (m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.den > 0)
{
framerate = m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.num /
(double)m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.den;
}
if ((framerate < 1 || framerate > 60) &&
m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.den > 0)
{
framerate = m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.num /
(double)m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.den;
}
if (framerate < 1 || framerate > 60)
{
framerate = 25;
}
}
return framerate;
}
BOOL CFilePlayer::onRecord()
{
AVICTX * p_avictx = m_pAviCtx;
int vcodec = getVideoCodec();
int v_extra_len = 0;
uint8 * v_extra = NULL;
if (VIDEO_CODEC_H264 == vcodec)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
v_extra = codecpar->extradata;
v_extra_len = codecpar->extradata_size;
avi_set_video_info(p_avictx, 0, 0, 0, "H264");
avi_set_video_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
}
else if (VIDEO_CODEC_H265 == vcodec)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
v_extra = codecpar->extradata;
v_extra_len = codecpar->extradata_size;
avi_set_video_info(p_avictx, 0, 0, 0, "H265");
avi_set_video_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
}
else if (VIDEO_CODEC_JPEG == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "JPEG");
}
else if (VIDEO_CODEC_MP4 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "MP4V");
}
int acodec = getAudioCodec();
int sr = 0;
int ch = 0;
if (m_nAudioIndex >= 0 && m_pFormatContext)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
sr = codecpar->sample_rate;
ch = codecpar->channels;
}
if (AUDIO_CODEC_G711A == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
}
else if (AUDIO_CODEC_G711U == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
}
else if (AUDIO_CODEC_G726 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
}
else if (AUDIO_CODEC_G722 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
}
else if (AUDIO_CODEC_AAC == acodec)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
avi_set_audio_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
}
avi_update_header(p_avictx);
if (p_avictx->ctxf_video)
{
if (v_extra && v_extra_len > 0)
{
recordVideo(v_extra, v_extra_len, 0, 0);
}
}
return TRUE;
}

View File

@@ -0,0 +1,95 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef FILE_PLAYER_H
#define FILE_PLAYER_H
#include "video_player.h"
#define FILE_EVE_STOPPED 80 // stopped
#define FILE_EVE_CONNECTING 81 // connecting
#define FILE_EVE_CONNFAIL 82 // connect failed
#define FILE_EVE_CONNSUCC 83 // connect success
#define FILE_EVE_NOSIGNAL 84 // no signal
#define FILE_EVE_RESUME 85 // resume
#define FILE_EVE_AUTHFAILED 86 // authentication failed
#define FILE_EVE_NODATA 87 // No data received within the timeout period
typedef struct
{
uint8 * data;
int size;
int64 ts;
int waitnext;
} HTPACKET;
class CFilePlayer : public CVideoPlayer
{
public:
CFilePlayer();
virtual ~CFilePlayer();
BOOL open(std::string fileName);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int64 getElapse() {return m_nCurPos;};
int64 getDuration() {return m_nDuration;}
int getVideoCodec();
int getAudioCodec();
BOOL onRecord();
void setBbox(cv::Rect bbox);
void setCrop(bool crop);;
void readThread();
void videoThread();
void audioThread();
private:
BOOL openFile(const char * filename);
BOOL readFrame();
void videoData(uint8 * data, int size, int64 pts, int waitnext);
void audioData(uint8 * data, int size, int64 pts);
void clearQueue(HQUEUE * queue);
BOOL seekStream(double pos);
double getFramerate();
private:
int m_nAudioIndex; // audio stream index
int m_nVideoIndex; // video stream index
int64 m_nDuration; // the stream duration, unit is millisecond
int64 m_nCurPos; // the current play position, unit is millisecond
int m_bSeek; // seek request
double m_dSeekPos; // seek position, nit is millisecond
int m_nNalLength;
AVFormatContext * m_pFormatContext; // format context
pthread_t m_hReadThread; // read thread
pthread_t m_hVideoThread; // video thread
pthread_t m_hAudioThread; // audio thread
HQUEUE * m_pVideoQueue; // video queue
HQUEUE * m_pAudioQueue; // audio queue
};
#endif

View File

@@ -0,0 +1,45 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef FORMAT_H
#define FORMAT_H
#define PACKET_TYPE_UNKNOW -1
#define PACKET_TYPE_VIDEO 0
#define PACKET_TYPE_AUDIO 1
#define AUDIO_FORMAT_PCM (0x0001)
#define AUDIO_FORMAT_G726 (0x0064)
#define AUDIO_FORMAT_G722 (0x028F)
#define AUDIO_FORMAT_MP3 (0x0055)
#define AUDIO_FORMAT_AAC (0x00FF)
#define AUDIO_FORMAT_ALAW (0x0006)
#define AUDIO_FORMAT_MULAW (0x0007)
#define AUDIO_FORMAT_OPUS (0x00AD)
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) | \
((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 ))
#define mmioFOURCC(ch0, ch1, ch2, ch3) MAKEFOURCC(ch0, ch1, ch2, ch3)
#endif
#endif

View File

@@ -0,0 +1,94 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "gles_engine.h"
#include <SLES/OpenSLES_Android.h>
#define CheckError(message) if (result != SL_RESULT_SUCCESS) { log_print(HT_LOG_ERR, "%s\r\n", message); return; }
static GlesEngine * g_glesEngine;
GlesEngine::GlesEngine()
: m_engineObject(0)
, m_engine(0)
{
SLresult result;
result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0);
CheckError("Failed to create engine");
result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE);
CheckError("Failed to realize engine");
result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine);
CheckError("Failed to get engine interface");
}
GlesEngine::~GlesEngine()
{
if (m_engineObject)
{
(*m_engineObject)->Destroy(m_engineObject);
}
}
GlesEngine *GlesEngine::instance()
{
if (NULL == g_glesEngine)
{
g_glesEngine = new GlesEngine();
}
return g_glesEngine;
}
bool GlesEngine::inputFormatIsSupported(SLDataFormat_PCM format)
{
SLresult result;
SLObjectItf recorder = 0;
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
SLDataSource audioSrc = { &loc_dev, NULL };
#ifdef ANDROID
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
#else
SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 };
#endif
SLDataSink audioSnk = { &loc_bq, &format };
result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0);
if (result == SL_RESULT_SUCCESS)
{
result = (*recorder)->Realize(recorder, false);
}
if (result == SL_RESULT_SUCCESS)
{
(*recorder)->Destroy(recorder);
return true;
}
return false;
}

View File

@@ -0,0 +1,43 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _GLES_ENGINE_H_
#define _GLES_ENGINE_H_
#include <SLES/OpenSLES.h>
class GlesEngine
{
public:
GlesEngine();
~GlesEngine();
static GlesEngine *instance();
SLEngineItf slEngine() const { return m_engine; }
bool inputFormatIsSupported(SLDataFormat_PCM format);
SLObjectItf m_engineObject;
SLEngineItf m_engine;
};
#endif

View File

@@ -0,0 +1,325 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "gles_input.h"
#include "gles_engine.h"
#ifdef ANDROID
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif
#ifdef ANDROID
static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
#else
static void bufferQueueCallback(SLBufferQueueItf, void *context)
#endif
{
GlesInput *audioInput = reinterpret_cast<GlesInput*>(context);
audioInput->processBuffer();
}
GlesInput::GlesInput()
: m_recorderObject(0)
, m_recorder(0)
, m_bufferQueue(0)
, m_bufferSize(1600)
, m_currentBuffer(0)
, m_pCallback(NULL)
, m_pUserdata(NULL)
{
#ifdef ANDROID
m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
#endif
for (int i = 0; i < NUM_BUFFERS; i++)
{
m_buffers[i] = NULL;
}
}
GlesInput::~GlesInput()
{
stop();
}
bool GlesInput::start(int samplerete)
{
bool ret = false;
if (startRecording(samplerete))
{
ret = true;
}
else
{
log_print(HT_LOG_ERR, "start audio recoding faild\r\n");
stopRecording();
}
return ret;
}
void GlesInput::stop()
{
stopRecording();
}
bool GlesInput::startRecording(int samplerete)
{
SLEngineItf engine = GlesEngine::instance()->slEngine();
if (!engine)
{
log_print(HT_LOG_ERR, "No engine\r\n");
return false;
}
SLresult result;
// configure audio source
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
SLDataSource audioSrc = { &loc_dev, NULL };
// configure audio sink
#ifdef ANDROID
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
NUM_BUFFERS };
#else
SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
#endif
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = 2;
format_pcm.samplesPerSec = samplerete * 1000;
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
format_pcm.channelMask = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
SLDataSink audioSnk = { &loc_bq, &format_pcm };
// create audio recorder
// (requires the RECORD_AUDIO permission)
#ifdef ANDROID
const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
#else
const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE };
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
#endif
result = (*engine)->CreateAudioRecorder(engine, &m_recorderObject,
&audioSrc, &audioSnk,
sizeof(req) / sizeof(SLboolean), id, req);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "create audio recorder failed\r\n");
return false;
}
#ifdef ANDROID
// configure recorder source
SLAndroidConfigurationItf configItf;
result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION,
&configItf);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "get audio configuration interface failed\r\n");
return false;
}
result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
&m_recorderPreset, sizeof(SLuint32));
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE;
SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big
result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
&presetSize, (void*)&presetValue);
if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE)
{
log_print(HT_LOG_ERR, "set audio record configuration failed\r\n");
return false;
}
#endif
// realize the audio recorder
result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "realize audio record object failed\r\n");
return false;
}
// get the record interface
result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "get audio record object failed\r\n");
return false;
}
// get the buffer queue interface
#ifdef ANDROID
SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
#else
SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE;
#endif
result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "get audio record buff queue failed\r\n");
return false;
}
// register callback on the buffer queue
result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, ::bufferQueueCallback, this);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "set buffer queue callback failed\r\n");
return false;
}
if (m_bufferSize <= 0)
{
m_bufferSize = (16*2/8)*50*samplerete/1000;
}
else
{
int minimumBufSize = (16*2/8)*5*samplerete/1000;
if (m_bufferSize < minimumBufSize)
m_bufferSize = minimumBufSize;
}
// enqueue empty buffers to be filled by the recorder
for (int i = 0; i < NUM_BUFFERS; ++i)
{
m_buffers[i] = (uint8 *)malloc(m_bufferSize);
if (NULL == m_buffers[i])
{
return false;
}
memset(m_buffers[i], 0, m_bufferSize);
result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i], m_bufferSize);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "audio record enqueue failed\r\n");
return false;
}
}
// start recording
result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
if (result != SL_RESULT_SUCCESS)
{
log_print(HT_LOG_ERR, "set audio record state failed\r\n");
return false;
}
return true;
}
void GlesInput::stopRecording()
{
if (m_recorder)
{
(*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED);
}
if (m_bufferQueue)
{
(*m_bufferQueue)->Clear(m_bufferQueue);
}
if (m_recorderObject)
{
(*m_recorderObject)->Destroy(m_recorderObject);
}
m_recorder = NULL;
m_bufferQueue = NULL;
m_recorderObject = NULL;
for (int i = 0; i < NUM_BUFFERS; ++i)
{
if (m_buffers[i])
{
free(m_buffers[i]);
m_buffers[i] = NULL;
}
}
m_currentBuffer = 0;
}
void GlesInput::setBufferSize(int value)
{
m_bufferSize = value;
}
int GlesInput::bufferSize() const
{
return m_bufferSize;
}
void GlesInput::processBuffer()
{
uint8 *processedBuffer = m_buffers[m_currentBuffer];
if (m_pCallback)
{
m_pCallback(processedBuffer, m_bufferSize, m_pUserdata);
}
// Re-enqueue the buffer
SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
processedBuffer,
m_bufferSize);
m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS;
// If the buffer queue is empty (shouldn't happen), stop recording.
#ifdef ANDROID
SLAndroidSimpleBufferQueueState state;
#else
SLBufferQueueState state;
#endif
result = (*m_bufferQueue)->GetState(m_bufferQueue, &state);
if (result != SL_RESULT_SUCCESS || state.count == 0)
{
log_print(HT_LOG_ERR, "processBuffer::state.count == 0\r\n");
}
}
void GlesInput::setCallback(GlesInputCB cb, void * puser)
{
m_pCallback = cb;
m_pUserdata = puser;
}

View File

@@ -0,0 +1,72 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _GLES_INPUT_H_
#define _GLES_INPUT_H_
#include "sys_inc.h"
#include <SLES/OpenSLES.h>
#ifdef ANDROID
#include <SLES/OpenSLES_Android.h>
#endif
#define NUM_BUFFERS 2
typedef void (*GlesInputCB)(uint8 * pdata, int len, void * puser);
class GlesInput
{
public:
GlesInput();
~GlesInput();
bool start(int samplerete);
void stop();
void setCallback(GlesInputCB cb, void * puser);
void setBufferSize(int value);
int bufferSize() const;
void processBuffer();
private:
bool startRecording(int samplerete);
void stopRecording();
private:
SLObjectItf m_recorderObject;
SLRecordItf m_recorder;
#ifdef ANDROID
SLuint32 m_recorderPreset;
SLAndroidSimpleBufferQueueItf m_bufferQueue;
#else
SLBufferQueueItf m_bufferQueue;
#endif
int m_bufferSize;
uint8 * m_buffers[NUM_BUFFERS];
int m_currentBuffer;
GlesInputCB m_pCallback;
void * m_pUserdata;
};
#endif

View File

@@ -0,0 +1,396 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "http_flv_player.h"
#include "utils.h"
/***************************************************************************************/
int http_flv_notify_callback(int evt, void * puser)
{
CHttpFlvPlayer * pPlayer = (CHttpFlvPlayer *) puser;
pPlayer->onNotify(evt);
return 0;
}
int http_flv_audio_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CHttpFlvPlayer * pPlayer = (CHttpFlvPlayer *) puser;
pPlayer->onAudio(pdata, len, ts);
return 0;
}
int http_flv_video_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CHttpFlvPlayer * pPlayer = (CHttpFlvPlayer *) puser;
pPlayer->onVideo(pdata, len, ts);
return 0;
}
/***************************************************************************************/
CHttpFlvPlayer::CHttpFlvPlayer(): CVideoPlayer()
{
memset(m_ip, 0, sizeof(m_ip));
}
CHttpFlvPlayer::~CHttpFlvPlayer()
{
close();
}
std::string CHttpFlvPlayer::createNewFLVUrl(const std::string& username,
const std::string& password,
const std::string& url) {
// Copy the original strings to avoid modifying the originals
std::string modUsername = username;
std::string modPassword = password;
// Replace '@' with '_' in username and password
std::replace(modUsername.begin(), modUsername.end(), '@', '_');
std::replace(modPassword.begin(), modPassword.end(), '@', '_');
// Extract the protocol and rest of the URL
size_t protocolEndPos = url.find("://") + 3; // Find the position right after "://"
std::string protocol = url.substr(0, protocolEndPos); // Include "://"
// Construct the new URL with modified credentials
std::string newUrl = protocol;
newUrl += modUsername + ":" + modPassword + "@";
newUrl += url.substr(protocolEndPos);
return newUrl;
}
BOOL CHttpFlvPlayer::open(std::string fileName)
{
close();
CVideoPlayer::open(fileName);
char host[100];
url_split(fileName.c_str(), NULL, 0, NULL, 0, NULL, 0, host, sizeof(host), NULL, NULL, 0);
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_httpflv.set_notify_cb(http_flv_notify_callback, this);
m_httpflv.set_audio_cb(http_flv_audio_callback);
m_httpflv.set_video_cb(http_flv_video_callback);
return TRUE;
}
BOOL CHttpFlvPlayer::open(std::string _username, std::string _password, std::string _url)
{
close();
CVideoPlayer::open(_username, _password, _url);
char host[100];
std::string fileName = createNewFLVUrl(_username, _password, _url);
url_split(fileName.c_str(), NULL, 0, NULL, 0, NULL, 0, host, sizeof(host), NULL, NULL, 0);
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_httpflv.set_notify_cb(http_flv_notify_callback, this);
m_httpflv.set_audio_cb(http_flv_audio_callback);
m_httpflv.set_video_cb(http_flv_video_callback);
return TRUE;
}
void CHttpFlvPlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CHttpFlvPlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
void CHttpFlvPlayer::close()
{
// stop http-flv connection
m_httpflv.http_flv_stop();
m_httpflv.http_flv_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder();
CVideoPlayer::close();
}
BOOL CHttpFlvPlayer::play()
{
if (m_httpflv.http_flv_start(m_sFileName.c_str(), m_acct.c_str(), m_pass.c_str()))
{
m_bPlaying = TRUE;
m_bPaused = FALSE;
}
CVideoPlayer::StartVideoDecoder();// Start the video decoder
return m_bPlaying;
}
void CHttpFlvPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_httpflv.http_flv_stop();
}
// Set flags BEFORE stopping decoder so rx thread stops calling decode()
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
}
BOOL CHttpFlvPlayer::pause()
{
if (m_bPlaying)
{
if (m_httpflv.http_flv_pause())
{
m_bPaused = TRUE;
m_bPlaying = FALSE;
}
}
else if (m_bPaused)
{
if (m_httpflv.http_flv_play())
{
m_bPaused = FALSE;
m_bPlaying = TRUE;
}
}
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
return m_bPaused;
}
BOOL CHttpFlvPlayer::seek(int pos)
{
return FALSE;
}
int64 CHttpFlvPlayer::getElapse()
{
uint32 cur_ts;
uint32 init_ts;
int frequency;
if (m_videoClock.SyncTime.tv_sec > m_audioClock.SyncTime.tv_sec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = m_httpflv.get_video_init_ts();
frequency = getVideoClock();
}
else if (m_videoClock.SyncTime.tv_sec == m_audioClock.SyncTime.tv_sec &&
m_videoClock.SyncTime.tv_usec > m_audioClock.SyncTime.tv_usec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = m_httpflv.get_video_init_ts();
frequency = getVideoClock();
}
else
{
cur_ts = m_audioClock.SyncTimestamp;
init_ts = m_httpflv.get_audio_init_ts();
frequency = getAudioClock();
}
if (init_ts == 0)
{
return 0;
}
int64 elapse;
int timestampDiff = cur_ts - init_ts;
// Divide this by the timestamp frequency to get real time:
double timeDiff = timestampDiff / (double)frequency;
if (timeDiff >= 0.0)
{
elapse = timeDiff * 1000;
}
else
{
timeDiff = -timeDiff;
elapse = timeDiff * 1000;
}
return elapse;
}
int64 CHttpFlvPlayer::getDuration()
{
return 0;
}
int CHttpFlvPlayer::getVideoCodec()
{
return m_httpflv.video_codec();
}
int CHttpFlvPlayer::getAudioCodec()
{
return m_httpflv.audio_codec();
}
void CHttpFlvPlayer::onNotify(int evt)
{
if (evt == HTTP_FLV_EVE_VIDEOREADY)
{
int videoCodec = m_httpflv.video_codec();
if (VIDEO_CODEC_NONE != videoCodec)
{
openVideo(videoCodec);
}
}
else if (evt == HTTP_FLV_EVE_AUDIOREADY)
{
int audioCodec = m_httpflv.audio_codec();
if (AUDIO_CODEC_NONE != audioCodec)
{
openAudio(audioCodec, m_httpflv.get_audio_samplerate(), m_httpflv.get_audio_channels());
}
}
}
void CHttpFlvPlayer::onAudio(uint8 * pdata, int len, uint32 ts)
{
playAudio(pdata, len, ts, 0);
}
void CHttpFlvPlayer::onVideo(uint8 * pdata, int len, uint32 ts)
{
playVideo(pdata, len, ts, 0);
}
BOOL CHttpFlvPlayer::onRecord()
{
CHttpFlvClient * p_httpflv = &m_httpflv;
AVICTX * p_avictx = m_pAviCtx;
int vcodec = p_httpflv->video_codec();
int fps = p_httpflv->get_video_framerate();
int v_extra_len = 0;
uint8 v_extra[1024];
if (VIDEO_CODEC_H264 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "H264");
uint8 sps[512], pps[512];
int sps_len = 0, pps_len = 0;
if (p_httpflv->get_h264_params(sps, &sps_len, pps, &pps_len))
{
memcpy(v_extra, sps, sps_len);
memcpy(v_extra + sps_len, pps, pps_len);
v_extra_len = sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_H265 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "H265");
uint8 vps[512], sps[512], pps[512];
int vps_len = 0, sps_len = 0, pps_len = 0;
if (p_httpflv->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
memcpy(v_extra, vps, vps_len);
memcpy(v_extra + vps_len, sps, sps_len);
memcpy(v_extra + vps_len + sps_len, pps, pps_len);
v_extra_len = vps_len + sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_JPEG == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "JPEG");
}
else if (VIDEO_CODEC_MP4 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "MP4V");
}
int acodec = p_httpflv->audio_codec();
int sr = p_httpflv->get_audio_samplerate();
int ch = p_httpflv->get_audio_channels();
if (AUDIO_CODEC_G711A == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
}
else if (AUDIO_CODEC_G711U == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
}
else if (AUDIO_CODEC_G726 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
}
else if (AUDIO_CODEC_G722 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
}
else if (AUDIO_CODEC_AAC == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
avi_set_audio_extra_info(p_avictx, p_httpflv->get_audio_config(), p_httpflv->get_audio_config_len());
}
avi_update_header(p_avictx);
if (p_avictx->ctxf_video)
{
if (v_extra_len > 0)
{
recordVideo(v_extra, v_extra_len, 0, 0);
}
}
return TRUE;
}

View File

@@ -0,0 +1,63 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef HTTP_FLV_PLAYER_H
#define HTTP_FLV_PLAYER_H
#include "video_player.h"
#include "http_flv_cln.h"
class CHttpFlvPlayer : public CVideoPlayer
{
public:
CHttpFlvPlayer();
virtual ~CHttpFlvPlayer();
BOOL open(std::string fileName);
BOOL open(std::string _username, std::string _password, std::string _url);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int64 getElapse();
int64 getDuration();
int getVideoCodec();
int getAudioCodec();
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
void onNotify(int evt);
void onAudio(uint8 * pdata, int len, uint32 ts);
void onVideo(uint8 * pdata, int len, uint32 ts);
BOOL onRecord();
private:
char m_ip[32];
CHttpFlvClient m_httpflv;
std::string createNewFLVUrl(const std::string& username,
const std::string& password,
const std::string& url);
};
#endif

View File

@@ -0,0 +1,204 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "http_mjpeg_player.h"
#include "utils.h"
/***************************************************************************************/
int http_mjpeg_notify_callback(int evt, void * puser)
{
CHttpMjpegPlayer * pPlayer = (CHttpMjpegPlayer *) puser;
pPlayer->onNotify(evt);
return 0;
}
int http_mjpeg_video_callback(uint8 * pdata, int len, void *puser)
{
CHttpMjpegPlayer * pPlayer = (CHttpMjpegPlayer *) puser;
pPlayer->onVideo(pdata, len, 0);
return 0;
}
/***************************************************************************************/
CHttpMjpegPlayer::CHttpMjpegPlayer(): CVideoPlayer()
{
memset(m_ip, 0, sizeof(m_ip));
}
CHttpMjpegPlayer::~CHttpMjpegPlayer()
{
close();
}
std::string CHttpMjpegPlayer::createNewMJPEGUrl(const std::string& username,
const std::string& password,
const std::string& url) {
// Copy the original strings to avoid modifying the originals
std::string modUsername = username;
std::string modPassword = password;
// Replace '@' with '_' in username and password
std::replace(modUsername.begin(), modUsername.end(), '@', '_');
std::replace(modPassword.begin(), modPassword.end(), '@', '_');
// Extract the protocol and rest of the URL
size_t protocolEndPos = url.find("://") + 3; // Find the position right after "://"
std::string protocol = url.substr(0, protocolEndPos); // Include "://"
// Construct the new URL with modified credentials
std::string newUrl = protocol;
newUrl += modUsername + ":" + modPassword + "@";
newUrl += url.substr(protocolEndPos);
return newUrl;
}
BOOL CHttpMjpegPlayer::open(std::string fileName)
{
close();
CVideoPlayer::open(fileName);
char host[100];
url_split(fileName.c_str(), NULL, 0, NULL, 0, NULL, 0, host, sizeof(host), NULL, NULL, 0);
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_httpmjpeg.set_notify_cb(http_mjpeg_notify_callback, this);
m_httpmjpeg.set_video_cb(http_mjpeg_video_callback);
return TRUE;
}
BOOL CHttpMjpegPlayer::open(std::string _username, std::string _password, std::string _url)
{
close();
CVideoPlayer::open(_username, _password, _url);
char host[100];
std::string fileName = createNewMJPEGUrl(_username, _password, _url);
url_split(fileName.c_str(), NULL, 0, NULL, 0, NULL, 0, host, sizeof(host), NULL, NULL, 0);
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_httpmjpeg.set_notify_cb(http_mjpeg_notify_callback, this);
m_httpmjpeg.set_video_cb(http_mjpeg_video_callback);
return TRUE;
}
void CHttpMjpegPlayer::close()
{
// stop http-mjpeg connection
m_httpmjpeg.mjpeg_stop();
m_httpmjpeg.mjpeg_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder();
CVideoPlayer::close();
}
void CHttpMjpegPlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CHttpMjpegPlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
BOOL CHttpMjpegPlayer::play()
{
if (m_httpmjpeg.mjpeg_start(m_sFileName.c_str(), m_acct.c_str(), m_pass.c_str()))
{
m_bPlaying = TRUE;
m_bPaused = FALSE;
}
CVideoPlayer::StartVideoDecoder();// Start the video decoder
return m_bPlaying;
}
void CHttpMjpegPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_httpmjpeg.mjpeg_stop();
}
// Set flags BEFORE stopping decoder so rx thread stops calling decode()
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
}
BOOL CHttpMjpegPlayer::pause()
{
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
return m_bPaused;
}
BOOL CHttpMjpegPlayer::seek(int pos)
{
return FALSE;
}
int CHttpMjpegPlayer::getVideoCodec()
{
return VIDEO_CODEC_JPEG;
}
void CHttpMjpegPlayer::onNotify(int evt)
{
if (evt == MJPEG_EVE_CONNSUCC)
{
openVideo(VIDEO_CODEC_JPEG);
}
}
void CHttpMjpegPlayer::onVideo(uint8 * pdata, int len, uint32 ts)
{
playVideo(pdata, len, ts, 0);
}
BOOL CHttpMjpegPlayer::onRecord()
{
avi_set_video_info(m_pAviCtx, 0, 0, 0, "JPEG");
avi_update_header(m_pAviCtx);
return TRUE;
}

View File

@@ -0,0 +1,59 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef HTTP_MJPEG_PLAYER_H
#define HTTP_MJPEG_PLAYER_H
#include "video_player.h"
#include "http_mjpeg_cln.h"
class CHttpMjpegPlayer : public CVideoPlayer
{
public:
CHttpMjpegPlayer();
virtual ~CHttpMjpegPlayer();
BOOL open(std::string fileName);
BOOL open(std::string _username, std::string _password, std::string _url);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int getVideoCodec();
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
void onNotify(int evt);
void onVideo(uint8 * pdata, int len, uint32 ts);
BOOL onRecord();
private:
char m_ip[32];
CHttpMjpeg m_httpmjpeg;
std::string createNewMJPEGUrl(const std::string& username,
const std::string& password,
const std::string& url);
};
#endif

37
MediaClient/media/lock.h Normal file
View File

@@ -0,0 +1,37 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _LOCK_H
#define _LOCK_H
#include "sys_inc.h"
class CLock
{
public:
CLock(void * pMutex) : m_pMutex(pMutex) {sys_os_mutex_enter(m_pMutex);}
~CLock() {sys_os_mutex_leave(m_pMutex);}
private:
void * m_pMutex;
};
#endif // _LOCK_H

View File

@@ -0,0 +1,215 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "media_codec.h"
#include "media_format.h"
AVCodecID to_audio_avcodecid(int codec)
{
AVCodecID codecid = AV_CODEC_ID_NONE;
switch (codec)
{
case AUDIO_CODEC_G711A:
codecid = AV_CODEC_ID_PCM_ALAW;
break;
case AUDIO_CODEC_G711U:
codecid = AV_CODEC_ID_PCM_MULAW;
break;
case AUDIO_CODEC_G726:
codecid = AV_CODEC_ID_ADPCM_G726;
break;
case AUDIO_CODEC_G722:
codecid = AV_CODEC_ID_ADPCM_G722;
break;
case AUDIO_CODEC_OPUS:
codecid = AV_CODEC_ID_OPUS;
break;
case AUDIO_CODEC_AAC:
codecid = AV_CODEC_ID_AAC;
break;
}
return codecid;
}
AVCodecID to_video_avcodecid(int codec)
{
AVCodecID codecid = AV_CODEC_ID_NONE;
switch (codec)
{
case VIDEO_CODEC_H264:
codecid = AV_CODEC_ID_H264;
break;
case VIDEO_CODEC_H265:
codecid = AV_CODEC_ID_HEVC;
break;
case VIDEO_CODEC_MP4:
codecid = AV_CODEC_ID_MPEG4;
break;
case VIDEO_CODEC_JPEG:
codecid = AV_CODEC_ID_MJPEG;
break;
}
return codecid;
}
int to_audio_codec(AVCodecID codecid)
{
int codec = AUDIO_CODEC_NONE;
switch (codecid)
{
case AV_CODEC_ID_PCM_ALAW:
codec = AUDIO_CODEC_G711A;
break;
case AV_CODEC_ID_PCM_MULAW:
codec = AUDIO_CODEC_G711U;
break;
case AV_CODEC_ID_ADPCM_G726:
codec = AUDIO_CODEC_G726;
break;
case AV_CODEC_ID_ADPCM_G722:
codec = AUDIO_CODEC_G722;
break;
case AV_CODEC_ID_OPUS:
codec = AUDIO_CODEC_OPUS;
break;
case AV_CODEC_ID_AAC:
codec = AUDIO_CODEC_AAC;
break;
default:
break;
}
return codec;
}
int to_video_codec(AVCodecID codecid)
{
int codec = VIDEO_CODEC_NONE;
switch (codecid)
{
case AV_CODEC_ID_H264:
codec = VIDEO_CODEC_H264;
break;
case AV_CODEC_ID_HEVC:
codec = VIDEO_CODEC_H265;
break;
case AV_CODEC_ID_MPEG4:
codec = VIDEO_CODEC_MP4;
break;
case AV_CODEC_ID_MJPEG:
codec = VIDEO_CODEC_JPEG;
break;
default:
break;
}
return codec;
}
AVPixelFormat to_avpixelformat(int fmt)
{
AVPixelFormat pixfmt = AV_PIX_FMT_NONE;
if (VIDEO_FMT_BGR24 == fmt)
{
pixfmt = AV_PIX_FMT_BGR24;
}
else if (VIDEO_FMT_YUV420P == fmt)
{
pixfmt = AV_PIX_FMT_YUV420P;
}
else if (VIDEO_FMT_YUYV422 == fmt)
{
pixfmt = AV_PIX_FMT_YUYV422;
}
else if (VIDEO_FMT_YVYU422 == fmt)
{
pixfmt = AV_PIX_FMT_YVYU422;
}
else if (VIDEO_FMT_UYVY422 == fmt)
{
pixfmt = AV_PIX_FMT_UYVY422;
}
else if (VIDEO_FMT_NV12 == fmt)
{
pixfmt = AV_PIX_FMT_NV12;
}
else if (VIDEO_FMT_NV21 == fmt)
{
pixfmt = AV_PIX_FMT_NV21;
}
else if (VIDEO_FMT_RGB24 == fmt)
{
pixfmt = AV_PIX_FMT_RGB24;
}
else if (VIDEO_FMT_RGB32 == fmt)
{
pixfmt = AV_PIX_FMT_RGB32;
}
else if (VIDEO_FMT_ARGB == fmt)
{
pixfmt = AV_PIX_FMT_ARGB;
}
else if (VIDEO_FMT_BGRA == fmt)
{
pixfmt = AV_PIX_FMT_BGRA;
}
else if (VIDEO_FMT_YV12 == fmt)
{
pixfmt = AV_PIX_FMT_YUV420P;
}
else if (VIDEO_FMT_BGR32 == fmt)
{
pixfmt = AV_PIX_FMT_BGR32;
}
else if (VIDEO_FMT_YUV422P == fmt)
{
pixfmt = AV_PIX_FMT_YUV422P;
}
return pixfmt;
}

View File

@@ -0,0 +1,43 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef MEDIA_CODEC_H
#define MEDIA_CODEC_H
extern "C" {
#include <libavcodec/avcodec.h>
}
#ifdef __cplusplus
extern "C" {
#endif
AVCodecID to_audio_avcodecid(int codec);
AVCodecID to_video_avcodecid(int codec);
int to_audio_codec(AVCodecID codecid);
int to_video_codec(AVCodecID codecid);
AVPixelFormat to_avpixelformat(int fmt);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,64 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef _MEDIA_FORMAT_H
#define _MEDIA_FORMAT_H
// video pixel format
#define VIDEO_FMT_NONE 0
#define VIDEO_FMT_YUYV422 1
#define VIDEO_FMT_YUV420P 2
#define VIDEO_FMT_YVYU422 3
#define VIDEO_FMT_UYVY422 4
#define VIDEO_FMT_NV12 5
#define VIDEO_FMT_NV21 6
#define VIDEO_FMT_RGB24 7
#define VIDEO_FMT_RGB32 8
#define VIDEO_FMT_ARGB 9
#define VIDEO_FMT_BGRA 10
#define VIDEO_FMT_YV12 11
#define VIDEO_FMT_BGR24 12
#define VIDEO_FMT_BGR32 13
#define VIDEO_FMT_YUV422P 14
#define VIDEO_FMT_MJPG 15
// video codec
#define VIDEO_CODEC_NONE 0
#define VIDEO_CODEC_H264 1
#define VIDEO_CODEC_MP4 2
#define VIDEO_CODEC_JPEG 3
#define VIDEO_CODEC_H265 4
// audio codec
#define AUDIO_CODEC_NONE 0
#define AUDIO_CODEC_G711A 1
#define AUDIO_CODEC_G711U 2
#define AUDIO_CODEC_G726 3
#define AUDIO_CODEC_AAC 4
#define AUDIO_CODEC_G722 5
#define AUDIO_CODEC_OPUS 6
#define DATA_TYPE_VIDEO 0
#define DATA_TYPE_AUDIO 1
#define DATA_TYPE_METADATA 2
#endif // _MEDIA_FORMAT_H

View File

@@ -0,0 +1,259 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "media_parse.h"
#include "media_format.h"
#include "media_util.h"
#include "h264.h"
#include "h265.h"
#include "mjpeg.h"
#include "h264_util.h"
#include "h265_util.h"
#include "bs.h"
#include <math.h>
int avc_parse_video_size(int codec, uint8 * p_data, uint32 len, int * width, int * height)
{
uint8 nalu_t;
if (p_data == NULL || len < 5)
{
return -1;
}
// Need to parse width X height
if (VIDEO_CODEC_H264 == codec)
{
int s_len = 0, n_len = 0, parse_len = len;
uint8 * p_cur = p_data;
uint8 * p_end = p_data + len;
while (p_cur && p_cur < p_end && parse_len > 0)
{
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
nalu_t = (p_cur[s_len] & 0x1F);
int b_start;
nal_t nal;
nal.i_payload = n_len-s_len-1;
nal.p_payload = p_cur+s_len+1;
nal.i_type = nalu_t;
if (nalu_t == H264_NAL_SPS)
{
h264_t parse;
h264_parser_init(&parse);
h264_parser_parse(&parse, &nal, &b_start);
log_print(HT_LOG_INFO, "%s, H264 width[%d],height[%d]\r\n", __FUNCTION__, parse.i_width, parse.i_height);
*width = parse.i_width;
*height = parse.i_height;
return 0;
}
parse_len -= n_len;
p_cur = p_next;
}
}
else if (VIDEO_CODEC_H265 == codec)
{
int s_len, n_len = 0, parse_len = len;
uint8 * p_cur = p_data;
uint8 * p_end = p_data + len;
while (p_cur && p_cur < p_end && parse_len > 0)
{
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
nalu_t = (p_cur[s_len] >> 1) & 0x3F;
if (nalu_t == HEVC_NAL_SPS)
{
h265_t parse;
h265_parser_init(&parse);
if (h265_parser_parse(&parse, p_cur+s_len, n_len-s_len) == 0)
{
log_print(HT_LOG_INFO, "%s, H265 width[%d],height[%d]\r\n", __FUNCTION__, parse.pic_width_in_luma_samples, parse.pic_height_in_luma_samples);
*width = parse.pic_width_in_luma_samples;
*height = parse.pic_height_in_luma_samples;
return 0;
}
}
parse_len -= n_len;
p_cur = p_next;
}
}
else if (VIDEO_CODEC_JPEG == codec)
{
uint32 offset = 0;
int size_chunk = 0;
while (offset < len - 8 && p_data[offset] == 0xFF)
{
if (p_data[offset+1] == MARKER_SOF0)
{
int h = ((p_data[offset+5] << 8) | p_data[offset+6]);
int w = ((p_data[offset+7] << 8) | p_data[offset+8]);
log_print(HT_LOG_INFO, "%s, MJPEG width[%d],height[%d]\r\n", __FUNCTION__, w, h);
*width = w;
*height = h;
break;
}
else if (p_data[offset+1] == MARKER_SOI)
{
offset += 2;
}
else
{
size_chunk = ((p_data[offset+2] << 8) | p_data[offset+3]);
offset += 2 + size_chunk;
}
}
}
else if (VIDEO_CODEC_MP4 == codec)
{
uint32 pos = 0;
int vol_f = 0;
int vol_pos = 0;
int vol_len = 0;
while (pos < len - 4)
{
if (p_data[pos] == 0 && p_data[pos+1] == 0 && p_data[pos+2] == 1)
{
if (p_data[pos+3] >= 0x20 && p_data[pos+3] <= 0x2F)
{
vol_f = 1;
vol_pos = pos+4;
}
else if (vol_f)
{
vol_len = pos - vol_pos;
break;
}
}
pos++;
}
if (!vol_f)
{
return 0;
}
else if (vol_len <= 0)
{
vol_len = len - vol_pos;
}
int vo_ver_id = 0;
bs_t bs;
bs_init(&bs, &p_data[vol_pos], vol_len * 8);
bs_skip(&bs, 1); /* random access */
bs_skip(&bs, 8); /* vo_type */
if (bs_read1(&bs)) /* is_ol_id */
{
vo_ver_id = bs_read(&bs, 4); /* vo_ver_id */
bs_skip(&bs, 3); /* vo_priority */
}
if (bs_read(&bs, 4) == 15) // aspect_ratio_info
{
bs_skip(&bs, 8); // par_width
bs_skip(&bs, 8); // par_height
}
if (bs_read1(&bs)) /* vol control parameter */
{
bs_read(&bs, 2);
bs_skip(&bs, 1); /* low_delay */
if (bs_read1(&bs))
{
/* vbv parameters */
bs_read(&bs, 15); /* first_half_bitrate */
bs_skip(&bs, 1);
bs_read(&bs, 15); /* latter_half_bitrate */
bs_skip(&bs, 1);
bs_read(&bs, 15); /* first_half_vbv_buffer_size */
bs_skip(&bs, 1);
bs_read(&bs, 3); /* latter_half_vbv_buffer_size */
bs_read(&bs, 11); /* first_half_vbv_occupancy */
bs_skip(&bs, 1);
bs_read(&bs, 15); /* latter_half_vbv_occupancy */
bs_skip(&bs, 1);
}
}
int shape = bs_read(&bs, 2); /* vol shape */
if (shape == 3 && vo_ver_id != 1)
{
bs_skip(&bs, 4); /* video_object_layer_shape_extension */
}
bs_skip(&bs, 1);
int framerate = bs_read(&bs, 16);
int time_increment_bits = (int) (log(framerate - 1.0) * 1.44269504088896340736 + 1); // log2(framerate - 1) + 1
if (time_increment_bits < 1)
{
time_increment_bits = 1;
}
bs_skip(&bs, 1);
if (bs_read1(&bs) != 0) /* fixed_vop_rate */
{
bs_skip(&bs, time_increment_bits);
}
if (shape != 2)
{
if (shape == 0)
{
bs_skip(&bs, 1);
int w = bs_read(&bs, 13);
bs_skip(&bs, 1);
int h = bs_read(&bs, 13);
log_print(HT_LOG_INFO, "%s, MPEG4 width[%d],height[%d]\r\n", __FUNCTION__, w, h);
*width = w;
*height = h;
return 0;
}
}
}
return -1;
}

View File

@@ -0,0 +1,37 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef MEDIA_PARSE_H
#define MEDIA_PARSE_H
#include "sys_inc.h"
#ifdef __cplusplus
extern "C" {
#endif
int avc_parse_video_size(int codec, uint8 * p_data, uint32 len, int * width, int * height);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,253 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "media_util.h"
#include "media_format.h"
#include "h264.h"
#include "h265.h"
uint32 remove_emulation_bytes(uint8* to, uint32 toMaxSize, uint8* from, uint32 fromSize)
{
uint32 toSize = 0;
uint32 i = 0;
while (i < fromSize && toSize+1 < toMaxSize)
{
if (i+2 < fromSize && from[i] == 0 && from[i+1] == 0 && from[i+2] == 3)
{
to[toSize] = to[toSize+1] = 0;
toSize += 2;
i += 3;
}
else
{
to[toSize] = from[i];
toSize += 1;
i += 1;
}
}
return toSize;
}
static uint8 * avc_find_startcode_internal(uint8 *p, uint8 *end)
{
uint8 *a = p + 4 - ((intptr_t)p & 3);
for (end -= 3; p < a && p < end; p++)
{
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
{
return p;
}
}
for (end -= 3; p < end; p += 4)
{
uint32 x = *(const uint32*)p;
if ((x - 0x01010101) & (~x) & 0x80808080)
{ // generic
if (p[1] == 0)
{
if (p[0] == 0 && p[2] == 1)
{
return p;
}
if (p[2] == 0 && p[3] == 1)
{
return p+1;
}
}
if (p[3] == 0)
{
if (p[2] == 0 && p[4] == 1)
{
return p+2;
}
if (p[4] == 0 && p[5] == 1)
{
return p+3;
}
}
}
}
for (end += 3; p < end; p++)
{
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
{
return p;
}
}
return end + 3;
}
uint8 * avc_find_startcode(uint8 *p, uint8 *end)
{
uint8 *out = avc_find_startcode_internal(p, end);
if (p<out && out<end && !out[-1])
{
out--;
}
return out;
}
uint8 * avc_split_nalu(uint8 * e_buf, int e_len, int * s_len, int * d_len)
{
uint8 *r, *r1 = NULL, *end = e_buf + e_len;
*s_len = 0;
*d_len = 0;
r = avc_find_startcode(e_buf, end);
if (r < end)
{
if (r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1)
{
*s_len = 4;
}
else if (r[0] == 0 && r[1] == 0 && r[2] == 1)
{
*s_len = 3;
}
while (!*(r++));
r1 = avc_find_startcode(r, end);
*d_len = r1 - r + *s_len;
}
else
{
*d_len = e_len;
}
return r1;
}
uint8 avc_h264_nalu_type(uint8 * e_buf, int len)
{
if (len > 4 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 0 && e_buf[3] == 1)
{
return (e_buf[4] & 0x1f);
}
else if (len > 3 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 1)
{
return (e_buf[3] & 0x1f);
}
else if (len > 0)
{
return (e_buf[0] & 0x1f);
}
return 0;
}
uint8 avc_h265_nalu_type(uint8 * e_buf, int len)
{
if (len > 4 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 0 && e_buf[3] == 1)
{
return ((e_buf[4] >> 1) & 0x3f);
}
else if (len > 3 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 1)
{
return ((e_buf[3] >> 1) & 0x3f);
}
else if (len > 0)
{
return ((e_buf[0] >> 1) & 0x3f);
}
return 0;
}
BOOL avc_get_h26x_paramsets(uint8 * data, int len, int codec, H26XParamSets * ps)
{
BOOL ret = FALSE;
int s_len = 0, n_len = 0, parse_len = len;
uint8 * p_cur = data;
uint8 * p_end = data + len;
while (p_cur && p_cur < p_end && parse_len > 0)
{
uint8 nalu;
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
if (VIDEO_CODEC_H264 == codec)
{
nalu = avc_h264_nalu_type(p_cur, n_len);
if (nalu == H264_NAL_SPS && ps->sps_size == 0 && n_len <= (int)sizeof(ps->sps))
{
memcpy(ps->sps, p_cur, n_len);
ps->sps_size = n_len;
}
else if (nalu == H264_NAL_PPS && ps->pps_size == 0 && n_len <= (int)sizeof(ps->pps))
{
memcpy(ps->pps, p_cur, n_len);
ps->pps_size = n_len;
}
if (ps->sps_size > 0 && ps->pps_size > 0)
{
ret = TRUE;
break;
}
}
else if (VIDEO_CODEC_H265 == codec)
{
nalu = avc_h265_nalu_type(p_cur, n_len);
if (nalu == HEVC_NAL_VPS && ps->vps_size == 0 && n_len <= (int)sizeof(ps->vps))
{
memcpy(ps->vps, p_cur, n_len);
ps->vps_size = n_len;
}
else if (nalu == HEVC_NAL_SPS && ps->sps_size == 0 && n_len <= (int)sizeof(ps->sps))
{
memcpy(ps->sps, p_cur, n_len);
ps->sps_size = n_len;
}
else if (nalu == HEVC_NAL_PPS && ps->pps_size == 0 && n_len <= (int)sizeof(ps->pps))
{
memcpy(ps->pps, p_cur, n_len);
ps->pps_size = n_len;
}
if (ps->vps_size > 0 && ps->sps_size > 0 && ps->pps_size > 0)
{
ret = TRUE;
break;
}
}
parse_len -= n_len;
p_cur = p_next;
}
return ret;
}

View File

@@ -0,0 +1,53 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef MEDIA_UTIL_H
#define MEDIA_UTIL_H
#include "sys_inc.h"
typedef struct
{
uint8 vps[512];
int vps_size;
uint8 sps[512];
int sps_size;
uint8 pps[512];
int pps_size;
} H26XParamSets;
#ifdef __cplusplus
extern "C" {
#endif
uint32 remove_emulation_bytes(uint8* to, uint32 toMaxSize, uint8* from, uint32 fromSize);
uint8 * avc_find_startcode(uint8 *p, uint8 *end);
uint8 * avc_split_nalu(uint8 * e_buf, int e_len, int * s_len, int * d_len);
uint8 avc_h264_nalu_type(uint8 * e_buf, int len);
uint8 avc_h265_nalu_type(uint8 * e_buf, int len);
BOOL avc_get_h26x_paramsets(uint8 * data, int len, int codec, H26XParamSets * ps);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,407 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "rtmp_player.h"
#include "utils.h"
/***************************************************************************************/
int rtmp_notify_callback(int evt, void * puser)
{
CRtmpPlayer * pPlayer = (CRtmpPlayer *) puser;
pPlayer->onNotify(evt);
return 0;
}
int rtmp_audio_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CRtmpPlayer * pPlayer = (CRtmpPlayer *) puser;
pPlayer->onAudio(pdata, len, ts);
return 0;
}
int rtmp_video_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CRtmpPlayer * pPlayer = (CRtmpPlayer *) puser;
pPlayer->onVideo(pdata, len, ts);
return 0;
}
/***************************************************************************************/
CRtmpPlayer::CRtmpPlayer()
: CVideoPlayer()
{
memset(m_ip, 0, sizeof(m_ip));
}
CRtmpPlayer::~CRtmpPlayer()
{
close();
}
std::string CRtmpPlayer::createNewRTMPUrl(const std::string& username,
const std::string& password,
const std::string& url) {
// Copy the original strings to avoid modifying the originals
std::string modUsername = username;
std::string modPassword = password;
// Replace '@' with '_' in username and password
std::replace(modUsername.begin(), modUsername.end(), '@', '_');
std::replace(modPassword.begin(), modPassword.end(), '@', '_');
// Extract the protocol and rest of the URL
size_t protocolEndPos = url.find("://") + 3; // Find the position right after "://"
std::string protocol = url.substr(0, protocolEndPos); // Include "://"
// Construct the new URL with modified credentials
std::string newUrl = protocol;
newUrl += modUsername + ":" + modPassword + "@";
newUrl += url.substr(protocolEndPos);
return newUrl;
}
BOOL CRtmpPlayer::open(std::string fileName)
{
close();
CVideoPlayer::open(fileName);
char host[100];
url_split(fileName.c_str(), NULL, 0, NULL, 0, NULL, 0, host, sizeof(host), NULL, NULL, 0);
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_rtmp.set_notify_cb(rtmp_notify_callback, this);
m_rtmp.set_audio_cb(rtmp_audio_callback);
m_rtmp.set_video_cb(rtmp_video_callback);
return TRUE;
}
BOOL CRtmpPlayer::open(std::string _username, std::string _password, std::string _url)
{
close();
CVideoPlayer::open(_username, _password, _url);
int port;
char proto[32], username[100], password[100], host[100], path[200];
std::string fileName = createNewRTMPUrl(_username, _password, _url);
url_split(fileName.c_str(),
proto, sizeof(proto),
username, sizeof(username),
password, sizeof(password),
host, sizeof(host),
&port,
path, sizeof(path));
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_rtmp.set_notify_cb(rtmp_notify_callback, this);
m_rtmp.set_audio_cb(rtmp_audio_callback);
m_rtmp.set_video_cb(rtmp_video_callback);
return TRUE;
}
void CRtmpPlayer::close()
{
// stop rtmp connection
m_rtmp.rtmp_stop();
m_rtmp.rtmp_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder();
CVideoPlayer::close();
}
BOOL CRtmpPlayer::play()
{
if (m_rtmp.rtmp_start(m_sFileName.c_str(), m_acct.c_str(), m_pass.c_str()))
{
m_bPlaying = TRUE;
m_bPaused = FALSE;
}
CVideoPlayer::StartVideoDecoder();// Start the video decoder
return m_bPlaying;
}
void CRtmpPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_rtmp.rtmp_stop();
}
// Set flags BEFORE stopping decoder so rx thread stops calling decode()
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
}
BOOL CRtmpPlayer::pause()
{
if (m_bPlaying)
{
if (m_rtmp.rtmp_pause())
{
m_bPaused = TRUE;
m_bPlaying = FALSE;
}
}
else if (m_bPaused)
{
if (m_rtmp.rtmp_play())
{
m_bPaused = FALSE;
m_bPlaying = TRUE;
}
}
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
return m_bPaused;
}
BOOL CRtmpPlayer::seek(int pos)
{
return FALSE;
}
int64 CRtmpPlayer::getElapse()
{
uint32 cur_ts;
uint32 init_ts;
int frequency;
if (m_videoClock.SyncTime.tv_sec > m_audioClock.SyncTime.tv_sec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = m_rtmp.get_video_init_ts();
frequency = getVideoClock();
}
else if (m_videoClock.SyncTime.tv_sec == m_audioClock.SyncTime.tv_sec &&
m_videoClock.SyncTime.tv_usec > m_audioClock.SyncTime.tv_usec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = m_rtmp.get_video_init_ts();
frequency = getVideoClock();
}
else
{
cur_ts = m_audioClock.SyncTimestamp;
init_ts = m_rtmp.get_audio_init_ts();
frequency = getAudioClock();
}
if (init_ts == 0)
{
return 0;
}
int64 elapse;
int timestampDiff = cur_ts - init_ts;
// Divide this by the timestamp frequency to get real time:
double timeDiff = timestampDiff / (double)frequency;
if (timeDiff >= 0.0)
{
elapse = timeDiff * 1000;
}
else
{
timeDiff = -timeDiff;
elapse = timeDiff * 1000;
}
return elapse;
}
int64 CRtmpPlayer::getDuration()
{
return 0;
}
int CRtmpPlayer::getVideoCodec()
{
return m_rtmp.video_codec();
}
int CRtmpPlayer::getAudioCodec()
{
return m_rtmp.audio_codec();
}
void CRtmpPlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CRtmpPlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
void CRtmpPlayer::onNotify(int evt)
{
if (evt == RTMP_EVE_VIDEOREADY)
{
int videoCodec = m_rtmp.video_codec();
if (VIDEO_CODEC_NONE != videoCodec)
{
openVideo(videoCodec);
}
}
else if (evt == RTMP_EVE_AUDIOREADY)
{
int audioCodec = m_rtmp.audio_codec();
if (AUDIO_CODEC_NONE != audioCodec)
{
openAudio(audioCodec, m_rtmp.get_audio_samplerate(), m_rtmp.get_audio_channels());
}
}
//emit notify(evt);
}
void CRtmpPlayer::onAudio(uint8 * pdata, int len, uint32 ts)
{
playAudio(pdata, len, ts, 0);
}
void CRtmpPlayer::onVideo(uint8 * pdata, int len, uint32 ts)
{
playVideo(pdata, len, ts, 0);
}
BOOL CRtmpPlayer::onRecord()
{
CRtmpClient * p_rtmp = &m_rtmp;
AVICTX * p_avictx = m_pAviCtx;
int vcodec = p_rtmp->video_codec();
int fps = p_rtmp->get_video_framerate();
int v_extra_len = 0;
uint8 v_extra[1024];
if (VIDEO_CODEC_H264 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "H264");
uint8 sps[512], pps[512];
int sps_len = 0, pps_len = 0;
if (p_rtmp->get_h264_params(sps, &sps_len, pps, &pps_len))
{
memcpy(v_extra, sps, sps_len);
memcpy(v_extra + sps_len, pps, pps_len);
v_extra_len = sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_H265 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "H265");
uint8 vps[512], sps[512], pps[512];
int vps_len = 0, sps_len = 0, pps_len = 0;
if (p_rtmp->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
memcpy(v_extra, vps, vps_len);
memcpy(v_extra + vps_len, sps, sps_len);
memcpy(v_extra + vps_len + sps_len, pps, pps_len);
v_extra_len = vps_len + sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_JPEG == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "JPEG");
}
else if (VIDEO_CODEC_MP4 == vcodec)
{
avi_set_video_info(p_avictx, fps, 0, 0, "MP4V");
}
int acodec = p_rtmp->audio_codec();
int sr = p_rtmp->get_audio_samplerate();
int ch = p_rtmp->get_audio_channels();
if (AUDIO_CODEC_G711A == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
}
else if (AUDIO_CODEC_G711U == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
}
else if (AUDIO_CODEC_G726 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
}
else if (AUDIO_CODEC_G722 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
}
else if (AUDIO_CODEC_AAC == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
avi_set_audio_extra_info(p_avictx, p_rtmp->get_audio_config(), p_rtmp->get_audio_config_len());
}
avi_update_header(p_avictx);
if (p_avictx->ctxf_video)
{
if (v_extra_len > 0)
{
recordVideo(v_extra, v_extra_len, 0, 0);
}
}
return TRUE;
}

View File

@@ -0,0 +1,62 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef RTMP_PLAYER_H
#define RTMP_PLAYER_H
#include "video_player.h"
#include "rtmp_cln.h"
class CRtmpPlayer : public CVideoPlayer
{
public:
CRtmpPlayer();
virtual ~CRtmpPlayer();
BOOL open(std::string fileName);
BOOL open(std::string _username, std::string _password, std::string _url);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int64 getElapse();
int64 getDuration();
int getVideoCodec();
int getAudioCodec();
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
void onNotify(int evt);
void onAudio(uint8 * pdata, int len, uint32 ts);
void onVideo(uint8 * pdata, int len, uint32 ts);
BOOL onRecord();
private:
char m_ip[32];
CRtmpClient m_rtmp;
std::string createNewRTMPUrl(const std::string& username,
const std::string& password,
const std::string& url);
};
#endif

View File

@@ -0,0 +1,674 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "rtsp_player.h"
#include "rtp.h"
#include <iostream>
/***************************************************************************************/
int rtsp_notify_cb(int evt, void * puser)
{
CRtspPlayer * pPlayer = (CRtspPlayer *) puser;
pPlayer->onNotify(evt);
return 0;
}
int rtsp_audio_cb(uint8 * pdata, int len, uint32 ts, uint16 seq, void *puser)
{
CRtspPlayer * pPlayer = (CRtspPlayer *) puser;
//pPlayer->onAudio(pdata, len, ts, seq); // Disable audio for now
return 0;
}
int rtsp_video_cb(uint8 * pdata, int len, uint32 ts, uint16 seq, void *puser)
{
CRtspPlayer * pPlayer = (CRtspPlayer *) puser;
pPlayer->onVideo(pdata, len, ts, seq);
return 0;
}
int rtsp_rtcp_cb(uint8 * pdata, int len, int type, void *puser)
{
CRtspPlayer * pPlayer = (CRtspPlayer *) puser;
pPlayer->onRtcp(pdata, len, type);
return 0;
}
int rtsp_metadata_cb(uint8 * pdata, int len, uint32 ts, uint16 seq, void *puser)
{
CRtspPlayer * pPlayer = (CRtspPlayer *) puser;
return 0;
}
/***************************************************************************************/
CRtspPlayer::CRtspPlayer(): CVideoPlayer(),m_port(554)
{
memset(m_ip, 0, sizeof(m_ip));
}
CRtspPlayer::~CRtspPlayer()
{
close();
}
BOOL CRtspPlayer::open(std::string fileName)
{
close();
CVideoPlayer::open(fileName);
int port;
char proto[32], username[100], password[100], host[100], path[200];
url_split(fileName.c_str(),
proto, sizeof(proto),
username, sizeof(username),
password, sizeof(password),
host, sizeof(host),
&port,
path, sizeof(path));
if (strcasecmp(proto, "rtsp") == 0)
{
port = (port == -1) ? 554 : port;
}
else if (strcasecmp(proto, "http") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "https") == 0)
{
port = (port == -1) ? 443 : port;
}
else if (strcasecmp(proto, "ws") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "wss") == 0)
{
port = (port == -1) ? 443 : port;
}
else
{
return FALSE;
}
if (host[0] == '\0')
{
return FALSE;
}
if (username[0] != '\0')
{
m_acct = username;
}
if (password[0] != '\0')
{
m_pass = password;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_port = port;
m_rtsp.set_notify_cb(rtsp_notify_cb, this);
m_rtsp.set_audio_cb(rtsp_audio_cb);
m_rtsp.set_video_cb(rtsp_video_cb);
m_rtsp.set_rtcp_cb(rtsp_rtcp_cb);
m_rtsp.set_metadata_cb(rtsp_metadata_cb);
m_rtsp.set_rx_timeout(10); // No data within 10s, receiving timeout
m_rtsp.set_conn_timeout(5);
return TRUE;
}
std::string CRtspPlayer::createNewRTSPUrl(const std::string& username,
const std::string& password,
const std::string& url) {
// Copy the original strings to avoid modifying the originals
std::string modUsername = username;
std::string modPassword = password;
// Replace '@' with '_' in username and password
std::replace(modUsername.begin(), modUsername.end(), '@', '_');
std::replace(modPassword.begin(), modPassword.end(), '@', '_');
// Extract the protocol and rest of the URL
size_t protocolEndPos = url.find("://") + 3; // Find the position right after "://"
std::string protocol = url.substr(0, protocolEndPos); // Include "://"
// Construct the new URL with modified credentials
std::string newUrl = protocol;
newUrl += modUsername + ":" + modPassword + "@";
newUrl += url.substr(protocolEndPos);
return newUrl;
}
BOOL CRtspPlayer::open(std::string _username, std::string _password, std::string _url)
{
close();
CVideoPlayer::open(_username, _password, _url);
int port;
char proto[32], username[100], password[100], host[100], path[200];
std::string fileName = createNewRTSPUrl(_username,_password,_url);
url_split(fileName.c_str(),
proto, sizeof(proto),
username, sizeof(username),
password, sizeof(password),
host, sizeof(host),
&port,
path, sizeof(path));
if (strcasecmp(proto, "rtsp") == 0)
{
port = (port == -1) ? 554 : port;
}
else if (strcasecmp(proto, "http") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "https") == 0)
{
port = (port == -1) ? 443 : port;
}
else if (strcasecmp(proto, "ws") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "wss") == 0)
{
port = (port == -1) ? 443 : port;
}
else
{
return FALSE;
}
if (host[0] == '\0')
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_port = port;
m_rtsp.set_notify_cb(rtsp_notify_cb, this);
m_rtsp.set_audio_cb(rtsp_audio_cb);
m_rtsp.set_video_cb(rtsp_video_cb);
m_rtsp.set_rtcp_cb(rtsp_rtcp_cb);
m_rtsp.set_metadata_cb(rtsp_metadata_cb);
m_rtsp.set_rx_timeout(10); // No data within 10s, receiving timeout
m_rtsp.set_conn_timeout(5);
return TRUE;
}
void CRtspPlayer::close()
{
m_rtsp.rtsp_stop();
m_rtsp.rtsp_close();
// Stop video decoder AFTER RTSP threads are dead (rtsp_close waits for them)
// but BEFORE CVideoPlayer::close(). This flushes the hardware decoder cleanly
// and prevents the destructor-time flush from blocking on the GPU.
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder();
m_port = 554;
memset(m_ip, 0, sizeof(m_ip));
CVideoPlayer::close();
}
void CRtspPlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CRtspPlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
BOOL CRtspPlayer::play()
{
if (m_rtsp.rtsp_start(m_sFileName.c_str(), m_acct.c_str(), m_pass.c_str()))
{
m_bPlaying = TRUE;
m_bPaused = FALSE;
}
CVideoPlayer::StartVideoDecoder();// Start the video decoder
return m_bPlaying;
}
void CRtspPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_rtsp.rtsp_stop();
}
// Set flags BEFORE stopping decoder so TCP rx thread stops calling decode()
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
}
BOOL CRtspPlayer::pause()
{
if (m_bPlaying)
{
if (m_rtsp.rtsp_pause())
{
m_bPaused = TRUE;
m_bPlaying = FALSE;
}
}
else if (m_bPaused)
{
if (m_rtsp.rtsp_play(0))
{
m_bPaused = FALSE;
m_bPlaying = TRUE;
}
}
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
return m_bPaused;
}
BOOL CRtspPlayer::seek(int pos)
{
if (pos < 0 || pos > 100)
{
return FALSE;
}
int64 duration = getDuration();
if (duration <= 0)
{
return FALSE;
}
int ntp_pos = duration / 100 * pos;
m_rtsp.rtsp_play(ntp_pos);
return TRUE;
}
int64 CRtspPlayer::getElapse()
{
uint32 cur_ts;
uint32 init_ts;
int frequency;
RCUA * p_rua = m_rtsp.get_rua();
if (NULL == p_rua)
{
return 0;
}
if (m_videoClock.SyncTime.tv_sec > m_audioClock.SyncTime.tv_sec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = p_rua->video_init_ts;
frequency = getVideoClock();
}
else if (m_videoClock.SyncTime.tv_sec == m_audioClock.SyncTime.tv_sec &&
m_videoClock.SyncTime.tv_usec > m_audioClock.SyncTime.tv_usec)
{
cur_ts = m_videoClock.SyncTimestamp;
init_ts = p_rua->video_init_ts;
frequency = getVideoClock();
}
else
{
cur_ts = m_audioClock.SyncTimestamp;
init_ts = p_rua->audio_init_ts;
frequency = getAudioClock();
}
if (init_ts == (uint32)-1)
{
return 0;
}
int64 elapse;
int timestampDiff = cur_ts - init_ts;
// Divide this by the timestamp frequency to get real time:
double timeDiff = timestampDiff / (double)frequency;
if (timeDiff >= 0.0)
{
elapse = p_rua->media_start + timeDiff * 1000;
}
else
{
timeDiff = -timeDiff;
elapse = p_rua->media_start + timeDiff * 1000;
}
return elapse;
}
int64 CRtspPlayer::getDuration()
{
RCUA * p_rua = m_rtsp.get_rua();
if (p_rua)
{
return p_rua->play_end - p_rua->play_start;
}
return 0;
}
int CRtspPlayer::getVideoClock()
{
return 90000;
}
int CRtspPlayer::getAudioClock()
{
return m_nSampleRate;
}
int CRtspPlayer::getVideoCodec()
{
return m_rtsp.video_codec();
}
int CRtspPlayer::getAudioCodec()
{
return m_rtsp.audio_codec();
}
void CRtspPlayer::setRtpMulticast(BOOL flag)
{
m_rtsp.set_rtp_multicast(flag);
}
void CRtspPlayer::setRtpOverUdp(BOOL flag)
{
m_rtsp.set_rtp_over_udp(flag);
}
#ifdef BACKCHANNEL
int CRtspPlayer::getBCFlag()
{
return m_rtsp.get_bc_flag();
}
void CRtspPlayer::setBCFlag(int flag)
{
m_rtsp.set_bc_flag(flag);
}
int CRtspPlayer::getBCDataFlag()
{
return m_rtsp.get_bc_data_flag();
}
void CRtspPlayer::setBCDataFlag(int flag)
{
if (m_rtsp.get_bc_data_flag())
{
m_rtsp.set_bc_data_flag(0);
}
else
{
m_rtsp.set_bc_data_flag(1);
}
}
void CRtspPlayer::setAudioDevice(int index)
{
m_rtsp.set_audio_device(index);
}
#endif // end of BACKCHANNEL
#ifdef REPLAY
int CRtspPlayer::getReplayFlag()
{
return m_rtsp.get_replay_flag();
}
void CRtspPlayer::setReplayFlag(int flag)
{
m_rtsp.set_replay_flag(flag);
}
void CRtspPlayer::setScale(double scale)
{
m_rtsp.set_scale(scale);
}
void CRtspPlayer::setRateControlFlag(int flag)
{
m_rtsp.set_rate_control_flag(flag);
}
void CRtspPlayer::setImmediateFlag(int flag)
{
m_rtsp.set_immediate_flag(flag);
}
void CRtspPlayer::setFramesFlag(int flag, int interval)
{
m_rtsp.set_frames_flag(flag, interval);
}
void CRtspPlayer::setReplayRange(time_t start, time_t end)
{
m_rtsp.set_replay_range(start, end);
}
#endif // end of REPLAY
#ifdef OVER_HTTP
void CRtspPlayer::setRtspOverHttp(int flag, int port)
{
m_rtsp.set_rtsp_over_http(flag, port);
}
#endif // OVER_HTTP
#ifdef OVER_WEBSOCKET
void CRtspPlayer::setRtspOverWs(int flag, int port)
{
m_rtsp.set_rtsp_over_ws(flag, port);
}
#endif // OVER_WEBSOCKET
void CRtspPlayer::onNotify(int evt)
{
if (evt == RTSP_EVE_CONNSUCC)
{
int videoCodec = m_rtsp.video_codec();
int audioCodec = m_rtsp.audio_codec();
if (VIDEO_CODEC_NONE != videoCodec)
{
openVideo(videoCodec);
}
if (AUDIO_CODEC_NONE != audioCodec)
{
openAudio(audioCodec,
m_rtsp.get_audio_samplerate(),
m_rtsp.get_audio_channels(),
m_rtsp.get_audio_bitpersample());
}
}
//emit notify(evt);
}
void CRtspPlayer::onAudio(uint8 * pdata, int len, uint32 ts, uint16 seq)
{
playAudio(pdata, len, ts, seq);
}
void CRtspPlayer::onVideo(uint8 * pdata, int len, uint32 ts, uint16 seq)
{
playVideo(pdata, len, ts, seq);
}
void CRtspPlayer::onRtcp(uint8 * pdata, int len, int type)
{
uint8 * p_rtp = pdata;
uint32 rtp_len = len;
// Check for the 20-byte RTCP header:
if (rtp_len < 20)
{
return;
}
uint8 pt = p_rtp[1];
uint32 rtpHdr = ntohl(*(uint32*)p_rtp); p_rtp += 4; rtp_len -= 4;
uint32 ssrc = ntohl(*(uint32*)(p_rtp)); p_rtp += 4; rtp_len -= 4;
uint32 ntpMSW = ntohl(*(uint32*)(p_rtp)); p_rtp += 4; rtp_len -= 4;
uint32 ntpLSW = ntohl(*(uint32*)(p_rtp)); p_rtp += 4; rtp_len -= 4;
uint32 rtpTimestamp = ntohl(*(uint32*)(p_rtp));
if (pt != RTCP_SR)
{
return;
}
// Check the RTP version number (it should be 2)
if ((rtpHdr & 0xC0000000) != 0x80000000)
{
return;
}
if (AV_TYPE_AUDIO == type)
{
m_audioClock.SyncTimestamp = rtpTimestamp;
m_audioClock.SyncTime.tv_sec = ntpMSW - 0x83AA7E80; // 1/1/1900 -> 1/1/1970
double microseconds = (ntpLSW * 15625.0) / 0x04000000; // 10^6/2^32
m_audioClock.SyncTime.tv_usec = (unsigned)(microseconds+0.5);
}
else if (AV_TYPE_VIDEO == type)
{
m_videoClock.SyncTimestamp = rtpTimestamp;
m_videoClock.SyncTime.tv_sec = ntpMSW - 0x83AA7E80; // 1/1/1900 -> 1/1/1970
double microseconds = (ntpLSW * 15625.0) / 0x04000000; // 10^6/2^32
m_videoClock.SyncTime.tv_usec = (unsigned)(microseconds+0.5);
}
}
BOOL CRtspPlayer::onRecord()
{
CRtspClient * p_rtsp = &m_rtsp;
AVICTX * p_avictx = m_pAviCtx;
int vcodec = p_rtsp->video_codec();
int v_extra_len = 0;
uint8 v_extra[1024];
if (VIDEO_CODEC_H264 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "H264");
uint8 sps[512], pps[512];
int sps_len = 0, pps_len = 0;
if (p_rtsp->get_h264_params(sps, &sps_len, pps, &pps_len))
{
memcpy(v_extra, sps, sps_len);
memcpy(v_extra + sps_len, pps, pps_len);
v_extra_len = sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_H265 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "H265");
uint8 vps[512], sps[512], pps[512];
int vps_len = 0, sps_len = 0, pps_len = 0;
if (p_rtsp->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
memcpy(v_extra, vps, vps_len);
memcpy(v_extra + vps_len, sps, sps_len);
memcpy(v_extra + vps_len + sps_len, pps, pps_len);
v_extra_len = vps_len + sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_JPEG == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "JPEG");
}
else if (VIDEO_CODEC_MP4 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "MP4V");
}
int acodec = p_rtsp->audio_codec();
int sr = p_rtsp->get_audio_samplerate();
int ch = p_rtsp->get_audio_channels();
if (AUDIO_CODEC_G711A == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
}
else if (AUDIO_CODEC_G711U == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
}
else if (AUDIO_CODEC_G726 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
}
else if (AUDIO_CODEC_G722 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
}
else if (AUDIO_CODEC_AAC == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
avi_set_audio_extra_info(p_avictx, p_rtsp->get_audio_config(), p_rtsp->get_audio_config_len());
}
avi_update_header(p_avictx);
if (p_avictx->ctxf_video)
{
if (v_extra_len > 0)
{
recordVideo(v_extra, v_extra_len, 0, 0);
}
}
return TRUE;
}

View File

@@ -0,0 +1,91 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef RTSP_PLAYER_H
#define RTSP_PLAYER_H
#include "video_player.h"
#include "rtsp_cln.h"
#include <string>
class CRtspPlayer : public CVideoPlayer
{
public:
CRtspPlayer();
virtual ~CRtspPlayer();
BOOL open(std::string fileName);
BOOL open(std::string _username, std::string _password, std::string _url);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int64 getElapse();
int64 getDuration();
int getVideoClock();
int getAudioClock();
int getVideoCodec();
int getAudioCodec();
void setRtpMulticast(BOOL flag);
void setRtpOverUdp(BOOL flag);
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
#ifdef OVER_HTTP
void setRtspOverHttp(int flag, int port);
#endif
#ifdef OVER_WEBSOCKET
void setRtspOverWs(int flag, int port);
#endif
#ifdef BACKCHANNEL
int getBCFlag();
void setBCFlag(int flag);
int getBCDataFlag();
void setBCDataFlag(int flag);
void setAudioDevice(int index);
#endif
#ifdef REPLAY
int getReplayFlag();
void setReplayFlag(int flag);
void setScale(double scale);
void setRateControlFlag(int flag);
void setImmediateFlag(int flag);
void setFramesFlag(int flag, int interval);
void setReplayRange(time_t start, time_t end);
#endif
void onNotify(int evt);
void onAudio(uint8 * pdata, int len, uint32 ts, uint16 seq);
void onVideo(uint8 * pdata, int len, uint32 ts, uint16 seq);
void onRtcp(uint8 * pdata, int len, int type);
BOOL onRecord();
private:
char m_ip[32];
int m_port;
std::string createNewRTSPUrl(const std::string& username, const std::string& password, const std::string& url);
CRtspClient m_rtsp;
};
#endif

View File

@@ -0,0 +1,412 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "srt_player.h"
#include "utils.h"
/***************************************************************************************/
int srt_notify_callback(int evt, void * puser)
{
CSrtPlayer * pPlayer = (CSrtPlayer *) puser;
pPlayer->onNotify(evt);
return 0;
}
int srt_audio_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CSrtPlayer * pPlayer = (CSrtPlayer *) puser;
pPlayer->onAudio(pdata, len, ts);
return 0;
}
int srt_video_callback(uint8 * pdata, int len, uint32 ts, void *puser)
{
CSrtPlayer * pPlayer = (CSrtPlayer *) puser;
pPlayer->onVideo(pdata, len, ts);
return 0;
}
/***************************************************************************************/
CSrtPlayer::CSrtPlayer(): CVideoPlayer()
, m_port(-1)
{
memset(m_ip, 0, sizeof(m_ip));
}
CSrtPlayer::~CSrtPlayer()
{
close();
}
std::string CSrtPlayer::createNewSRTUrl(const std::string& username,
const std::string& password,
const std::string& url) {
// Copy the original strings to avoid modifying the originals
std::string modUsername = username;
std::string modPassword = password;
// Replace '@' with '_' in username and password
std::replace(modUsername.begin(), modUsername.end(), '@', '_');
std::replace(modPassword.begin(), modPassword.end(), '@', '_');
// Extract the protocol and rest of the URL
size_t protocolEndPos = url.find("://") + 3; // Find the position right after "://"
std::string protocol = url.substr(0, protocolEndPos); // Include "://"
// Construct the new URL with modified credentials
std::string newUrl = protocol;
newUrl += modUsername + ":" + modPassword + "@";
newUrl += url.substr(protocolEndPos);
return newUrl;
}
BOOL CSrtPlayer::open(std::string fileName)
{
close();
CVideoPlayer::open(fileName);
int port;
char proto[32], username[100], password[100], host[100], path[200];
url_split(fileName.c_str(), proto, sizeof(proto), username, sizeof(username),
password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));
if (host[0] == '\0')
{
return FALSE;
}
if (port <= 0)
{
return FALSE;
}
if (username[0] != '\0')
{
m_acct = username;
}
if (password[0] != '\0')
{
m_pass = password;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_port = port;
m_srt.set_notify_cb(srt_notify_callback, this);
m_srt.set_audio_cb(srt_audio_callback);
m_srt.set_video_cb(srt_video_callback);
return TRUE;
}
BOOL CSrtPlayer::open(std::string _username, std::string _password, std::string _url)
{
close();
CVideoPlayer::open(_username, _password, _url);
int port;
char proto[32], username[100], password[100], host[100], path[200];
std::string fileName = createNewSRTUrl(_username, _password, _url);
url_split(fileName.c_str(), proto, sizeof(proto), username, sizeof(username),
password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));
if (host[0] == '\0')
{
return FALSE;
}
if (port <= 0)
{
return FALSE;
}
strncpy(m_ip, host, sizeof(m_ip) - 1);
m_port = port;
m_srt.set_notify_cb(srt_notify_callback, this);
m_srt.set_audio_cb(srt_audio_callback);
m_srt.set_video_cb(srt_video_callback);
return TRUE;
}
void CSrtPlayer::close()
{
// stop connection
m_srt.srt_stop();
m_srt.srt_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder();
m_port = -1;
memset(m_ip, 0, sizeof(m_ip));
CVideoPlayer::close();
}
BOOL CSrtPlayer::play()
{
if (m_srt.srt_start(m_sFileName.c_str(), m_acct.c_str(), m_pass.c_str()))
{
m_bPlaying = TRUE;
m_bPaused = FALSE;
}
CVideoPlayer::StartVideoDecoder();// Start the video decoder
return m_bPlaying;
}
void CSrtPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_srt.srt_stop();
}
// Set flags BEFORE stopping decoder so rx thread stops calling decode()
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
}
BOOL CSrtPlayer::pause()
{
if (m_bPlaying)
{
if (m_srt.srt_pause())
{
m_bPaused = TRUE;
m_bPlaying = FALSE;
}
}
else if (m_bPaused)
{
if (m_srt.srt_play())
{
m_bPaused = FALSE;
m_bPlaying = TRUE;
}
}
CVideoPlayer::StopVideoDecoder(); // Stop the video decoder
return m_bPaused;
}
BOOL CSrtPlayer::seek(int pos)
{
if (pos < 0 || pos > 100)
{
return FALSE;
}
int64 duration = getDuration();
if (duration <= 0)
{
return FALSE;
}
int ntp_pos = duration / 100 * pos;
// m_srt.srt_play();
return TRUE;
}
int64 CSrtPlayer::getElapse()
{
return 0;
}
int64 CSrtPlayer::getDuration()
{
return 0;
}
int CSrtPlayer::getVideoClock()
{
return 90000;
}
int CSrtPlayer::getAudioClock()
{
return 90000;
}
int CSrtPlayer::getVideoCodec()
{
return m_srt.video_codec();
}
int CSrtPlayer::getAudioCodec()
{
return m_srt.audio_codec();
}
void CSrtPlayer::onNotify(int evt)
{
if (evt == SRT_EVE_VIDEOREADY)
{
int videoCodec = m_srt.video_codec();
if (VIDEO_CODEC_NONE != videoCodec)
{
openVideo(videoCodec);
}
}
else if (evt == SRT_EVE_AUDIOREADY)
{
int audioCodec = m_srt.audio_codec();
if (AUDIO_CODEC_NONE != audioCodec)
{
openAudio(audioCodec, m_srt.get_audio_samplerate(), m_srt.get_audio_channels());
}
}
}
void CSrtPlayer::onAudio(uint8 * pdata, int len, uint32 ts)
{
playAudio(pdata, len, ts, 0);
}
void CSrtPlayer::onVideo(uint8 * pdata, int len, uint32 ts)
{
playVideo(pdata, len, ts, 0);
}
void CSrtPlayer::setBbox(cv::Rect bbox) {
CVideoPlayer::setBbox(bbox);
}
void CSrtPlayer::setCrop(bool crop) {
CVideoPlayer::setCrop(crop);
}
BOOL CSrtPlayer::onRecord()
{
CSrtClient * p_srt = &m_srt;
AVICTX * p_avictx = m_pAviCtx;
int vcodec = p_srt->video_codec();
int v_extra_len = 0;
uint8 v_extra[1024];
if (VIDEO_CODEC_H264 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "H264");
uint8 sps[512], pps[512];
int sps_len = 0, pps_len = 0;
if (p_srt->get_h264_params(sps, &sps_len, pps, &pps_len))
{
memcpy(v_extra, sps, sps_len);
memcpy(v_extra + sps_len, pps, pps_len);
v_extra_len = sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_H265 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "H265");
uint8 vps[512], sps[512], pps[512];
int vps_len = 0, sps_len = 0, pps_len = 0;
if (p_srt->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
memcpy(v_extra, vps, vps_len);
memcpy(v_extra + vps_len, sps, sps_len);
memcpy(v_extra + vps_len + sps_len, pps, pps_len);
v_extra_len = vps_len + sps_len + pps_len;
}
if (v_extra_len > 0)
{
avi_set_video_extra_info(p_avictx, v_extra, v_extra_len);
}
}
else if (VIDEO_CODEC_JPEG == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "JPEG");
}
else if (VIDEO_CODEC_MP4 == vcodec)
{
avi_set_video_info(p_avictx, 0, 0, 0, "MP4V");
}
int acodec = p_srt->audio_codec();
int sr = p_srt->get_audio_samplerate();
int ch = p_srt->get_audio_channels();
if (AUDIO_CODEC_G711A == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
}
else if (AUDIO_CODEC_G711U == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
}
else if (AUDIO_CODEC_G726 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
}
else if (AUDIO_CODEC_G722 == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
}
else if (AUDIO_CODEC_AAC == acodec)
{
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
avi_set_audio_extra_info(p_avictx, p_srt->get_audio_config(), p_srt->get_audio_config_len());
}
avi_update_header(p_avictx);
if (p_avictx->ctxf_video)
{
if (v_extra_len > 0)
{
recordVideo(v_extra, v_extra_len, 0, 0);
}
}
return TRUE;
}

View File

@@ -0,0 +1,66 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef SRT_PLAYER_H
#define SRT_PLAYER_H
#include "video_player.h"
#include "srt_cln.h"
class CSrtPlayer : public CVideoPlayer
{
public:
CSrtPlayer();
virtual ~CSrtPlayer();
BOOL open(std::string fileName);
BOOL open(std::string _username, std::string _password, std::string _url);
void close();
BOOL play();
void stop();
BOOL pause();
BOOL seek(int pos);
int64 getElapse();
int64 getDuration();
int getVideoClock();
int getAudioClock();
int getVideoCodec();
int getAudioCodec();
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
void onNotify(int evt);
void onAudio(uint8 * pdata, int len, uint32 ts);
void onVideo(uint8 * pdata, int len, uint32 ts);
BOOL onRecord();
private:
char m_ip[32];
int m_port;
CSrtClient m_srt;
std::string createNewSRTUrl(const std::string& username,
const std::string& password,
const std::string& url);
};
#endif

View File

@@ -0,0 +1,989 @@
#include "video_decoder.h"
#include "avcodec_mutex.h"
#include "lock.h"
#include "media_codec.h"
#include "media_parse.h"
#include <memory>
uint32 g_hw_decoder_nums = 0;
uint32 g_hw_decoder_max = 4; // Hardware decoding resources are limited, Limit up to 4 hardware decoding sessions
void* g_hw_decoder_mutex = sys_os_create_mutex();
// ---------------------------------------------------------------------------
// HWDecoderPool implementation
// ---------------------------------------------------------------------------
HWDecoderPool& HWDecoderPool::instance() {
static HWDecoderPool pool;
return pool;
}
void HWDecoderPool::configure(int numGpus, int maxPerGpu) {
// Uniform limit: same max for all GPUs
std::vector<int> limits(numGpus, maxPerGpu);
configure(limits);
}
void HWDecoderPool::configure(const std::vector<int>& maxPerGpuList) {
std::lock_guard<std::mutex> lock(m_mutex);
m_maxPerGpu = maxPerGpuList;
m_activePerGpu.assign(maxPerGpuList.size(), 0);
m_configured = true;
// Also update legacy global for backward compatibility
int total = 0;
for (int m : m_maxPerGpu) total += m;
g_hw_decoder_max = static_cast<uint32>(total);
for (int i = 0; i < static_cast<int>(m_maxPerGpu.size()); ++i) {
fprintf(stderr, "[HWDecode] HWDecoderPool: GPU[%d] max=%d sessions\n", i, m_maxPerGpu[i]);
}
fprintf(stderr, "[HWDecode] HWDecoderPool: configured %d GPU(s), %d total sessions\n",
static_cast<int>(m_maxPerGpu.size()), total);
}
bool HWDecoderPool::isConfigured() const {
return m_configured;
}
int HWDecoderPool::acquireSlot(int preferredGpu) {
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_configured || m_activePerGpu.empty()) return -1;
// If caller requested a specific GPU (e.g. to match inference GPU for NV12 zero-copy),
// try that GPU first. This avoids cross-GPU device pointer access which causes
// "illegal memory access" sticky CUDA errors.
if (preferredGpu >= 0 && preferredGpu < static_cast<int>(m_activePerGpu.size())) {
if (m_activePerGpu[preferredGpu] < m_maxPerGpu[preferredGpu]) {
m_activePerGpu[preferredGpu]++;
fprintf(stderr, "[HWDecode] HWDecoderPool: acquired slot on PREFERRED GPU[%d] (%d/%d)\n",
preferredGpu, m_activePerGpu[preferredGpu], m_maxPerGpu[preferredGpu]);
return preferredGpu;
}
fprintf(stderr, "[HWDecode] HWDecoderPool: preferred GPU[%d] at capacity (%d/%d), falling back to least-loaded\n",
preferredGpu, m_activePerGpu[preferredGpu], m_maxPerGpu[preferredGpu]);
}
// Fallback: find the GPU with the fewest active sessions that still has capacity
int bestGpu = -1;
int bestCount = INT_MAX;
for (int i = 0; i < static_cast<int>(m_activePerGpu.size()); ++i) {
if (m_activePerGpu[i] < m_maxPerGpu[i] && m_activePerGpu[i] < bestCount) {
bestCount = m_activePerGpu[i];
bestGpu = i;
}
}
if (bestGpu >= 0) {
m_activePerGpu[bestGpu]++;
fprintf(stderr, "[HWDecode] HWDecoderPool: acquired slot on GPU[%d] (%d/%d)\n",
bestGpu, m_activePerGpu[bestGpu], m_maxPerGpu[bestGpu]);
}
return bestGpu;
}
void HWDecoderPool::releaseSlot(int gpuIndex) {
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_configured) return;
if (gpuIndex >= 0 && gpuIndex < static_cast<int>(m_activePerGpu.size())) {
if (m_activePerGpu[gpuIndex] > 0) {
m_activePerGpu[gpuIndex]--;
fprintf(stderr, "[HWDecode] HWDecoderPool: released slot on GPU[%d] (%d/%d)\n",
gpuIndex, m_activePerGpu[gpuIndex], m_maxPerGpu[gpuIndex]);
}
}
}
int HWDecoderPool::getTotalMax() const {
int total = 0;
for (int m : m_maxPerGpu) total += m;
return total;
}
int HWDecoderPool::getTotalActive() const {
int total = 0;
for (int c : m_activePerGpu) total += c;
return total;
}
// ---------------------------------------------------------------------------
// SharedHWDeviceCtx implementation
// ---------------------------------------------------------------------------
SharedHWDeviceCtx& SharedHWDeviceCtx::instance() {
static SharedHWDeviceCtx inst;
return inst;
}
SharedHWDeviceCtx::~SharedHWDeviceCtx() {
// Intentionally empty — do NOT release GPU/D3D11 resources here.
// This destructor runs during DLL_PROCESS_DETACH while the OS loader
// lock is held. Releasing D3D11/NVIDIA resources requires driver
// worker threads that also need the loader lock → deadlock.
// The OS reclaims all GPU resources when the process exits.
}
AVBufferRef* SharedHWDeviceCtx::acquire(int gpuIndex, AVHWDeviceType type) {
std::lock_guard<std::mutex> lock(m_mutex);
// Grow cache if needed
if (gpuIndex < 0) gpuIndex = 0;
if (static_cast<int>(m_cache.size()) <= gpuIndex) {
m_cache.resize(gpuIndex + 1);
}
GpuCtx& slot = m_cache[gpuIndex];
// If already created for this GPU and same type, return a new reference
if (slot.ctx && slot.type == type) {
fprintf(stderr, "[HWDecode] SharedHWDeviceCtx: reusing shared context for GPU[%d]\n", gpuIndex);
return av_buffer_ref(slot.ctx);
}
// Release old context if type changed
if (slot.ctx) {
av_buffer_unref(&slot.ctx);
slot.ctx = nullptr;
}
// Create new HW device context for this GPU
char adapterStr[16] = {};
snprintf(adapterStr, sizeof(adapterStr), "%d", gpuIndex);
int err = av_hwdevice_ctx_create(&slot.ctx, type, adapterStr, nullptr, 0);
if (err < 0) {
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(err, error_buf, sizeof(error_buf));
fprintf(stderr, "[HWDecode] SharedHWDeviceCtx: FAILED to create context for GPU[%d]: %s\n",
gpuIndex, error_buf);
slot.ctx = nullptr;
return nullptr;
}
slot.type = type;
fprintf(stderr, "[HWDecode] SharedHWDeviceCtx: created shared context for GPU[%d] type=%s\n",
gpuIndex, av_hwdevice_get_type_name(type));
// Return a new reference (caller owns it)
return av_buffer_ref(slot.ctx);
}
void SharedHWDeviceCtx::releaseAll() {
std::lock_guard<std::mutex> lock(m_mutex);
for (auto& slot : m_cache) {
if (slot.ctx) {
av_buffer_unref(&slot.ctx);
slot.ctx = nullptr;
}
}
m_cache.clear();
}
enum AVPixelFormat getHWFormat(AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts)
{
CVideoDecoder* pthis = (CVideoDecoder*)ctx->opaque;
AVPixelFormat dst_pix_fmt = AV_PIX_FMT_NONE;
pthis->getHWFormat(ctx, pix_fmts, &dst_pix_fmt);
return dst_pix_fmt;
}
enum AVPixelFormat get_hw_format(AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts)
{
for (const enum AVPixelFormat* p = pix_fmts; *p != -1; p++) {
if (*p == AV_PIX_FMT_YUVJ420P) {
return AV_PIX_FMT_YUVJ420P;
}
}
// If YUVJ420P is not available, fall back to default
return ctx->pix_fmt;
}
CVideoDecoder::CVideoDecoder()
{
m_bInited = FALSE;
m_bRunning = FALSE;
m_bHardwareDecoderEnabled = FALSE;
m_bCudaHWAccel = false;
m_hwGpuIndex = -1;
m_pCodec = NULL;
m_pContext = NULL;
m_pFrame = NULL;
m_pSoftFrame = NULL;
m_pCudaHWFrame = NULL;
m_pCallback = NULL;
m_pUserdata = NULL;
m_hwPixFmt = AV_PIX_FMT_NONE;
m_pHWDeviceCtx = NULL;
}
AVFrame* CVideoDecoder::takeCudaHWFrame() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
AVFrame* result = m_pCudaHWFrame;
m_pCudaHWFrame = nullptr;
return result;
}
AVFrame* CVideoDecoder::cloneCudaHWFrame_unlocked() {
// Caller MUST already hold _mutex (called from decode thread's callback chain).
// Returns a clone so the original m_pCudaHWFrame stays valid for the decode loop.
return m_pCudaHWFrame ? av_frame_clone(m_pCudaHWFrame) : nullptr;
}
CVideoDecoder::~CVideoDecoder()
{
uninit();
}
void CVideoDecoder::uninit()
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
// Stop processing first
// Backup first
BOOL wasRunning = m_bRunning;
m_bRunning = FALSE;
flush();
// FIXED: Clean up frames before context to avoid use-after-free
if (m_pFrame)
{
av_frame_free(&m_pFrame);
m_pFrame = NULL;
}
if (m_pSoftFrame)
{
av_frame_free(&m_pSoftFrame);
m_pSoftFrame = NULL;
}
if (m_pCudaHWFrame)
{
av_frame_free(&m_pCudaHWFrame);
m_pCudaHWFrame = NULL;
}
if (m_pContext)
{
// FIXED: Free extradata before freeing context
if (m_pContext->extradata) {
av_free(m_pContext->extradata);
m_pContext->extradata = NULL;
m_pContext->extradata_size = 0;
}
// FIXED: Properly release hardware context reference
if (m_pContext->hw_device_ctx) {
av_buffer_unref(&m_pContext->hw_device_ctx);
m_pContext->hw_device_ctx = NULL;
}
// FIXED: Close codec before freeing context
avcodec_close(m_pContext);
avcodec_free_context(&m_pContext);
m_pContext = NULL;
}
// Only decrement hardware decoder count if it was actually enabled
if (m_pHWDeviceCtx && m_bHardwareDecoderEnabled)
{
av_buffer_unref(&m_pHWDeviceCtx);
m_pHWDeviceCtx = NULL;
// Release via per-GPU pool or legacy global counter
HWDecoderPool& pool = HWDecoderPool::instance();
if (pool.isConfigured() && m_hwGpuIndex >= 0) {
pool.releaseSlot(m_hwGpuIndex);
} else {
CLock hw_lock(g_hw_decoder_mutex);
if (g_hw_decoder_nums > 0) {
g_hw_decoder_nums--;
}
}
m_hwGpuIndex = -1;
m_bHardwareDecoderEnabled = FALSE;
}
// Restore running state if needed
m_bRunning = wasRunning;
m_pCodec = NULL;
m_bInited = FALSE;
}
BOOL CVideoDecoder::init(enum AVCodecID codec, uint8* extradata, int extradata_size, int hwMode, int preferredGpu)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
// Clean up any existing state
if (m_bInited) {
uninit();
}
int width = 0;
int height = 0;
if (extradata && extradata_size > 0)
{
int vcodec = VIDEO_CODEC_NONE;
if (AV_CODEC_ID_H264 == codec)
{
vcodec = VIDEO_CODEC_H264;
}
else if (AV_CODEC_ID_HEVC == codec)
{
vcodec = VIDEO_CODEC_H265;
}
else if (AV_CODEC_ID_MJPEG == codec)
{
vcodec = VIDEO_CODEC_JPEG;
}
else if (AV_CODEC_ID_MPEG4 == codec)
{
vcodec = VIDEO_CODEC_MP4;
}
avc_parse_video_size(vcodec, extradata, extradata_size, &width, &height);
}
#ifdef ANDROID
if (HW_DECODING_DISABLE != hwMode && width * height >= 320 * 240)
{
if (AV_CODEC_ID_H264 == codec)
{
m_pCodec = avcodec_find_decoder_by_name("h264_mediacodec");
}
else if (AV_CODEC_ID_HEVC == codec)
{
m_pCodec = avcodec_find_decoder_by_name("hevc_mediacodec");
}
else if (AV_CODEC_ID_MPEG4 == codec)
{
m_pCodec = avcodec_find_decoder_by_name("mpeg4_mediacodec");
}
}
if (NULL == m_pCodec)
{
m_pCodec = avcodec_find_decoder(codec);
}
#else
m_pCodec = avcodec_find_decoder(codec);
#endif
if (NULL == m_pCodec)
{
log_print(HT_LOG_ERR, "%s, m_pCodec is NULL for codec %d\r\n", __FUNCTION__, codec);
return FALSE;
}
m_pContext = avcodec_alloc_context3(m_pCodec);
if (NULL == m_pContext)
{
log_print(HT_LOG_ERR, "%s, avcodec_alloc_context3 failed\r\n", __FUNCTION__);
return FALSE;
}
m_pContext->width = width;
m_pContext->height = height;
m_pContext->flags |= AV_CODEC_FLAG_LOW_DELAY;
m_pContext->flags2 |= AV_CODEC_FLAG2_FAST;
m_pContext->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
m_pContext->err_recognition = AV_EF_IGNORE_ERR;
av_opt_set_int(m_pContext, "refcounted_frames", 1, 0);
// Initialize hardware decoder
if (HW_DECODING_DISABLE != hwMode) {
int hw_ret = hwDecoderInit(m_pContext, hwMode, preferredGpu);
if (hw_ret < 0) {
log_print(HT_LOG_WARN, "%s, hwDecoderInit failed with error %d, falling back to software decoding\r\n", __FUNCTION__, hw_ret);
}
}
// Handle extradata
if (extradata && extradata_size > 0)
{
int size = extradata_size + AV_INPUT_BUFFER_PADDING_SIZE;
m_pContext->extradata = (uint8*)av_mallocz(size);
if (m_pContext->extradata)
{
m_pContext->extradata_size = extradata_size;
memcpy(m_pContext->extradata, extradata, extradata_size);
}
else
{
log_print(HT_LOG_ERR, "%s, Failed to allocate extradata\r\n", __FUNCTION__);
uninit(); // FIXED: Clean up on failure
return FALSE;
}
}
// FIXED: Use avcodec_open2 instead of avcodec_thread_open
if (avcodec_open2(m_pContext, m_pCodec, NULL) < 0)
{
log_print(HT_LOG_ERR, "%s, avcodec_open2 failed\r\n", __FUNCTION__);
uninit(); // FIXED: Clean up on failure
return FALSE;
}
m_pFrame = av_frame_alloc();
if (NULL == m_pFrame)
{
log_print(HT_LOG_ERR, "%s, av_frame_alloc failed for m_pFrame\r\n", __FUNCTION__);
uninit(); // FIXED: Clean up on failure
return FALSE;
}
m_pSoftFrame = av_frame_alloc();
if (NULL == m_pSoftFrame)
{
log_print(HT_LOG_ERR, "%s, av_frame_alloc failed for m_pSoftFrame\r\n", __FUNCTION__);
uninit(); // FIXED: Clean up on failure
return FALSE;
}
m_bInited = TRUE;
//m_bRunning = TRUE;
return TRUE;
}
BOOL CVideoDecoder::init(int codec, uint8* extradata, int extradata_size, int hwMode, int preferredGpu)
{
BOOL result = init(to_video_avcodecid(codec), extradata, extradata_size, hwMode, preferredGpu);
if (result) {
m_bRunning = TRUE; // Set running only if initialization succeeded
}
return result;
}
int CVideoDecoder::hwDecoderInit(AVCodecContext* ctx, int hwMode, int preferredGpu) {
int err = 0;
std::string hwtype;
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
if (hwMode == HW_DECODING_DISABLE) {
return 0; // Hardware decoding is disabled
}
// -- Per-GPU pool path (preferred) or legacy global path ----------------
int assignedGpu = -1;
HWDecoderPool& pool = HWDecoderPool::instance();
if (pool.isConfigured()) {
// Per-GPU: acquire a slot, preferring the caller's requested GPU
// (e.g. inference GPU for NV12 zero-copy alignment)
assignedGpu = pool.acquireSlot(preferredGpu);
if (assignedGpu < 0) {
log_print(HT_LOG_WARN, "%s, All GPU HW decoder slots full (%d/%d total)\r\n",
__FUNCTION__, pool.getTotalActive(), pool.getTotalMax());
return -1;
}
} else {
// Legacy: single global counter
CLock lock(g_hw_decoder_mutex);
if (g_hw_decoder_max > 0 && g_hw_decoder_nums >= g_hw_decoder_max) {
log_print(HT_LOG_WARN, "%s, Maximum number of hardware decoders reached (%d/%d)\r\n",
__FUNCTION__, g_hw_decoder_nums, g_hw_decoder_max);
return -1;
}
}
// Determine the hardware type based on platform and hardware mode
if (!getHardwareTypeForPlatform(hwMode, hwtype)) {
log_print(HT_LOG_WARN, "%s, Unsupported hardware mode %d for the current platform\r\n", __FUNCTION__, hwMode);
if (assignedGpu >= 0) pool.releaseSlot(assignedGpu);
return -1;
}
// Find the hardware device type by name
type = av_hwdevice_find_type_by_name(hwtype.c_str());
if (type == AV_HWDEVICE_TYPE_NONE) {
log_print(HT_LOG_WARN, "%s, Hardware device type %s is not supported\r\n", __FUNCTION__, hwtype.c_str());
logSupportedHwTypes();
if (assignedGpu >= 0) pool.releaseSlot(assignedGpu);
return -1;
}
// Find a hardware configuration that supports the specified device type
if (!findHwConfigForDeviceType(type)) {
log_print(HT_LOG_WARN, "%s, Decoder %s does not support the specified hardware device type %s\r\n",
__FUNCTION__, m_pCodec->long_name, av_hwdevice_get_type_name(type));
if (assignedGpu >= 0) pool.releaseSlot(assignedGpu);
return -1;
}
// Get or create a shared HW device context for this GPU.
// NVIDIA recommends sharing CUDA contexts across decode sessions to minimize
// GPU memory overhead (each CUDA context costs ~50-100MB).
// See: NVDEC Video Decoder API Programming Guide, Section "Multi-session decoding"
int gpuIdx = (assignedGpu >= 0) ? assignedGpu : 0;
m_pHWDeviceCtx = SharedHWDeviceCtx::instance().acquire(gpuIdx, type);
if (!m_pHWDeviceCtx) {
log_print(HT_LOG_ERR, "%s, Failed to acquire shared HW device context, type=%s, gpu=%d\r\n",
__FUNCTION__, av_hwdevice_get_type_name(type), gpuIdx);
if (assignedGpu >= 0) pool.releaseSlot(assignedGpu);
return -1;
}
// Configure the codec context to use the shared hardware device
ctx->opaque = this;
ctx->get_format = ::getHWFormat;
ctx->hw_device_ctx = av_buffer_ref(m_pHWDeviceCtx);
ctx->err_recognition = AV_EF_IGNORE_ERR;
ctx->flags2 |= AV_CODEC_FLAG2_EXPORT_MVS;
// Reserve extra NVDEC surfaces for application-held av_frame_clone() references.
// The clone chain (decoder → player → registry) holds ~2 surfaces simultaneously
// (decoder's clone + registry's clone; getCudaHWFrame uses ownership transfer).
// Without this, the default pool (num_decode_surfaces + 2) can run out under
// load with many concurrent streams, causing the decoder to stall.
ctx->extra_hw_frames = 2;
// Track which GPU this decoder is on
m_hwGpuIndex = assignedGpu;
m_bHardwareDecoderEnabled = TRUE;
m_bCudaHWAccel = (type == AV_HWDEVICE_TYPE_CUDA);
// Legacy counter (for backward compatibility)
if (!pool.isConfigured()) {
CLock lock(g_hw_decoder_mutex);
g_hw_decoder_nums++;
}
log_print(HT_LOG_INFO, "%s, Successfully initialized hardware decoder %s on GPU[%d] (%d/%d)\r\n",
__FUNCTION__, av_hwdevice_get_type_name(type),
gpuIdx,
pool.isConfigured() ? pool.getTotalActive() : g_hw_decoder_nums,
pool.isConfigured() ? pool.getTotalMax() : g_hw_decoder_max);
return 0;
}
void CVideoDecoder::Start() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (m_pContext) {
avcodec_flush_buffers(m_pContext);
}
m_bRunning = TRUE;
log_print(HT_LOG_INFO, "%s, Video decoder started\r\n", __FUNCTION__);
}
void CVideoDecoder::Stop() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
m_bRunning = FALSE;
log_print(HT_LOG_INFO, "%s, Video decoder stopped\r\n", __FUNCTION__);
}
// Log all supported hardware types
void CVideoDecoder::logSupportedHwTypes() {
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
log_print(HT_LOG_INFO, "%s, Available hardware device types:\r\n", __FUNCTION__);
while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
log_print(HT_LOG_INFO, "%s, - %s\r\n", __FUNCTION__, av_hwdevice_get_type_name(type));
}
}
// Platform-specific function to determine the hardware type based on the mode
bool CVideoDecoder::getHardwareTypeForPlatform(int hwMode, std::string& hwtype) {
#if __WINDOWS_OS__
switch (hwMode) {
case HW_DECODING_D3D11: hwtype = "d3d11va"; break;
case HW_DECODING_DXVA: hwtype = "dxva2"; break;
case HW_DECODING_CUDA: hwtype = "cuda"; break;
case HW_DECODING_AUTO:
hwtype = "cuda";
if (av_hwdevice_find_type_by_name(hwtype.c_str()) == AV_HWDEVICE_TYPE_NONE) {
hwtype = "d3d11va";
if (av_hwdevice_find_type_by_name(hwtype.c_str()) == AV_HWDEVICE_TYPE_NONE) {
hwtype = "dxva2";
}
}
break;
default: return false;
}
#elif defined(IOS)
switch (hwMode) {
case HW_DECODING_VIDEOTOOLBOX: hwtype = "videotoolbox"; break;
case HW_DECODING_OPENCL: hwtype = "opencl"; break;
case HW_DECODING_AUTO:
hwtype = "videotoolbox";
if (av_hwdevice_find_type_by_name(hwtype.c_str()) == AV_HWDEVICE_TYPE_NONE) {
hwtype = "opencl";
}
break;
default: return false;
}
#elif defined(ANDROID)
if (hwMode == HW_DECODING_MEDIACODEC || hwMode == HW_DECODING_AUTO) {
hwtype = "mediacodec";
}
else {
return false;
}
#elif __LINUX_OS__
switch (hwMode) {
case HW_DECODING_VAAPI: hwtype = "vaapi"; break;
case HW_DECODING_OPENCL: hwtype = "opencl"; break;
case HW_DECODING_AUTO:
hwtype = "vaapi";
if (av_hwdevice_find_type_by_name(hwtype.c_str()) == AV_HWDEVICE_TYPE_NONE) {
hwtype = "opencl";
}
break;
default: return false;
}
#else
return false; // Unsupported platform
#endif
return true;
}
// Find a hardware configuration that matches the specified device type
bool CVideoDecoder::findHwConfigForDeviceType(AVHWDeviceType type) {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(m_pCodec, i);
if (!config) {
return false; // No matching hardware configuration found
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
m_hwPixFmt = config->pix_fmt;
return true;
}
}
}
BOOL CVideoDecoder::getHWFormat(AVCodecContext* ctx, const AVPixelFormat* pix_fmts, AVPixelFormat* dst)
{
const AVPixelFormat* p;
*dst = AV_PIX_FMT_NONE;
// First, attempt to use hardware pixel format if available
for (p = pix_fmts; *p != -1; p++)
{
if (*p == m_hwPixFmt)
{
*dst = *p;
return TRUE;
}
}
// If hardware format is not supported, fall back to YUVJ420P
for (p = pix_fmts; *p != -1; p++)
{
if (*p == AV_PIX_FMT_YUVJ420P ||
*p == AV_PIX_FMT_YUVJ422P ||
*p == AV_PIX_FMT_YUVJ444P)
{
*dst = *p;
return TRUE;
}
}
// As a last resort, use other formats (YUV420P)
for (p = pix_fmts; *p != -1; p++)
{
if (*p == AV_PIX_FMT_YUV420P ||
*p == AV_PIX_FMT_YUV422P ||
*p == AV_PIX_FMT_YUV444P)
{
*dst = *p;
return TRUE;
}
}
if (*pix_fmts != -1)
{
*dst = *pix_fmts;
return TRUE;
}
log_print(HT_LOG_ERR, "%s, Failed to get HW surface format\r\n", __FUNCTION__);
return FALSE;
}
int CVideoDecoder::getWidth()
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (m_pContext)
{
return m_pContext->width;
}
return 0;
}
int CVideoDecoder::getHeight()
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (m_pContext)
{
return m_pContext->height;
}
return 0;
}
double CVideoDecoder::getFrameRate()
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (m_pContext)
{
if (m_pContext->framerate.den > 0)
{
return (double)((double)(m_pContext->framerate.num) / m_pContext->framerate.den);
}
}
return 0;
}
BOOL CVideoDecoder::decode(AVPacket* pkt)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (!m_bInited)
{
log_print(HT_LOG_ERR, "%s, Decoder not initialized\r\n", __FUNCTION__);
return FALSE;
}
if (!m_bRunning)
{
log_print(HT_LOG_WARN, "%s, Decoder not running\r\n", __FUNCTION__);
return FALSE;
}
if (!m_pContext) {
log_print(HT_LOG_ERR, "%s, Context is NULL\r\n", __FUNCTION__);
return FALSE;
}
int ret;
int retryCount = 0;
const int maxRetries = 3;
// Attempt to send packet to decoder
while ((ret = avcodec_send_packet(m_pContext, pkt)) == AVERROR(EAGAIN) &&
retryCount < maxRetries)
{
if (!readFrame())
{
log_print(HT_LOG_ERR, "%s, Failed to read frame during retry %d\r\n", __FUNCTION__, retryCount);
return FALSE;
}
Sleep(1); // Reduced sleep time
retryCount++;
}
// Check for other errors
if (ret < 0 && ret != AVERROR_EOF)
{
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, error_buf, sizeof(error_buf));
log_print(HT_LOG_ERR, "%s, avcodec_send_packet failed: %s (ret=%d)\r\n", __FUNCTION__, error_buf, ret);
return FALSE;
}
// If the packet was successfully sent, proceed to read frame
return readFrame();
}
BOOL CVideoDecoder::decode(uint8* data, int len, int64_t pts)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (!m_bInited)
{
log_print(HT_LOG_ERR, "%s, Decoder not initialized\r\n", __FUNCTION__);
return FALSE;
}
if (!m_bRunning)
{
log_print(HT_LOG_WARN, "%s, Decoder not running\r\n", __FUNCTION__);
return FALSE;
}
if (!data || len <= 0) {
log_print(HT_LOG_ERR, "%s, Invalid input data\r\n", __FUNCTION__);
return FALSE;
}
// Allocate packet
AVPacket* packet = av_packet_alloc();
if (!packet) {
log_print(HT_LOG_ERR, "%s, Failed to allocate AVPacket\r\n", __FUNCTION__);
return FALSE;
}
// FIXED: Use av_new_packet() to properly allocate and manage packet data
int ret = av_new_packet(packet, len);
if (ret < 0) {
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, error_buf, sizeof(error_buf));
log_print(HT_LOG_ERR, "%s, Failed to allocate packet data: %s\r\n", __FUNCTION__, error_buf);
av_packet_free(&packet);
return FALSE;
}
// Copy data - av_new_packet() already allocated the buffer with proper padding
memcpy(packet->data, data, len);
// Set packet timing information
packet->pts = pts;
packet->dts = pts;
// Call decode function
BOOL result = decode(packet);
// Clean up - av_packet_free will properly handle the data buffer
av_packet_free(&packet);
return result;
}
BOOL CVideoDecoder::readFrame()
{
int ret = 0;
AVFrame* tmp_frame = NULL;
BOOL frame_processed = FALSE;
while (ret >= 0)
{
ret = avcodec_receive_frame(m_pContext, m_pFrame);
if (ret == AVERROR(EAGAIN)) {
// Need more input data
return frame_processed ? TRUE : FALSE;
}
else if (ret == AVERROR_EOF) {
// End of stream
return TRUE;
}
else if (ret < 0) {
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, error_buf, sizeof(error_buf));
log_print(HT_LOG_ERR, "%s, avcodec_receive_frame failed: %s (ret=%d)\r\n", __FUNCTION__, error_buf, ret);
return FALSE;
}
// Check if we got a valid frame
if (!m_pFrame || m_pFrame->width <= 0 || m_pFrame->height <= 0) {
log_print(HT_LOG_WARN, "%s, Received invalid frame\r\n", __FUNCTION__);
av_frame_unref(m_pFrame);
continue;
}
if (m_pFrame->format == m_hwPixFmt)
{
// CUDA HW accel: clone the HW frame BEFORE transfer so inference
// can use CUDA device pointers directly (zero-copy, no upload).
if (m_bCudaHWAccel) {
if (m_pCudaHWFrame) av_frame_free(&m_pCudaHWFrame);
m_pCudaHWFrame = av_frame_clone(m_pFrame);
}
// FIXED: Ensure m_pSoftFrame is properly initialized before transfer
av_frame_unref(m_pSoftFrame); // Clear any previous data
// Hardware frame - transfer to software (needed for display)
ret = av_hwframe_transfer_data(m_pSoftFrame, m_pFrame, 0);
if (ret < 0)
{
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, error_buf, sizeof(error_buf));
log_print(HT_LOG_ERR, "%s, Error transferring hardware frame to system memory: %s (ret=%d)\r\n",
__FUNCTION__, error_buf, ret);
av_frame_unref(m_pFrame);
if (m_pCudaHWFrame) av_frame_free(&m_pCudaHWFrame);
continue;
}
// Copy timing information
m_pSoftFrame->pts = m_pFrame->pts;
m_pSoftFrame->pkt_dts = m_pFrame->pkt_dts;
m_pSoftFrame->best_effort_timestamp = m_pFrame->best_effort_timestamp;
tmp_frame = m_pSoftFrame;
}
else
{
// Software frame - use directly
tmp_frame = m_pFrame;
}
// Render the frame
if (tmp_frame) {
render(tmp_frame);
frame_processed = TRUE;
}
// FIXED: Ensure proper cleanup of frame references
if (tmp_frame == m_pSoftFrame) {
av_frame_unref(m_pSoftFrame);
}
av_frame_unref(m_pFrame);
}
return TRUE;
}
int CVideoDecoder::render(AVFrame* frame)
{
if (!m_bRunning || !frame)
{
return 0;
}
if (m_pCallback)
{
try {
m_pCallback(frame, m_pUserdata);
}
catch (...) {
log_print(HT_LOG_ERR, "%s, Exception in callback function\r\n", __FUNCTION__);
return 0;
}
}
return 1;
}
void CVideoDecoder::flush()
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (NULL == m_pContext ||
NULL == m_pContext->codec ||
!(m_pContext->codec->capabilities & AV_CODEC_CAP_DELAY))
{
return;
}
log_print(HT_LOG_INFO, "%s, Flushing decoder buffers\r\n", __FUNCTION__);
// Send NULL packet to flush
avcodec_send_packet(m_pContext, NULL);
// FIXED: Drain all frames after flushing
while (true) {
int ret = avcodec_receive_frame(m_pContext, m_pFrame);
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
break;
}
if (ret < 0) {
char error_buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, error_buf, sizeof(error_buf));
log_print(HT_LOG_WARN, "%s, Error during flush: %s\r\n", __FUNCTION__, error_buf);
break;
}
// Process the frame if needed, or just unref it
av_frame_unref(m_pFrame);
}
// Also flush the codec buffers
if (m_pContext) {
avcodec_flush_buffers(m_pContext);
}
}

View File

@@ -0,0 +1,172 @@
#ifndef VIDEO_DECODER_H
#define VIDEO_DECODER_H
#include "sys_inc.h"
#include "media_format.h"
#include <string>
#include <mutex>
#include <vector>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include <libavutil/opt.h>
}
#define HW_DECODING_AUTO 0 // automatic select video acceleration hardware
#define HW_DECODING_D3D11 1 // D3D11 video acceleration
#define HW_DECODING_DXVA 2 // DXVA video acceleration
#define HW_DECODING_VAAPI 3 // VAAPI video acceleration
#define HW_DECODING_OPENCL 4 // OPENCL video acceleration
#define HW_DECODING_VIDEOTOOLBOX 5 // VideoToolBox video acceleration
#define HW_DECODING_MEDIACODEC 6 // MediaCodec video acceleration
#define HW_DECODING_CUDA 7 // CUDA/NVDEC — decoded NV12 stays in GPU VRAM
#define HW_DECODING_DISABLE -1 // disable video acceleration
// Legacy global limit (default: 4). Still works if HWDecoderPool is not configured.
extern uint32 g_hw_decoder_max;
// ---------------------------------------------------------------------------
// HWDecoderPool -- per-GPU hardware decoder session manager
//
// Tracks active HW decoder sessions per GPU and distributes new sessions
// to the GPU with the fewest active decoders (least-loaded).
//
// Usage:
// // Auto-configure from outside (e.g., ANSRTSP):
// HWDecoderPool::instance().configure(numGpus, maxSessionsPerGpu);
//
// // Or leave unconfigured -- falls back to legacy g_hw_decoder_max behaviour.
// ---------------------------------------------------------------------------
class HWDecoderPool {
public:
static HWDecoderPool& instance();
// Configure uniform per-GPU limits. Call once at startup before creating decoders.
void configure(int numGpus, int maxPerGpu);
// Configure per-GPU limits individually (different GPUs may have different capabilities).
void configure(const std::vector<int>& maxPerGpuList);
// Is the pool configured with per-GPU tracking?
bool isConfigured() const;
// Try to acquire a HW decoder slot. Returns the GPU index to use,
// or -1 if all GPUs are at capacity.
// If preferredGpu >= 0, prefer that GPU (e.g. to match inference GPU for zero-copy).
// Falls back to least-loaded if preferred GPU is at capacity.
int acquireSlot(int preferredGpu = -1);
// Release a HW decoder slot on the given GPU.
void releaseSlot(int gpuIndex);
// Get total max sessions across all GPUs.
int getTotalMax() const;
// Get number of active sessions across all GPUs.
int getTotalActive() const;
private:
HWDecoderPool() = default;
std::mutex m_mutex;
bool m_configured = false;
std::vector<int> m_maxPerGpu; // max session limit per GPU
std::vector<int> m_activePerGpu; // active session count per GPU
};
// ---------------------------------------------------------------------------
// SharedHWDeviceCtx -- per-GPU shared AVHWDeviceContext cache
//
// NVIDIA recommends sharing CUDA contexts across decode sessions to reduce
// GPU memory overhead. This cache creates one AVHWDeviceContext per GPU
// and shares it (via av_buffer_ref) across all decoder sessions on that GPU.
//
// Thread-safe: all methods lock internally.
// ---------------------------------------------------------------------------
class SharedHWDeviceCtx {
public:
static SharedHWDeviceCtx& instance();
// Get (or create) a shared HW device context for the given GPU index and device type.
// Returns a new av_buffer_ref to the shared context (caller must av_buffer_unref).
// Returns nullptr on failure.
AVBufferRef* acquire(int gpuIndex, AVHWDeviceType type);
// Release all cached contexts (call at shutdown).
void releaseAll();
private:
SharedHWDeviceCtx() = default;
~SharedHWDeviceCtx();
struct GpuCtx {
AVBufferRef* ctx = nullptr;
AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
};
std::mutex m_mutex;
std::vector<GpuCtx> m_cache;
};
typedef void (*VDCB)(AVFrame* frame, void* pUserdata);
class CVideoDecoder
{
public:
CVideoDecoder();
~CVideoDecoder();
public:
BOOL init(int codec, uint8* extradata = NULL, int extradata_size = 0, int hwMode = HW_DECODING_AUTO, int preferredGpu = -1);
BOOL init(enum AVCodecID codec, uint8* extradata = NULL, int extradata_size = 0, int hwMode = HW_DECODING_AUTO, int preferredGpu = -1);
void uninit();
int getWidth();
int getHeight();
double getFrameRate();
BOOL decode(uint8* data, int len, int64_t pts = AV_NOPTS_VALUE);
BOOL decode(AVPacket* pkt);
void setCallback(VDCB pCallback, void* pUserdata) { m_pCallback = pCallback; m_pUserdata = pUserdata; }
BOOL getHWFormat(AVCodecContext* ctx, const AVPixelFormat* pix_fmts, AVPixelFormat* dst);
bool getHardwareTypeForPlatform(int hwMode, std::string& hwtype);
bool findHwConfigForDeviceType(AVHWDeviceType type);
void logSupportedHwTypes();
BOOL isHardwareDecoderEnabled() const { return m_bHardwareDecoderEnabled; }
int getHWGpuIndex() const { return m_hwGpuIndex; }
bool isCudaHWAccel() const { return m_bCudaHWAccel; }
// Returns the CUDA HW frame (device pointers). Caller takes ownership.
AVFrame* takeCudaHWFrame();
// Clone CUDA HW frame without locking — caller MUST already hold _mutex
// (used by onVideoFrame callback which runs inside decode()'s lock scope).
AVFrame* cloneCudaHWFrame_unlocked();
void Start();
void Stop();
void flush();
AVCodecContext* getAVCodeContext() {
return m_pContext;
}
private:
BOOL readFrame();
int render(AVFrame* frame);
int hwDecoderInit(AVCodecContext* ctx, int hwMode, int preferredGpu = -1);
private:
BOOL m_bInited;
BOOL m_bRunning;
BOOL m_bHardwareDecoderEnabled; // Track if hardware decoder is enabled
bool m_bCudaHWAccel; // true when using AV_HWDEVICE_TYPE_CUDA
int m_hwGpuIndex; // GPU index assigned by HWDecoderPool (-1 = legacy)
AVFrame* m_pCudaHWFrame; // Cloned CUDA HW frame (device ptrs) for inference
const AVCodec* m_pCodec;
AVCodecContext* m_pContext;
AVFrame* m_pFrame;
AVFrame* m_pSoftFrame;
VDCB m_pCallback;
void* m_pUserdata;
AVPixelFormat m_hwPixFmt;
AVBufferRef* m_pHWDeviceCtx;
std::recursive_mutex _mutex;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,340 @@
#ifndef VIDEO_PLAYER_H
#define VIDEO_PLAYER_H
#include "sys_inc.h"
#include "hqueue.h"
#include "video_decoder.h"
#include "audio_decoder.h"
#include "audio_play.h"
#include "avi_write.h"
#include "media_util.h"
#include <list>
#include <string>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <turbojpeg.h>
typedef struct
{
uint32 SyncTimestamp;
struct timeval SyncTime;
} HTCLOCK;
typedef std::list<AVFrame*> FRAMELIST;
// Thread-safe queue for AVFrame
class FrameQueue {
private:
std::queue<AVFrame*> frameQueue;
std::mutex queueMutex;
const size_t maxFrames = 20; // Increased buffer size for better performance
uint64_t m_frameSeq = 0; // Sequence counter — increments on each new frame push
public:
// Push a new frame to the queue (removes oldest if full)
void pushFrame(AVFrame* newFrame) {
if (!newFrame) return;
std::lock_guard<std::mutex> lock(queueMutex);
// Clone the frame to ensure proper ownership transfer
AVFrame* frameCopy = av_frame_clone(newFrame);
if (!frameCopy) {
std::cerr << "Failed to clone AVFrame!" << std::endl;
return;
}
frameQueue.push(frameCopy);
m_frameSeq++; // New frame arrived
// If queue exceeds max size, remove the oldest frame
if (frameQueue.size() > maxFrames) {
AVFrame* oldFrame = frameQueue.front();
frameQueue.pop();
av_frame_free(&oldFrame);
}
}
// Get current sequence number (check if new frames arrived)
uint64_t getSequence() {
std::lock_guard<std::mutex> lock(queueMutex);
return m_frameSeq;
}
// Retrieve latest frame (returns a clone for thread safety)
AVFrame* getLatestFrame() {
std::lock_guard<std::mutex> lock(queueMutex);
if (frameQueue.empty()) {
return nullptr; // No frames available
}
// Clone the latest frame before returning it
return av_frame_clone(frameQueue.back());
}
// Retrieve and remove the oldest frame from the queue
AVFrame* popFrame() {
std::lock_guard<std::mutex> lock(queueMutex);
if (frameQueue.empty()) {
return nullptr;
}
AVFrame* frontFrame = frameQueue.front();
frameQueue.pop();
return frontFrame; // Caller must free the returned frame
}
// Check if the queue is empty
bool isEmpty() {
std::lock_guard<std::mutex> lock(queueMutex);
return frameQueue.empty();
}
// Clear the queue (e.g., when shutting down)
void clearQueue() {
std::lock_guard<std::mutex> lock(queueMutex);
while (!frameQueue.empty()) {
AVFrame* frame = frameQueue.front();
frameQueue.pop();
av_frame_free(&frame);
}
m_frameSeq = 0;
}
};
class CVideoPlayer
{
public:
CVideoPlayer();
virtual ~CVideoPlayer();
virtual BOOL open(std::string fileName);
virtual BOOL open(std::string username, std::string password, std::string url);
virtual BOOL play() = 0;
virtual void stop() = 0;
virtual BOOL pause() = 0;
virtual void close();
virtual BOOL seek(int pos) { return FALSE; }
virtual BOOL isPlaying() { return m_bPlaying; }
virtual BOOL isPaused() { return m_bPaused; }
virtual void setVolume(int volume);
virtual void snapshot(int videofmt);
virtual BOOL record(std::string baseName);
virtual void stopRecord();
virtual BOOL isRecording() { return m_bRecording; }
virtual BOOL onRecord() { return FALSE; }
virtual int getVideoClock() { return 1000; }
virtual int getAudioClock() { return 1000; }
//virtual void setWindowSize(int size);
virtual int64 getElapse() { return 0; }
virtual int64 getDuration() { return 0; }
virtual int getVideoCodec() { return m_nVideoCodec; }
virtual int getAudioCodec() { return m_nAudioCodec; }
virtual void setAuthInfo(std::string acct, std::string pass) { m_acct = acct; m_pass = pass; }
//virtual void setRenderMode(int mode) { m_nRenderMode = mode; }
virtual void setHWDecoding(int mode, int preferredGpu = -1) { m_nHWDecoding = mode; m_nPreferredGpu = preferredGpu; }
virtual bool isHWDecodingActive() const {
return m_pVideoDecoder && m_pVideoDecoder->isHardwareDecoderEnabled();
}
virtual int getHWDecodingGpuIndex() const {
return m_pVideoDecoder ? m_pVideoDecoder->getHWGpuIndex() : -1;
}
// Image quality mode: 0=fast (OpenCV BT.601, ~2ms), 1=quality (sws BT.709+range, ~12ms)
virtual void setImageQuality(int mode) { m_nImageQuality = mode; }
virtual void setRtpMulticast(BOOL flag) {}
virtual void setRtpOverUdp(BOOL flag) {}
#ifdef OVER_HTTP
virtual void setRtspOverHttp(int flag, int port) {}
#endif
#ifdef OVER_WEBSOCKET
virtual void setRtspOverWs(int flag, int port) {}
#endif
#ifdef BACKCHANNEL
virtual int getBCFlag() { return 0; }
virtual void setBCFlag(int flag) {}
virtual int getBCDataFlag() { return 0; }
virtual void setBCDataFlag(int flag) {}
virtual void setAudioDevice(int index) {}
#endif
#ifdef REPLAY
virtual int getReplayFlag() { return 0; }
virtual void setReplayFlag(int flag) {}
virtual void setScale(double scale) {}
virtual void setRateControlFlag(int flag) {}
virtual void setImmediateFlag(int flag) {}
virtual void setFramesFlag(int flag, int interval) {}
virtual void setReplayRange(time_t start, time_t end) {}
#endif
BOOL openVideo(int codec, uint8* extradata = NULL, int extradata_size = 0);
BOOL openVideo(enum AVCodecID codec, uint8* extradata = NULL, int extradata_size = 0);
void closeVideo();
BOOL openAudio(int codec, int samplerate, int channels, int bitpersample = 0);
BOOL openAudio(enum AVCodecID codec, int samplerate, int channels, int bitpersample = 0);
void closeAudio();
void enableAudio(bool status);
void playVideo(uint8* data, int len, uint32 ts, uint16 seq);
void playAudio(uint8* data, int len, uint32 ts, uint16 seq);
void recordVideo(uint8* data, int len, uint32 ts, uint16 seq);
void recordAudio(uint8* data, int len, uint32 ts, uint16 seq);
int getVideoWidth();
int getVideoHeight();
int getOriginalWidth() { return m_nv12OrigWidth; } // Full NV12 resolution (before display downscale)
int getOriginalHeight() { return m_nv12OrigHeight; } // Full NV12 resolution (before display downscale)
double getFrameRate();
int getSampleRate() { return m_nSampleRate; }
int getChannel() { return m_nChannel; }
void onVideoFrame(AVFrame* frame);
void onAudioFrame(AVFrame* frame);
void StartVideoDecoder();
void StopVideoDecoder();
cv::Mat getImage(int& width, int& height, int64_t& pts);// Get image
std::string getJpegImage(int& width, int& height, int64_t& pts);// Get image
AVFrame* getNV12Frame(); // Transfers ownership of NV12/YUV frame for GPU fast-path (caller must av_frame_free)
AVFrame* getCudaHWFrame(); // Returns clone of CUDA HW frame (device ptrs); caller must av_frame_free
bool isCudaHWAccel() const;
void setBbox(cv::Rect bbox);
void setCrop(bool crop);
protected:
void updateClock(HTCLOCK* clock, uint32 ts, int frequency);
BOOL initFrame(AVFrame*& frame, int width, int height, AVPixelFormat pixfmt);
BOOL doSnapshot(AVFrame* srcframe);
AVFrame* convertFrame(AVFrame* srcframe, AVFrame* dstframe, BOOL updown);
void recordVideoEx(uint8* data, int len, uint32 ts, uint16 seq);
BOOL recordSwitchCheck();
void recordFileSwitch();
AVFrame* cropFrame(const AVFrame* srcFrame, cv::Rect bBox, bool cropFlag);
cv::Mat avframeAnyToCvmat(const AVFrame* frame);
cv::Mat avframeNV12ToCvMat(const AVFrame* frame);
cv::Mat avframeYUVJ420PToCvmat(const AVFrame* frame);
cv::Mat avframeToCVMat(const AVFrame* frame);
// YUVJ420P to JPEG
std::string avframeYUVJ420PToJpegString(const AVFrame* pFrame);
std::string avframeYUVJ420PToJpegStringUsingTurboJPEG(const AVFrame* pFrame);
std::string avframeYUVJ420PToJpegStringUsingFFMpeg(const AVFrame* pFrame);
AVFrame* convertNV12ToYUVJ420P(const AVFrame* nv12Frame);
std::string encodeYUVJ420PToJPEG(AVFrame* frame, int quality = 90);
//Direct conversion from NV12 to JPEG
std::string avframeToJpegString(const AVFrame* pFrame);
std::string encodeNV12ToJPEG_TurboJPEG(const AVFrame* frame, int quality = 90);
std::string encodeNV12ToJPEG_FFmpeg(const AVFrame* pFrame, int quality = 90);
// Check if frame are identical
bool areFramesIdentical(AVFrame* frame1, AVFrame* frame2);
protected:
// Use a constant for the maximum queue size
//const int MAX_VIDEO_FRAMES = 10;
//const int MAX_AUDIO_FRAMES = 2;
BOOL m_bVideoInited;
BOOL m_bAudioInited;
tjhandle _tjInstance;
std::recursive_mutex _mutex;
std::unique_ptr<CVideoDecoder> m_pVideoDecoder = std::make_unique<CVideoDecoder>();
std::unique_ptr<CAudioDecoder> m_pAudioDecoder = std::make_unique<CAudioDecoder>();
std::unique_ptr<CAudioPlay> m_pAudioPlay = nullptr;
cv::Mat m_currentImage;
AVFrame* m_currentNV12Frame = nullptr; // Preserved NV12/YUV frame for GPU fast-path
AVFrame* m_currentCudaHWFrame = nullptr; // CUDA HW frame with device ptrs for zero-copy
int m_Width;
int m_Height;
int m_nv12OrigWidth = 0; // Original NV12 frame width (before display resize)
int m_nv12OrigHeight = 0; // Original NV12 frame height (before display resize)
int64_t m_pts;
uint64_t m_lastFrameSeq = 0; // Last processed frame sequence number
bool m_bWaitingForKeyframe = true; // Skip frames until first keyframe after start/restart
int m_cleanFrameCount = 0; // Count of clean frames after keyframe
static const int SETTLE_FRAME_COUNT = 5; // Number of clean frames before delivering new frames
BOOL m_bPlaying;
BOOL m_bPaused;
std::string m_acct;
std::string m_pass;
std::string m_sFileName;
std::string m_sBaseName;
std::string m_jpegImage;
std::string m_lastJpegImage;
int m_nHWDecoding;
int m_nPreferredGpu = -1; // -1=auto (least-loaded), >=0 = prefer this GPU for NVDEC
int m_nImageQuality = 0; // 0=fast (default), 1=quality BT.709
//AVPixelFormat m_nDstVideoFmt;
BOOL m_bUpdown;
BOOL m_bSnapshot;
int m_nSnapVideoFmt;
H26XParamSets m_h26XParamSets;
int m_nVideoCodec;
int m_nAudioCodec;
int m_nSampleRate;
int m_nChannel;
int m_nBitPerSample;
AVFrame* m_pSnapFrame;
FrameQueue g_frameQueue;
FrameQueue a_frameQueue;
BOOL m_bRecording;
BOOL m_bNalFlag;
AVICTX* m_pAviCtx;
void* m_pRecordMutex;
HTCLOCK m_audioClock;
void* m_pAudioListMutex;
//FRAMELIST m_audioFrameList;
BOOL m_audioPlayFlag;
//pthread_t m_audioPlayThread;
HTCLOCK m_videoClock;
void* m_pVideoListMutex;
//FRAMELIST m_videoFrameList;
BOOL m_videoPlayFlag;
//pthread_t m_videoPlayThread;
uint64 m_nLastAudioPts;
time_t m_lastAudioTS;
cv::Rect m_Bbox;
bool m_bCrop;
SwsContext* swsCtx = nullptr; // Shared SwsContext
int lastWidth = 0, lastHeight = 0;
AVPixelFormat lastPixFmt = AV_PIX_FMT_NONE;
AVPixelFormat lastOutPixFmt = AV_PIX_FMT_NONE;
// Dedicated NV12→BGR context with correct color space (BT.709 for HD/4K)
SwsContext* m_nv12SwsCtx = nullptr;
int m_nv12LastWidth = 0, m_nv12LastHeight = 0;
int m_nv12LastColorspace = -1;
int m_nv12LastRange = -1;
void initSwsContext(int width, int height, AVPixelFormat pixFmt, AVPixelFormat outputPixFmt = AV_PIX_FMT_YUVJ420P);
void initNV12SwsContext(const AVFrame* frame);
void cropPlane(const AVFrame* srcFrame, AVFrame* croppedFrame, int planeIndex, int offsetX, int offsetY, int subsampleX, int subsampleY);
bool cropFrameData(const AVFrame* srcFrame, AVFrame* croppedFrame, const cv::Rect& bBox);
};
#endif // end of VIDEO_PLAYER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "video_render.h"
CVideoRender::CVideoRender()
: m_bInited(FALSE)
, m_hWnd(0)
, m_nVideoWidth(0)
, m_nVideoHeight(0)
, m_nVideoFmt(VIDEO_FMT_YUV420P)
{
}
CVideoRender::~CVideoRender()
{
unInit();
}
BOOL CVideoRender::init(WId hWnd, int w, int h, int videofmt)
{
m_hWnd = hWnd;
m_nVideoWidth = w;
m_nVideoHeight = h;
m_nVideoFmt = videofmt;
return TRUE;
}
void CVideoRender::unInit()
{
m_bInited = FALSE;
}
BOOL CVideoRender::render(AVFrame * frame, int mode)
{
return FALSE;
}

View File

@@ -0,0 +1,63 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef VIDEO_RENDER_H
#define VIDEO_RENDER_H
#include "media_format.h"
#include <QWidget>
extern "C"
{
#include <libavcodec/avcodec.h>
}
/***************************************************************************************/
#define RENDER_MODE_KEEP 0
#define RENDER_MODE_FILL 1
/***************************************************************************************/
class CVideoRender
{
public:
CVideoRender();
virtual ~CVideoRender();
virtual BOOL init(WId hWnd, int w, int h, int videofmt);
virtual void unInit();
virtual BOOL render(AVFrame * frame, int mode);
virtual void setWindowSize(QSize size) {}
virtual void setWindowTitle(const char * title) {}
protected:
BOOL m_bInited;
WId m_hWnd;
int m_nVideoWidth;
int m_nVideoHeight;
int m_nVideoFmt;
};
#endif // end of VIDEO_RENDER_H

View File

@@ -0,0 +1,829 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "video_render_d3d.h"
#include "lock.h"
/***************************************************************************************/
static void * g_d3d_mutex = sys_os_create_mutex();
const DWORD g_YUV420ps30_main[] =
{
0xffff0300, 0x003ffffe, 0x42415443, 0x0000001c, 0x000000c7, 0xffff0300,
0x00000004, 0x0000001c, 0x20000102, 0x000000c0, 0x0000006c, 0x00010003,
0x00000001, 0x00000074, 0x00000000, 0x00000084, 0x00020003, 0x00000001,
0x00000074, 0x00000000, 0x0000008c, 0x00000003, 0x00000001, 0x00000074,
0x00000000, 0x00000094, 0x00000002, 0x00000001, 0x000000a0, 0x000000b0,
0x78655455, 0x00657574, 0x000c0004, 0x00010001, 0x00000001, 0x00000000,
0x78655456, 0x00657574, 0x78655459, 0x00657574, 0x6e617274, 0x72617073,
0x00746e65, 0x00030000, 0x00010001, 0x00000001, 0x00000000, 0x3f800000,
0x00000000, 0x00000000, 0x00000000, 0x335f7370, 0x4d00305f, 0x6f726369,
0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320,
0x656c6970, 0x2e392072, 0x392e3432, 0x322e3934, 0x00373033, 0x05000051,
0xa00f0001, 0x3f950a81, 0x3fcc4a9d, 0xbf5fcbb4, 0x00000000, 0x05000051,
0xa00f0002, 0x3f950a81, 0xbec89507, 0xbf501eac, 0x3f081b65, 0x05000051,
0xa00f0003, 0x3f950a81, 0x40011a54, 0xbf8af5f5, 0x00000000, 0x05000051,
0xa00f0004, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x0200001f,
0x80000005, 0x90030000, 0x0200001f, 0x80010005, 0x90030001, 0x0200001f,
0x80020005, 0x90030002, 0x0200001f, 0x90000000, 0xa00f0800, 0x0200001f,
0x90000000, 0xa00f0801, 0x0200001f, 0x90000000, 0xa00f0802, 0x03000042,
0x800f0000, 0x90e40002, 0xa0e40802, 0x02000001, 0x80040000, 0x80000000,
0x03000042, 0x800f0001, 0x90e40000, 0xa0e40800, 0x04000004, 0x80090000,
0x80000001, 0xa0640004, 0xa0250004, 0x03000008, 0x80010800, 0xa0e40001,
0x80f80000, 0x03000042, 0x800f0001, 0x90e40001, 0xa0e40801, 0x02000001,
0x80020000, 0x80000001, 0x03000009, 0x80020800, 0xa0e40002, 0x80e40000,
0x03000008, 0x80040800, 0xa0e40003, 0x80f40000, 0x02000001, 0x80080800,
0xa0000000, 0x0000ffff
};
const DWORD g_YUV420ps20_main[] =
{
0xffff0200, 0x003ffffe, 0x42415443, 0x0000001c, 0x000000c7, 0xffff0200,
0x00000004, 0x0000001c, 0x20000102, 0x000000c0, 0x0000006c, 0x00010003,
0x00000001, 0x00000074, 0x00000000, 0x00000084, 0x00020003, 0x00000001,
0x00000074, 0x00000000, 0x0000008c, 0x00000003, 0x00000001, 0x00000074,
0x00000000, 0x00000094, 0x00000002, 0x00000001, 0x000000a0, 0x000000b0,
0x78655455, 0x00657574, 0x000c0004, 0x00010001, 0x00000001, 0x00000000,
0x78655456, 0x00657574, 0x78655459, 0x00657574, 0x6e617274, 0x72617073,
0x00746e65, 0x00030000, 0x00010001, 0x00000001, 0x00000000, 0x3f800000,
0x00000000, 0x00000000, 0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369,
0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320,
0x656c6970, 0x2e392072, 0x392e3432, 0x322e3934, 0x00373033, 0x05000051,
0xa00f0001, 0x3f950a81, 0x00000000, 0x3fcc4a9d, 0xbf5fcbb4, 0x05000051,
0xa00f0002, 0x3f950a81, 0xbec89507, 0xbf501eac, 0x3f081b65, 0x05000051,
0xa00f0003, 0x3f950a81, 0x40011a54, 0x00000000, 0xbf8af5f5, 0x05000051,
0xa00f0004, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x0200001f,
0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb0030001, 0x0200001f,
0x80000000, 0xb0030002, 0x0200001f, 0x90000000, 0xa00f0800, 0x0200001f,
0x90000000, 0xa00f0801, 0x0200001f, 0x90000000, 0xa00f0802, 0x03000042,
0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40001,
0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40002, 0xa0e40802, 0x02000001,
0x80080003, 0xa0000000, 0x02000001, 0x80020000, 0x80000001, 0x02000001,
0x80040000, 0x80000002, 0x02000001, 0x80080000, 0xa0000004, 0x03000009,
0x80010003, 0xa0e40001, 0x80e40000, 0x03000009, 0x80020003, 0xa0e40002,
0x80e40000, 0x03000009, 0x80040003, 0xa0e40003, 0x80e40000, 0x02000001,
0x800f0800, 0x80e40003, 0x0000ffff
};
/***************************************************************************************/
CD3DVideoRender::CD3DVideoRender()
: CVideoRender()
, m_pD3D9(NULL)
, m_pD3DD9(NULL)
, m_pRenderSurface(NULL)
, m_pVertices(NULL)
, m_pPixelShader(NULL)
, m_pPixelConstTable(NULL)
, m_hTransparent(NULL)
, m_nFlip(GS_NONE)
, m_dTransparent(0.0f)
, m_dwColor(0)
, m_bReset(FALSE)
{
m_pTexture[0] = NULL;
m_pTexture[1] = NULL;
m_pTexture[2] = NULL;
m_pTexTemp[0] = NULL;
m_pTexTemp[1] = NULL;
m_pTexTemp[2] = NULL;
m_hYUVTexture[0] = NULL;
m_hYUVTexture[1] = NULL;
m_hYUVTexture[2] = NULL;
memset(&m_dstRect, 0, sizeof(RECT));
memset(&m_srcRect, 0, sizeof(RECT));
}
CD3DVideoRender::~CD3DVideoRender()
{
unInit();
}
BOOL CD3DVideoRender::init(WId wid, int w, int h, int videofmt)
{
HWND hWnd = (HWND)wid;
CLock lock(g_d3d_mutex);
m_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);
if (NULL == m_pD3D9)
{
log_print(HT_LOG_ERR, "%s, Direct3DCreate9 failed\r\n", __FUNCTION__);
return FALSE;
}
HRESULT hr;
D3DPRESENT_PARAMETERS D3DPP;
initD3DPP(&D3DPP, hWnd);
if (!initDevice(&D3DPP, hWnd))
{
log_print(HT_LOG_ERR, "%s, initDevice failed\r\n", __FUNCTION__);
return FALSE;
}
hr = m_pD3DD9->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX3);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetFVF failed\r\n", __FUNCTION__);
return FALSE;
}
if (initShader(g_YUV420ps30_main, g_YUV420ps20_main) == FALSE)
{
log_print(HT_LOG_ERR, "%s, initShader failed\r\n", __FUNCTION__);
return FALSE;
}
hr = m_pD3DD9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, Clear failed\r\n", __FUNCTION__);
return FALSE;
}
CVideoRender::init(wid, w, h, videofmt);
m_bInited = initImageBuffer(w, h, videofmt);
return m_bInited;
}
void CD3DVideoRender::unInit()
{
CLock lock(g_d3d_mutex);
freeImageBuffer();
if (m_pPixelShader)
{
m_pPixelShader->Release();
m_pPixelShader = NULL;
}
if (m_pPixelConstTable)
{
m_pPixelConstTable->Release();
m_pPixelConstTable = NULL;
}
if (m_pD3DD9)
{
m_pD3DD9->Release();
m_pD3DD9 = NULL;
}
if (m_pD3D9)
{
m_pD3D9->Release();
m_pD3D9 = NULL;
}
m_bInited = FALSE;
}
BOOL CD3DVideoRender::initD3DPP(D3DPRESENT_PARAMETERS * pD3DPP, HWND hWnd)
{
memset(pD3DPP, 0, sizeof(D3DPRESENT_PARAMETERS));
pD3DPP->Windowed = TRUE;
pD3DPP->hDeviceWindow = hWnd;
pD3DPP->Flags = D3DPRESENTFLAG_VIDEO;
pD3DPP->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
pD3DPP->BackBufferCount = 1;
pD3DPP->BackBufferFormat = D3DFMT_UNKNOWN;
pD3DPP->BackBufferWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
pD3DPP->BackBufferHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
//
// Mark the back buffer lockable if software DXVA2 could be used.
// This is because software DXVA2 device requires a lockable render target
// for the optimal performance.
//
pD3DPP->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
pD3DPP->SwapEffect = D3DSWAPEFFECT_DISCARD;
return TRUE;
}
BOOL CD3DVideoRender::initDevice(D3DPRESENT_PARAMETERS * pD3DPP, HWND hWnd)
{
HRESULT hr;
UINT AdapterToUse = D3DADAPTER_DEFAULT;
D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
DWORD thread_modes[] = { D3DCREATE_MULTITHREADED, 0 };
DWORD vertex_modes[] = { D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
D3DCREATE_MIXED_VERTEXPROCESSING,
D3DCREATE_SOFTWARE_VERTEXPROCESSING };
for (size_t t = 0; t < ARRAY_SIZE(thread_modes); t++)
{
for (size_t v = 0; v < ARRAY_SIZE(vertex_modes); v++)
{
DWORD creationFlags = thread_modes[t] | vertex_modes[v];
hr = m_pD3D9->CreateDevice(AdapterToUse,
DeviceType, hWnd,
creationFlags,
pD3DPP, &m_pD3DD9);
if (SUCCEEDED(hr))
{
return TRUE;
}
}
}
return FALSE;
}
BOOL CD3DVideoRender::initImageBuffer(int w, int h, int videofmt)
{
RECT rect;
GetClientRect((HWND)m_hWnd, &rect);
m_srcRect.left = m_srcRect.top = 0;
m_srcRect.right = w;
m_srcRect.bottom = h;
m_dstRect.left = rect.left;
m_dstRect.top = rect.top;
m_dstRect.right = rect.right;
m_dstRect.bottom = rect.bottom;
m_verPoint[0] = D3DXVECTOR4(m_dstRect.left-0.5f, m_dstRect.top-0.5f, 0.0f, 1.0f);
m_verPoint[1] = D3DXVECTOR4(m_dstRect.right-0.5f, m_dstRect.top-0.5f, 0.0f, 1.0f);
m_verPoint[2] = D3DXVECTOR4(m_dstRect.right-0.5f, m_dstRect.bottom-0.5f, 0.0f, 1.0f);
m_verPoint[3] = D3DXVECTOR4(m_dstRect.left-0.5f, m_dstRect.bottom-0.5f, 0.0f, 1.0f);
float dx = 1.0f / w;
float dy = 1.0f / h;
m_texPoint[0] = D3DXVECTOR2(dx * m_srcRect.left, dy * m_srcRect.top);
m_texPoint[1] = D3DXVECTOR2(dx * m_srcRect.right, dy * m_srcRect.top);
m_texPoint[2] = D3DXVECTOR2(dx * m_srcRect.right, dy * m_srcRect.bottom);
m_texPoint[3] = D3DXVECTOR2(dx * m_srcRect.left, dy * m_srcRect.bottom);
HRESULT hr = m_pD3DD9->CreateVertexBuffer(4 * sizeof(YUV_VERTEX),
D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX3, D3DPOOL_DEFAULT, &m_pVertices, NULL);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, CreateVertexBuffer failed\r\n", __FUNCTION__);
goto done;
}
int i;
YUV_VERTEX * pVB;
hr = m_pVertices->Lock(0, 0, (void**)&pVB, 0);
if (FAILED(hr))
{
goto done;
}
for (i = 0; i < 4; i++)
{
pVB[i].pos = m_verPoint[i];
pVB[i].color = m_dwColor;
pVB[i].tex1 = pVB[i].tex2 = pVB[i].tex3 = m_texPoint[i];
}
hr = m_pVertices->Unlock();
if (FAILED(hr))
{
goto done;
}
for (i = 0; i < 3; i++)
{
int Width = w;
int Height = h;
if (i != 0)
{
Width /= 2;
Height /= 2;
}
hr = m_pD3DD9->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &m_pTexture[i], NULL);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, CreateTexture failed\r\n", __FUNCTION__);
goto done;
}
}
m_pTexTemp[Y_TEX] = m_pTexture[Y_TEX];
if (VIDEO_FMT_YV12 == videofmt)
{
m_pTexTemp[U_TEX] = m_pTexture[V_TEX];
m_pTexTemp[V_TEX] = m_pTexture[U_TEX];
}
else if (VIDEO_FMT_YUV420P == videofmt)
{
m_pTexTemp[U_TEX] = m_pTexture[U_TEX];
m_pTexTemp[V_TEX] = m_pTexture[V_TEX];
}
return TRUE;
done:
return FALSE;
}
void CD3DVideoRender::freeImageBuffer()
{
if (m_pVertices)
{
m_pVertices->Release();
m_pVertices = NULL;
}
for (int i = 0 ; i < COUNT_YUV_TEX; i++)
{
if (m_pTexture[i])
{
m_pTexture[i]->Release();
m_pTexture[i] = NULL;
}
}
}
BOOL CD3DVideoRender::initShader(const DWORD * pCode3, const DWORD * pCode2)
{
D3DCAPS9 caps;
HRESULT hr = S_OK;
hr = m_pD3DD9->GetDeviceCaps(&caps);
if (FAILED(hr))
{
goto done;
}
const DWORD * pCode;
if (caps.PixelShaderVersion >= D3DPS_VERSION(3, 0))
{
pCode = pCode3;
}
else
{
pCode = pCode2;
}
hr = m_pD3DD9->CreatePixelShader(pCode, &m_pPixelShader);
if (FAILED(hr))
{
goto done;
}
hr = D3DXGetShaderConstantTable(pCode, &m_pPixelConstTable);
if (FAILED(hr))
{
goto done;
}
m_hYUVTexture[Y_TEX] = m_pPixelConstTable->GetConstantByName(NULL, "YTextue");
if (m_hYUVTexture[Y_TEX] == NULL)
{
goto done;
}
m_hYUVTexture[U_TEX] = m_pPixelConstTable->GetConstantByName(NULL, "UTextue");
if (m_hYUVTexture[U_TEX] == NULL)
{
goto done;
}
m_hYUVTexture[V_TEX] = m_pPixelConstTable->GetConstantByName(NULL, "VTextue");
if (m_hYUVTexture[V_TEX] == NULL)
{
goto done;
}
UINT count;
hr = m_pPixelConstTable->GetConstantDesc(m_hYUVTexture[Y_TEX], &m_dYUVTexture[Y_TEX], &count);
if (FAILED(hr))
{
goto done;
}
hr = m_pPixelConstTable->GetConstantDesc(m_hYUVTexture[U_TEX], &m_dYUVTexture[U_TEX], &count);
if (FAILED(hr))
{
goto done;
}
hr = m_pPixelConstTable->GetConstantDesc(m_hYUVTexture[V_TEX], &m_dYUVTexture[V_TEX], &count);
if (FAILED(hr))
{
goto done;
}
m_hTransparent = m_pPixelConstTable->GetConstantByName(NULL, "transparent");
if (m_hTransparent == NULL)
{
goto done;
}
hr = m_pPixelConstTable->SetDefaults(m_pD3DD9);
done:
return SUCCEEDED(hr) ? TRUE : FALSE;
}
BOOL CD3DVideoRender::resetDevice()
{
freeImageBuffer();
if (m_pPixelShader)
{
m_pPixelShader->Release();
m_pPixelShader = NULL;
}
if (m_pPixelConstTable)
{
m_pPixelConstTable->Release();
m_pPixelConstTable = NULL;
}
HRESULT hr;
D3DPRESENT_PARAMETERS D3DPP;
initD3DPP(&D3DPP, (HWND)m_hWnd);
hr = m_pD3DD9->Reset(&D3DPP);
if (FAILED(hr))
{
return FALSE;
}
hr = m_pD3DD9->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX3);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetFVF failed\r\n", __FUNCTION__);
return FALSE;
}
if (initShader(g_YUV420ps30_main, g_YUV420ps20_main) == FALSE)
{
log_print(HT_LOG_ERR, "%s, initShader failed\r\n", __FUNCTION__);
return FALSE;
}
hr = m_pD3DD9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, Clear failed\r\n", __FUNCTION__);
return FALSE;
}
return initImageBuffer(m_nVideoWidth, m_nVideoHeight, m_nVideoFmt);
}
BOOL CD3DVideoRender::setImageArgs(float Transparent, GEOMETRICTRANSFORMATION flip, RECT * pDstRect, RECT * pSrcRect)
{
BOOL bSet = FALSE;
if (m_dTransparent != Transparent)
{
m_dTransparent = Transparent;
m_dwColor = ((DWORD)( 255 * Transparent ) << 24) | 0x00ffffff;
bSet = TRUE;
}
if (memcmp(&m_dstRect, pDstRect, sizeof(RECT)) != 0)
{
memcpy(&m_dstRect, pDstRect, sizeof(RECT));
m_verPoint[0] = D3DXVECTOR4(m_dstRect.left-0.5f, m_dstRect.top-0.5f, 0.0f, 1.0f);
m_verPoint[1] = D3DXVECTOR4(m_dstRect.right-0.5f, m_dstRect.top-0.5f, 0.0f, 1.0f);
m_verPoint[2] = D3DXVECTOR4(m_dstRect.right-0.5f, m_dstRect.bottom-0.5f, 0.0f, 1.0f);
m_verPoint[3] = D3DXVECTOR4(m_dstRect.left-0.5f, m_dstRect.bottom-0.5f, 0.0f, 1.0f);
bSet = TRUE;
}
if (memcmp(&m_srcRect, pSrcRect, sizeof(RECT)) != 0 || flip != m_nFlip)
{
float dx = 1.0f / m_nVideoWidth;
float dy = 1.0f / m_nVideoHeight;
memcpy(&m_srcRect, pSrcRect, sizeof(RECT));
m_texPoint[0] = D3DXVECTOR2(dx * m_srcRect.left, dy * m_srcRect.top);
m_texPoint[1] = D3DXVECTOR2(dx * m_srcRect.right, dy * m_srcRect.top);
m_texPoint[2] = D3DXVECTOR2(dx * m_srcRect.right, dy * m_srcRect.bottom);
m_texPoint[3] = D3DXVECTOR2(dx * m_srcRect.left, dy * m_srcRect.bottom);
if (flip & GS_UPPER_DOWN_FLIP)
{
D3DXVECTOR2 temp = m_texPoint[0];
m_texPoint[0] = m_texPoint[3];
m_texPoint[3] = temp;
temp = m_texPoint[1];
m_texPoint[1] = m_texPoint[2];
m_texPoint[2] = temp;
}
if (flip & GS_LEFT_RIGHT_FLIP)
{
D3DXVECTOR2 temp = m_texPoint[0];
m_texPoint[0] = m_texPoint[1];
m_texPoint[1] = temp;
temp = m_texPoint[2];
m_texPoint[2] = m_texPoint[3];
m_texPoint[3] = temp;
}
m_nFlip = flip;
bSet = TRUE;
}
return bSet;
}
BOOL CD3DVideoRender::setImage(GEOMETRICTRANSFORMATION flip, RECT * pDstRect, RECT * pSrcRect)
{
if (!setImageArgs(1.0f, flip, pDstRect, pSrcRect))
{
return TRUE;
}
YUV_VERTEX * pVB;
HRESULT hr = m_pVertices->Lock(0, 0, (void**)&pVB, D3DLOCK_DISCARD);
if (FAILED(hr))
{
return FALSE;
}
for (int i = 0 ; i < 4 ; i++)
{
pVB[i].pos = m_verPoint[i];
pVB[i].color = m_dwColor;
pVB[i].tex1 = pVB[i].tex2 = pVB[i].tex3 = m_texPoint[i];
}
hr = m_pVertices->Unlock();
if (FAILED(hr))
{
return FALSE;
}
return TRUE;
}
BOOL CD3DVideoRender::render(AVFrame * frame, int mode)
{
HRESULT hr = S_OK;
if (m_bReset)
{
if (resetDevice())
{
m_bReset = FALSE;
}
}
if (!m_bInited || m_bReset || NULL == frame)
{
return FALSE;
}
if (m_pD3DD9)
{
hr = m_pD3DD9->TestCooperativeLevel();
}
else
{
return FALSE;
}
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, TestCooperativeLevel failed. (hr=0x%0lx)\r\n", __FUNCTION__, hr);
goto done;
}
if (m_nVideoWidth != frame->width || m_nVideoHeight != frame->height)
{
freeImageBuffer();
if (!initImageBuffer(frame->width, frame->height, m_nVideoFmt))
{
log_print(HT_LOG_ERR, "%s, initImageBuffer failed\r\n", __FUNCTION__);
return FALSE;
}
m_nVideoWidth = frame->width;
m_nVideoHeight = frame->height;
}
RECT rect;
GetClientRect((HWND)m_hWnd, &rect);
RECT targetRect;
RECT sourceRect;
sourceRect.left = 0;
sourceRect.top = 0;
sourceRect.right = frame->width;
sourceRect.bottom = frame->height;
if (mode == RENDER_MODE_KEEP)
{
int rectw = rect.right - rect.left;
int recth = rect.bottom - rect.top;
double dw = rectw / (double) m_nVideoWidth;
double dh = recth / (double) m_nVideoHeight;
double rate = (dw > dh) ? dh : dw;
int rw = (int)(m_nVideoWidth * rate);
int rh = (int)(m_nVideoHeight * rate);
targetRect.left = (rectw - rw) / 2;
targetRect.top = (recth - rh) / 2;
targetRect.right = targetRect.left + rw;
targetRect.bottom = targetRect.top + rh;
}
else
{
targetRect = rect;
}
for (int i = 0 ; i < 3 ; i++)
{
int w = m_nVideoWidth;
int h = m_nVideoHeight;
if (i != 0)
{
w /= 2;
h /= 2;
}
BYTE * pSrc = frame->data[i];
D3DLOCKED_RECT lrect;
hr = m_pTexture[i]->LockRect(0, &lrect, NULL, D3DLOCK_DISCARD);
if (SUCCEEDED(hr))
{
BYTE * pDest = (BYTE*) lrect.pBits;
for (int j = 0 ; j < h; j++)
{
memcpy(pDest, pSrc, w);
pDest += lrect.Pitch;
pSrc += frame->linesize[i];
}
hr = m_pTexture[i]->UnlockRect(0);
if (FAILED(hr))
{
goto done;
}
}
else
{
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, LockRect failed\r\n", __FUNCTION__);
goto done;
}
}
}
// begin draw
hr = m_pD3DD9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pRenderSurface);
if (SUCCEEDED(hr))
{
m_pD3DD9->ColorFill(m_pRenderSurface, &rect, D3DCOLOR_ARGB(0xff,0x0,0x0,0x0));
m_pD3DD9->SetRenderTarget(0, m_pRenderSurface);
m_pRenderSurface->Release();
}
hr = m_pD3DD9->BeginScene();
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, BeginScene failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX3);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetFVF failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->SetPixelShader(m_pPixelShader);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetPixelShader failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pPixelConstTable->SetFloat(m_pD3DD9, m_hTransparent, 1.0);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetFloat failed\r\n", __FUNCTION__);
goto done;
}
setImage(GS_NONE, &targetRect, &sourceRect);
hr = m_pD3DD9->SetTexture(m_dYUVTexture[Y_TEX].RegisterIndex, m_pTexTemp[Y_TEX]);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetTexture failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->SetTexture(m_dYUVTexture[U_TEX].RegisterIndex, m_pTexTemp[U_TEX]);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetTexture failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->SetTexture(m_dYUVTexture[V_TEX].RegisterIndex, m_pTexTemp[V_TEX]);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetTexture failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->SetStreamSource(0, m_pVertices, 0, sizeof(YUV_VERTEX));
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, SetStreamSource failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, DrawPrimitive failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->EndScene();
if (FAILED(hr))
{
log_print(HT_LOG_ERR, "%s, EndScene failed\r\n", __FUNCTION__);
goto done;
}
hr = m_pD3DD9->Present(&rect, &rect, NULL, NULL);
done:
if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET)
{
log_print(HT_LOG_DBG, "%s, device lost!\r\n", __FUNCTION__);
m_bReset = TRUE;
return FALSE;
}
return SUCCEEDED(hr) ? TRUE : FALSE;
}

View File

@@ -0,0 +1,101 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef VIDEO_RENDER_D3D_H
#define VIDEO_RENDER_D3D_H
#include "video_render.h"
#include <d3d9.h>
#include <D3DX9Shader.h>
/***************************************************************************************/
typedef struct _YUV_VERTEX
{
D3DXVECTOR4 pos;
DWORD color;
D3DXVECTOR2 tex1;
D3DXVECTOR2 tex2;
D3DXVECTOR2 tex3;
} YUV_VERTEX;
enum GEOMETRICTRANSFORMATION
{
GS_NONE = 0,
GS_UPPER_DOWN_FLIP = 1,
GS_LEFT_RIGHT_FLIP = 2
};
#define Y_TEX 0
#define U_TEX 1
#define V_TEX 2
#define COUNT_YUV_TEX 3
/***************************************************************************************/
class CD3DVideoRender : public CVideoRender
{
public:
CD3DVideoRender();
~CD3DVideoRender();
BOOL init(WId wid, int w, int h, int videofmt);
void unInit();
BOOL render(AVFrame * frame, int mode);
private:
BOOL initDevice(D3DPRESENT_PARAMETERS * pD3DPP, HWND hWnd);
BOOL initD3DPP(D3DPRESENT_PARAMETERS * pD3DPP, HWND hWnd);
BOOL initShader(const DWORD * pCode3, const DWORD * pCode2);
BOOL initImageBuffer(int w, int h, int videofmt);
void freeImageBuffer();
BOOL resetDevice();
BOOL setImage(GEOMETRICTRANSFORMATION flip, RECT * pDstRect, RECT * pSrcRect);
BOOL setImageArgs(float Transparent, GEOMETRICTRANSFORMATION flip, RECT * pDstRect, RECT * pSrcRect);
private:
IDirect3D9 * m_pD3D9;
IDirect3DDevice9 * m_pD3DD9;
IDirect3DSurface9 * m_pRenderSurface;
IDirect3DTexture9 * m_pTexture[3];
IDirect3DTexture9 * m_pTexTemp[3];
D3DXVECTOR2 m_texPoint[4];
D3DXVECTOR4 m_verPoint[4];
LPDIRECT3DVERTEXBUFFER9 m_pVertices;
LPDIRECT3DPIXELSHADER9 m_pPixelShader;
LPD3DXCONSTANTTABLE m_pPixelConstTable;
D3DXHANDLE m_hYUVTexture[3];
D3DXCONSTANT_DESC m_dYUVTexture[3];
D3DXHANDLE m_hTransparent;
GEOMETRICTRANSFORMATION m_nFlip;
float m_dTransparent;
DWORD m_dwColor;
BOOL m_bReset;
RECT m_dstRect;
RECT m_srcRect;
};
#endif // end of VIDEO_RENDER_D3D_H

View File

@@ -0,0 +1,254 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "video_render_gdi.h"
/***************************************************************************************/
#pragma comment(lib, "vfw32")
/***************************************************************************************/
CGDIVideoRender::CGDIVideoRender()
: CVideoRender()
, m_hDC(NULL)
, m_hCompatDC(NULL)
, m_hCompatBitmap(NULL)
, m_hDib(NULL)
, m_bReinit(FALSE)
{
memset(&m_bmpHdr, 0, sizeof(m_bmpHdr));
memset(&m_dstRect, 0, sizeof(RECT));
}
CGDIVideoRender::~CGDIVideoRender()
{
unInit();
}
BOOL CGDIVideoRender::init(WId wid, int w, int h, int videofmt)
{
HWND hWnd = (HWND)wid;
m_hDC = ::GetDC(hWnd);
if (NULL == m_hDC)
{
return FALSE;
}
m_hCompatDC = ::CreateCompatibleDC(m_hDC);
if (NULL == m_hCompatDC)
{
log_print(HT_LOG_ERR, "%s, CreateCompatibleDC failed\r\n", __FUNCTION__);
return FALSE;
}
::SetStretchBltMode(m_hDC, HALFTONE);
::SetStretchBltMode(m_hCompatDC, HALFTONE);
RECT rect;
::GetClientRect(hWnd, &rect);
m_hCompatBitmap = ::CreateCompatibleBitmap(m_hDC, rect.right - rect.left, rect.bottom - rect.top);
if (NULL == m_hCompatBitmap)
{
log_print(HT_LOG_ERR, "%s, CreateCompatibleBitmap failed\r\n", __FUNCTION__);
return FALSE;
}
::SelectObject(m_hCompatDC, m_hCompatBitmap);
memset(&m_bmpHdr, 0, sizeof(BITMAPINFOHEADER));
m_bmpHdr.biSize = 40;
m_bmpHdr.biWidth = w;
m_bmpHdr.biHeight = h;
m_bmpHdr.biBitCount = 24;
m_bmpHdr.biPlanes = 1;
m_bmpHdr.biSizeImage = w * h * m_bmpHdr.biBitCount / 8;
m_hDib = ::DrawDibOpen();
if (NULL == m_hDib)
{
log_print(HT_LOG_ERR, "%s, DrawDibOpen failed\r\n", __FUNCTION__);
return FALSE;
}
int rectw = rect.right - rect.left;
int recth = rect.bottom - rect.top;
double dw = rectw / (double) w;
double dh = recth / (double) h;
double rate = (dw > dh)? dh : dw;
int rw = (int)(w * rate);
int rh = (int)(h * rate);
m_dstRect.left = (rectw - rw) / 2;
m_dstRect.top = (recth - rh) / 2;
m_dstRect.right = m_dstRect.left + rw;
m_dstRect.bottom = m_dstRect.top + rh;
CVideoRender::init(wid, w, h, videofmt);
m_bInited = ::DrawDibBegin(m_hDib, m_hCompatDC, rw, rh, &m_bmpHdr, w, h, 0);
return m_bInited;
}
void CGDIVideoRender::unInit()
{
if (m_hCompatDC)
{
::DeleteDC(m_hCompatDC);
m_hCompatDC = NULL;
}
if (m_hDC)
{
::ReleaseDC((HWND)m_hWnd, m_hDC);
m_hDC = NULL;
}
if (m_hCompatBitmap)
{
::DeleteObject(m_hCompatBitmap);
m_hCompatBitmap = NULL;
}
if (m_hDib)
{
::DrawDibEnd(m_hDib);
::DrawDibClose(m_hDib);
m_hDib = NULL;
}
m_bInited = FALSE;
}
BOOL CGDIVideoRender::render(AVFrame * frame, int mode)
{
if (NULL == frame)
{
return FALSE;
}
if (m_bReinit)
{
unInit();
if (init(m_hWnd, frame->width, frame->height, m_nVideoFmt))
{
m_bReinit = FALSE;
}
}
if (!m_bInited)
{
return FALSE;
}
BOOL ret = FALSE;
RECT rect;
GetClientRect((HWND)m_hWnd, &rect);
RECT targetRect;
RECT sourceRect;
sourceRect.left = 0;
sourceRect.top = 0;
sourceRect.right = frame->width;
sourceRect.bottom = frame->height;
if (mode == RENDER_MODE_KEEP)
{
int rectw = rect.right - rect.left;
int recth = rect.bottom - rect.top;
double dw = rectw / (double) frame->width;
double dh = recth / (double) frame->height;
double rate = (dw > dh) ? dh : dw;
int rw = (int)(frame->width * rate);
int rh = (int)(frame->height * rate);
targetRect.left = (rectw - rw) / 2;
targetRect.top = (recth - rh) / 2;
targetRect.right = targetRect.left + rw;
targetRect.bottom = targetRect.top + rh;
}
else
{
targetRect = rect;
}
if (memcmp(&m_dstRect, &targetRect, sizeof(RECT)) != 0)
{
m_bReinit = TRUE;
memcpy(&m_dstRect, &targetRect, sizeof(RECT));
}
if (m_nVideoWidth != frame->width || m_nVideoHeight != frame->height)
{
m_bReinit = TRUE;
}
if (m_bReinit)
{
unInit();
if (!init(m_hWnd, frame->width, frame->height, m_nVideoFmt))
{
log_print(HT_LOG_ERR, "%s, init failed\r\n", __FUNCTION__);
return FALSE;
}
m_bReinit = FALSE;
}
::FillRect(m_hCompatDC, &rect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
if (RENDER_MODE_KEEP == mode)
{
ret = ::DrawDibDraw(m_hDib, m_hCompatDC, targetRect.left, targetRect.top,
targetRect.right - targetRect.left, targetRect.bottom - targetRect.top,
&m_bmpHdr, frame->data[0],
sourceRect.left, sourceRect.top, sourceRect.right - sourceRect.left,
sourceRect.bottom - sourceRect.top, DDF_BACKGROUNDPAL);
}
else
{
ret = ::DrawDibDraw(m_hDib, m_hCompatDC, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
&m_bmpHdr, frame->data[0],
sourceRect.left, sourceRect.top, sourceRect.right - sourceRect.left,
sourceRect.bottom - sourceRect.top, DDF_BACKGROUNDPAL);
}
if (ret)
{
ret = ::BitBlt(m_hDC, 0, 0, rect.right - rect.left, rect.bottom - rect.top , m_hCompatDC, 0, 0, SRCCOPY);
}
return ret;
}

View File

@@ -0,0 +1,58 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef VIDEO_RENDER_GDI_H
#define VIDEO_RENDER_GDI_H
#include "video_render.h"
/***************************************************************************************/
/***************************************************************************************/
class CGDIVideoRender : public CVideoRender
{
public:
CGDIVideoRender();
~CGDIVideoRender();
BOOL init(WId wid, int w, int h, int videofmt);
void unInit();
BOOL render(AVFrame * frame, int mode);
private:
HDC m_hDC;
HDC m_hCompatDC;
HBITMAP m_hCompatBitmap;
HDRAWDIB m_hDib;
BITMAPINFOHEADER m_bmpHdr;
BOOL m_bReinit;
RECT m_dstRect;
};
#endif // end of VIDEO_RENDER_GDI_H

View File

@@ -0,0 +1,244 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "video_render_sdl.h"
#include "lock.h"
extern "C"
{
#include <libavutil/imgutils.h>
}
/***************************************************************************************/
static void * g_sdl_mutex = sys_os_create_mutex();
/***************************************************************************************/
CSDLVideoRender::CSDLVideoRender()
: CVideoRender()
, m_pWindow(NULL)
, m_pRenderer(NULL)
, m_pTexture(NULL)
{
}
CSDLVideoRender::~CSDLVideoRender()
{
unInit();
}
BOOL CSDLVideoRender::init(WId hWnd, int w, int h, int videofmt)
{
CVideoRender::init(hWnd, w, h, videofmt);
CLock lock(g_sdl_mutex);
m_bInited = initTexture();
return m_bInited;
}
void CSDLVideoRender::unInit()
{
CLock lock(g_sdl_mutex);
if (m_pTexture)
{
SDL_DestroyTexture(m_pTexture);
m_pTexture = NULL;
}
if (m_pRenderer)
{
SDL_DestroyRenderer(m_pRenderer);
m_pRenderer = NULL;
}
if (m_pWindow)
{
SDL_DestroyWindow(m_pWindow);
m_pWindow = NULL;
}
m_bInited = FALSE;
}
BOOL CSDLVideoRender::reInit(WId hWnd, int w, int h, int videofmt)
{
BOOL ret;
#ifdef IOS
char title[512] = {'\0'};
if (m_pWindow)
{
const char * p_title = SDL_GetWindowTitle(m_pWindow);
if (p_title)
{
strncpy(title, p_title, sizeof(title)-1);
}
}
#endif
unInit();
ret = init(m_hWnd, w, h, m_nVideoFmt);
#ifdef IOS
if (ret && m_pWindow)
{
SDL_SetWindowTitle(m_pWindow, title);
}
#endif
return ret;
}
BOOL CSDLVideoRender::render(AVFrame * frame, int mode)
{
if (!m_bInited || NULL == frame)
{
return FALSE;
}
return renderTexture(frame, mode);
}
BOOL CSDLVideoRender::initTexture()
{
#ifdef IOS
// On Mac platform, SDL does not support rendering videos on child windows
// and must be presented on a separate top-level window
m_pWindow = SDL_CreateWindow("", 0, 0, 400, 300, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
#else
m_pWindow = SDL_CreateWindowFrom((void *)m_hWnd);
#endif
if (NULL == m_pWindow)
{
return FALSE;
}
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
if (NULL == m_pRenderer)
{
return FALSE;
}
m_pTexture = SDL_CreateTexture(m_pRenderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, m_nVideoWidth, m_nVideoHeight);
if (NULL == m_pTexture)
{
return FALSE;
}
SDL_ShowWindow(m_pWindow);
return TRUE;
}
BOOL CSDLVideoRender::renderTexture(AVFrame * frame, int mode)
{
int pitch;
uint8 * ptr;
uint8 * pixels;
AVFrame dstFrame;
if (m_nVideoWidth != frame->width || m_nVideoHeight != frame->height)
{
if (!reInit(m_hWnd, frame->width, frame->height, m_nVideoFmt))
{
return TRUE;
}
m_nVideoWidth = frame->width;
m_nVideoHeight = frame->height;
}
memset(&dstFrame, 0, sizeof(AVFrame));
SDL_LockTexture(m_pTexture, NULL, (void **)&pixels, &pitch);
av_image_fill_arrays(dstFrame.data, dstFrame.linesize, pixels, AV_PIX_FMT_YUV420P, m_nVideoWidth, m_nVideoHeight, 1);
// swap UV plane
ptr = dstFrame.data[1];
dstFrame.data[1] = dstFrame.data[2];
dstFrame.data[2] = ptr;
av_image_copy(dstFrame.data, dstFrame.linesize, (const uint8_t**)frame->data, frame->linesize, AV_PIX_FMT_YUV420P, m_nVideoWidth, m_nVideoHeight);
SDL_UnlockTexture(m_pTexture);
SDL_RenderClear(m_pRenderer);
int w, h;
SDL_Rect rect;
SDL_GetRendererOutputSize(m_pRenderer, &w, &h);
if (mode == RENDER_MODE_KEEP)
{
double dw = w / (double) m_nVideoWidth;
double dh = h / (double) m_nVideoHeight;
double rate = (dw > dh) ? dh : dw;
int rw = (int)(m_nVideoWidth * rate);
int rh = (int)(m_nVideoHeight * rate);
rect.x = (w - rw) / 2;
rect.y = (h - rh) / 2;
rect.w = rw;
rect.h = rh;
}
else
{
rect.x = 0;
rect.y = 0;
rect.w = w;
rect.h = h;
}
SDL_RenderCopy(m_pRenderer, m_pTexture, NULL, &rect);
SDL_RenderPresent(m_pRenderer);
return TRUE;
}
void CSDLVideoRender::setWindowSize(QSize size)
{
reInit(m_hWnd, m_nVideoWidth, m_nVideoHeight, m_nVideoFmt);
}
void CSDLVideoRender::setWindowTitle(const char * title)
{
if (m_pWindow)
{
if (title)
{
SDL_SetWindowTitle(m_pWindow, title);
}
else
{
SDL_SetWindowTitle(m_pWindow, "");
}
}
}

View File

@@ -0,0 +1,57 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef VIDEO_RENDER_SDL_H
#define VIDEO_RENDER_SDL_H
#include "video_render.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
/***************************************************************************************/
class CSDLVideoRender : public CVideoRender
{
public:
CSDLVideoRender();
~CSDLVideoRender();
BOOL init(WId hWnd, int w, int h, int videofmt);
void unInit();
BOOL render(AVFrame * frame, int mode);
void setWindowSize(QSize size);
void setWindowTitle(const char * title);
private:
BOOL reInit(WId hWnd, int w, int h, int videofmt);
BOOL initTexture();
BOOL renderTexture(AVFrame * frame, int mode);
private:
SDL_Window * m_pWindow;
SDL_Renderer * m_pRenderer;
SDL_Texture * m_pTexture;
};
#endif