Initial setup for CLion
This commit is contained in:
214
MediaClient/media/alsa.cpp
Normal file
214
MediaClient/media/alsa.cpp
Normal 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
59
MediaClient/media/alsa.h
Normal 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
|
||||
|
||||
|
||||
|
||||
119
MediaClient/media/audio_capture.cpp
Normal file
119
MediaClient/media/audio_capture.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
87
MediaClient/media/audio_capture.h
Normal file
87
MediaClient/media/audio_capture.h
Normal 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
|
||||
|
||||
|
||||
176
MediaClient/media/audio_capture_android.cpp
Normal file
176
MediaClient/media/audio_capture_android.cpp
Normal 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(¶ms, 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(¶ms) == 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
61
MediaClient/media/audio_capture_android.h
Normal file
61
MediaClient/media/audio_capture_android.h
Normal 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
|
||||
|
||||
57
MediaClient/media/audio_capture_avf.h
Normal file
57
MediaClient/media/audio_capture_avf.h
Normal 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
|
||||
|
||||
|
||||
1091
MediaClient/media/audio_capture_avf.mm
Normal file
1091
MediaClient/media/audio_capture_avf.mm
Normal file
File diff suppressed because it is too large
Load Diff
425
MediaClient/media/audio_capture_linux.cpp
Normal file
425
MediaClient/media/audio_capture_linux.cpp
Normal 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(¶ms, 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(¶ms) == 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
68
MediaClient/media/audio_capture_linux.h
Normal file
68
MediaClient/media/audio_capture_linux.h
Normal 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
|
||||
|
||||
|
||||
275
MediaClient/media/audio_capture_mac.cpp
Normal file
275
MediaClient/media/audio_capture_mac.cpp
Normal 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(¶ms, 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(¶ms) == 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
67
MediaClient/media/audio_capture_mac.h
Normal file
67
MediaClient/media/audio_capture_mac.h
Normal 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
|
||||
|
||||
|
||||
627
MediaClient/media/audio_capture_win.cpp
Normal file
627
MediaClient/media/audio_capture_win.cpp
Normal 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(¶ms, 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(¶ms) == 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
MediaClient/media/audio_capture_win.h
Normal file
76
MediaClient/media/audio_capture_win.h
Normal 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
|
||||
|
||||
|
||||
|
||||
246
MediaClient/media/audio_decoder.cpp
Normal file
246
MediaClient/media/audio_decoder.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
MediaClient/media/audio_decoder.h
Normal file
73
MediaClient/media/audio_decoder.h
Normal 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
|
||||
|
||||
|
||||
577
MediaClient/media/audio_encoder.cpp
Normal file
577
MediaClient/media/audio_encoder.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
111
MediaClient/media/audio_encoder.h
Normal file
111
MediaClient/media/audio_encoder.h
Normal 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
|
||||
|
||||
|
||||
|
||||
68
MediaClient/media/audio_play.cpp
Normal file
68
MediaClient/media/audio_play.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
53
MediaClient/media/audio_play.h
Normal file
53
MediaClient/media/audio_play.h
Normal 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
|
||||
|
||||
|
||||
|
||||
41
MediaClient/media/audio_play_avf.h
Normal file
41
MediaClient/media/audio_play_avf.h
Normal 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
|
||||
|
||||
|
||||
439
MediaClient/media/audio_play_avf.mm
Normal file
439
MediaClient/media/audio_play_avf.mm
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
181
MediaClient/media/audio_play_mac.cpp
Normal file
181
MediaClient/media/audio_play_mac.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
53
MediaClient/media/audio_play_mac.h
Normal file
53
MediaClient/media/audio_play_mac.h
Normal 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
|
||||
|
||||
|
||||
157
MediaClient/media/audio_play_qt.cpp
Normal file
157
MediaClient/media/audio_play_qt.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
56
MediaClient/media/audio_play_qt.h
Normal file
56
MediaClient/media/audio_play_qt.h
Normal 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
|
||||
|
||||
|
||||
349
MediaClient/media/audio_play_win.cpp
Normal file
349
MediaClient/media/audio_play_win.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
MediaClient/media/audio_play_win.h
Normal file
61
MediaClient/media/audio_play_win.h
Normal 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
|
||||
|
||||
|
||||
|
||||
48
MediaClient/media/avcodec_mutex.cpp
Normal file
48
MediaClient/media/avcodec_mutex.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
41
MediaClient/media/avcodec_mutex.h
Normal file
41
MediaClient/media/avcodec_mutex.h
Normal 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
188
MediaClient/media/avi.h
Normal 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
|
||||
|
||||
|
||||
|
||||
1144
MediaClient/media/avi_write.cpp
Normal file
1144
MediaClient/media/avi_write.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
MediaClient/media/avi_write.h
Normal file
61
MediaClient/media/avi_write.h
Normal 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
|
||||
|
||||
|
||||
|
||||
917
MediaClient/media/file_player.cpp
Normal file
917
MediaClient/media/file_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
95
MediaClient/media/file_player.h
Normal file
95
MediaClient/media/file_player.h
Normal 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
|
||||
|
||||
|
||||
45
MediaClient/media/format.h
Normal file
45
MediaClient/media/format.h
Normal 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
|
||||
|
||||
|
||||
94
MediaClient/media/gles_engine.cpp
Normal file
94
MediaClient/media/gles_engine.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
43
MediaClient/media/gles_engine.h
Normal file
43
MediaClient/media/gles_engine.h
Normal 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
|
||||
|
||||
|
||||
325
MediaClient/media/gles_input.cpp
Normal file
325
MediaClient/media/gles_input.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
72
MediaClient/media/gles_input.h
Normal file
72
MediaClient/media/gles_input.h
Normal 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
|
||||
|
||||
396
MediaClient/media/http_flv_player.cpp
Normal file
396
MediaClient/media/http_flv_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
63
MediaClient/media/http_flv_player.h
Normal file
63
MediaClient/media/http_flv_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
204
MediaClient/media/http_mjpeg_player.cpp
Normal file
204
MediaClient/media/http_mjpeg_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
59
MediaClient/media/http_mjpeg_player.h
Normal file
59
MediaClient/media/http_mjpeg_player.h
Normal 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
37
MediaClient/media/lock.h
Normal 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
|
||||
|
||||
|
||||
215
MediaClient/media/media_codec.cpp
Normal file
215
MediaClient/media/media_codec.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
43
MediaClient/media/media_codec.h
Normal file
43
MediaClient/media/media_codec.h
Normal 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
|
||||
|
||||
|
||||
64
MediaClient/media/media_format.h
Normal file
64
MediaClient/media/media_format.h
Normal 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
|
||||
|
||||
|
||||
259
MediaClient/media/media_parse.cpp
Normal file
259
MediaClient/media/media_parse.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
37
MediaClient/media/media_parse.h
Normal file
37
MediaClient/media/media_parse.h
Normal 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
|
||||
|
||||
|
||||
253
MediaClient/media/media_util.cpp
Normal file
253
MediaClient/media/media_util.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
53
MediaClient/media/media_util.h
Normal file
53
MediaClient/media/media_util.h
Normal 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
|
||||
|
||||
|
||||
|
||||
407
MediaClient/media/rtmp_player.cpp
Normal file
407
MediaClient/media/rtmp_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
62
MediaClient/media/rtmp_player.h
Normal file
62
MediaClient/media/rtmp_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
674
MediaClient/media/rtsp_player.cpp
Normal file
674
MediaClient/media/rtsp_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
91
MediaClient/media/rtsp_player.h
Normal file
91
MediaClient/media/rtsp_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
412
MediaClient/media/srt_player.cpp
Normal file
412
MediaClient/media/srt_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
66
MediaClient/media/srt_player.h
Normal file
66
MediaClient/media/srt_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
989
MediaClient/media/video_decoder.cpp
Normal file
989
MediaClient/media/video_decoder.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
172
MediaClient/media/video_decoder.h
Normal file
172
MediaClient/media/video_decoder.h
Normal 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
|
||||
|
||||
|
||||
2290
MediaClient/media/video_player.cpp
Normal file
2290
MediaClient/media/video_player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
340
MediaClient/media/video_player.h
Normal file
340
MediaClient/media/video_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
1101
MediaClient/media/video_player.mm
Normal file
1101
MediaClient/media/video_player.mm
Normal file
File diff suppressed because it is too large
Load Diff
60
MediaClient/media/video_render.cpp
Normal file
60
MediaClient/media/video_render.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
63
MediaClient/media/video_render.h
Normal file
63
MediaClient/media/video_render.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
829
MediaClient/media/video_render_d3d.cpp
Normal file
829
MediaClient/media/video_render_d3d.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
101
MediaClient/media/video_render_d3d.h
Normal file
101
MediaClient/media/video_render_d3d.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
254
MediaClient/media/video_render_gdi.cpp
Normal file
254
MediaClient/media/video_render_gdi.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
58
MediaClient/media/video_render_gdi.h
Normal file
58
MediaClient/media/video_render_gdi.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
244
MediaClient/media/video_render_sdl.cpp
Normal file
244
MediaClient/media/video_render_sdl.cpp
Normal 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, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
MediaClient/media/video_render_sdl.h
Normal file
57
MediaClient/media/video_render_sdl.h
Normal 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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user