Update MediaClient

This commit is contained in:
2026-03-28 11:39:04 +11:00
parent 24dc6c7cd0
commit f3266566eb
1284 changed files with 462406 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,831 @@
/***************************************************************************************
*
* 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(QObject * parent)
: CVideoPlayer(parent)
, 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(QString fileName, WId hWnd)
{
BOOL ret = FALSE;
close();
CVideoPlayer::open(fileName, hWnd);
ret = openFile(fileName.toStdString().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()
{
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);
return TRUE;
}
void CFilePlayer::stop()
{
close();
}
BOOL CFilePlayer::pause()
{
if (!m_bPaused)
{
m_bPaused = TRUE;
}
else
{
m_bPaused = FALSE;
}
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;
}
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;
}

View File

@@ -0,0 +1,86 @@
/***************************************************************************************
*
* 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"
typedef struct
{
uint8 * data;
int size;
int64 ts;
int waitnext;
} HTPACKET;
class CFilePlayer : public CVideoPlayer
{
Q_OBJECT
public:
CFilePlayer(QObject * parent = 0);
virtual ~CFilePlayer();
BOOL open(QString fileName, WId hWnd);
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();
void readThread();
void videoThread();
void audioThread();
private:
BOOL openFile(const char * filename);
BOOL readFrame();
void videoData(uint8 * data, int size, int64 pts, int waitnext);
void audioData(uint8 * data, int size, int64 pts);
void clearQueue(HQUEUE * queue);
BOOL seekStream(double pos);
double getFramerate();
private:
int m_nAudioIndex; // audio stream index
int m_nVideoIndex; // video stream index
int64 m_nDuration; // the stream duration, unit is millisecond
int64 m_nCurPos; // the current play position, unit is millisecond
int m_bSeek; // seek request
double m_dSeekPos; // seek position, nit is millisecond
int m_nNalLength;
AVFormatContext * m_pFormatContext; // format context
pthread_t m_hReadThread; // read thread
pthread_t m_hVideoThread; // video thread
pthread_t m_hAudioThread; // audio thread
HQUEUE * m_pVideoQueue; // video queue
HQUEUE * m_pAudioQueue; // audio queue
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,434 @@
/***************************************************************************************
*
* 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(QObject * parent)
: CVideoPlayer(parent)
{
memset(m_ip, 0, sizeof(m_ip));
}
CHttpFlvPlayer::~CHttpFlvPlayer()
{
close();
}
BOOL CHttpFlvPlayer::open(QString fileName, WId hWnd)
{
close();
CVideoPlayer::open(fileName, hWnd);
char host[100];
url_split(fileName.toStdString().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::close()
{
// stop http-flv connection
m_httpflv.http_flv_stop();
m_httpflv.http_flv_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::close();
}
BOOL CHttpFlvPlayer::play()
{
if (m_httpflv.http_flv_start(m_sFileName.toStdString().c_str(), m_acct.toStdString().c_str(), m_pass.toStdString().c_str()))
{
m_bPlaying = TRUE;
}
return m_bPlaying;
}
void CHttpFlvPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_httpflv.http_flv_stop();
}
m_bPlaying = FALSE;
m_bPaused = FALSE;
}
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;
}
}
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)
{
BOOL ret;
uint8 extradata[1024];
int extradata_size = 0;
if (VIDEO_CODEC_H264 == videoCodec)
{
ret = m_httpflv.get_h264_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else if (VIDEO_CODEC_H265 == videoCodec)
{
ret = m_httpflv.get_h265_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size,
m_h26XParamSets.vps, &m_h26XParamSets.vps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.vps, m_h26XParamSets.vps_size);
extradata_size += m_h26XParamSets.vps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else
{
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());
}
}
emit notify(evt);
}
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;
}
void CHttpFlvPlayer::onRecordFileSwitch()
{
AVICTX * p_ctx;
AVICTX * p_oldctx = m_pAviCtx;
QString path = getRecordPath();
QString file = path + "/" + getTempFile(m_ip, ".avi");
p_ctx = avi_write_open(file.toLocal8Bit().toStdString().c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
p_ctx->ctxf_audio = p_oldctx->ctxf_audio;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
if (p_ctx->ctxf_audio)
{
avi_set_audio_info(p_ctx, p_oldctx->a_chns, p_oldctx->a_rate, p_oldctx->a_fmt);
avi_set_audio_extra_info(p_ctx, p_oldctx->a_extra, p_oldctx->a_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
if (m_h26XParamSets.vps_size > 0 ||
m_h26XParamSets.sps_size > 0 ||
m_h26XParamSets.pps_size > 0)
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
}
}

View File

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

View File

@@ -0,0 +1,183 @@
/***************************************************************************************
*
* 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(QObject * parent)
: CVideoPlayer(parent)
{
memset(m_ip, 0, sizeof(m_ip));
}
CHttpMjpegPlayer::~CHttpMjpegPlayer()
{
close();
}
BOOL CHttpMjpegPlayer::open(QString fileName, WId hWnd)
{
close();
CVideoPlayer::open(fileName, hWnd);
char host[100];
url_split(fileName.toStdString().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::close();
}
BOOL CHttpMjpegPlayer::play()
{
if (m_httpmjpeg.mjpeg_start(m_sFileName.toStdString().c_str(), m_acct.toStdString().c_str(), m_pass.toStdString().c_str()))
{
m_bPlaying = TRUE;
}
return m_bPlaying;
}
void CHttpMjpegPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_httpmjpeg.mjpeg_stop();
}
m_bPlaying = FALSE;
m_bPaused = FALSE;
}
BOOL CHttpMjpegPlayer::pause()
{
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);
}
emit notify(evt);
}
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;
}
void CHttpMjpegPlayer::onRecordFileSwitch()
{
AVICTX * p_ctx;
AVICTX * p_oldctx = m_pAviCtx;
QString path = getRecordPath();
QString file = path + "/" + getTempFile(m_ip, ".avi");
p_ctx = avi_write_open(file.toLocal8Bit().toStdString().c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,434 @@
/***************************************************************************************
*
* 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(QObject * parent)
: CVideoPlayer(parent)
{
memset(m_ip, 0, sizeof(m_ip));
}
CRtmpPlayer::~CRtmpPlayer()
{
close();
}
BOOL CRtmpPlayer::open(QString fileName, WId hWnd)
{
close();
CVideoPlayer::open(fileName, hWnd);
char host[100];
url_split(fileName.toStdString().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;
}
void CRtmpPlayer::close()
{
// stop rtmp connection
m_rtmp.rtmp_stop();
m_rtmp.rtmp_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
CVideoPlayer::close();
}
BOOL CRtmpPlayer::play()
{
if (m_rtmp.rtmp_start(m_sFileName.toStdString().c_str(), m_acct.toStdString().c_str(), m_pass.toStdString().c_str()))
{
m_bPlaying = TRUE;
}
return m_bPlaying;
}
void CRtmpPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_rtmp.rtmp_stop();
}
m_bPlaying = FALSE;
m_bPaused = FALSE;
}
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;
}
}
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::onNotify(int evt)
{
if (evt == RTMP_EVE_VIDEOREADY)
{
int videoCodec = m_rtmp.video_codec();
if (VIDEO_CODEC_NONE != videoCodec)
{
BOOL ret;
uint8 extradata[1024];
int extradata_size = 0;
if (VIDEO_CODEC_H264 == videoCodec)
{
ret = m_rtmp.get_h264_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else if (VIDEO_CODEC_H265 == videoCodec)
{
ret = m_rtmp.get_h265_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size,
m_h26XParamSets.vps, &m_h26XParamSets.vps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.vps, m_h26XParamSets.vps_size);
extradata_size += m_h26XParamSets.vps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else
{
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;
}
void CRtmpPlayer::onRecordFileSwitch()
{
AVICTX * p_ctx;
AVICTX * p_oldctx = m_pAviCtx;
QString path = getRecordPath();
QString file = path + "/" + getTempFile(m_ip, ".avi");
p_ctx = avi_write_open(file.toLocal8Bit().toStdString().c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
p_ctx->ctxf_audio = p_oldctx->ctxf_audio;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
if (p_ctx->ctxf_audio)
{
avi_set_audio_info(p_ctx, p_oldctx->a_chns, p_oldctx->a_rate, p_oldctx->a_fmt);
avi_set_audio_extra_info(p_ctx, p_oldctx->a_extra, p_oldctx->a_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
if (m_h26XParamSets.vps_size > 0 ||
m_h26XParamSets.sps_size > 0 ||
m_h26XParamSets.pps_size > 0)
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
}
}

View File

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

View File

@@ -0,0 +1,683 @@
/***************************************************************************************
*
* 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 "utils.h"
#include "rtp.h"
/***************************************************************************************/
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);
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(QObject * parent)
: CVideoPlayer(parent)
, m_port(554)
{
memset(m_ip, 0, sizeof(m_ip));
}
CRtspPlayer::~CRtspPlayer()
{
close();
}
BOOL CRtspPlayer::open(QString fileName, WId hWnd)
{
close();
CVideoPlayer::open(fileName, hWnd);
int port;
char proto[32], username[100], password[100], host[100], path[200];
url_split(fileName.toStdString().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;
}
#ifdef OVER_HTTP
else if (strcasecmp(proto, "http") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "https") == 0)
{
port = (port == -1) ? 443 : port;
}
#endif
#ifdef OVER_WEBSOCKET
else if (strcasecmp(proto, "ws") == 0)
{
port = (port == -1) ? 80 : port;
}
else if (strcasecmp(proto, "wss") == 0)
{
port = (port == -1) ? 443 : port;
}
#endif
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);
#ifdef METADATA
m_rtsp.set_metadata_cb(rtsp_metadata_cb);
#endif
return TRUE;
}
void CRtspPlayer::close()
{
// stop rtsp connection
m_rtsp.rtsp_stop();
m_rtsp.rtsp_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
m_port = 554;
memset(m_ip, 0, sizeof(m_ip));
CVideoPlayer::close();
}
BOOL CRtspPlayer::play()
{
if (m_rtsp.rtsp_start(m_sFileName.toStdString().c_str(), m_acct.toStdString().c_str(), m_pass.toStdString().c_str()))
{
m_bPlaying = TRUE;
}
return m_bPlaying;
}
void CRtspPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_rtsp.rtsp_stop();
}
m_bPlaying = FALSE;
m_bPaused = FALSE;
}
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;
}
}
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)
{
BOOL ret;
uint8 extradata[1024];
int extradata_size = 0;
if (VIDEO_CODEC_H264 == videoCodec)
{
ret = m_rtsp.get_h264_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else if (VIDEO_CODEC_H265 == videoCodec)
{
ret = m_rtsp.get_h265_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size,
m_h26XParamSets.vps, &m_h26XParamSets.vps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.vps, m_h26XParamSets.vps_size);
extradata_size += m_h26XParamSets.vps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else
{
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;
}
void CRtspPlayer::onRecordFileSwitch()
{
AVICTX * p_ctx;
AVICTX * p_oldctx = m_pAviCtx;
QString path = getRecordPath();
QString file = path + "/" + getTempFile(m_ip, ".avi");
p_ctx = avi_write_open(file.toLocal8Bit().toStdString().c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
p_ctx->ctxf_audio = p_oldctx->ctxf_audio;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
if (p_ctx->ctxf_audio)
{
avi_set_audio_info(p_ctx, p_oldctx->a_chns, p_oldctx->a_rate, p_oldctx->a_fmt);
avi_set_audio_extra_info(p_ctx, p_oldctx->a_extra, p_oldctx->a_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
if (m_h26XParamSets.vps_size > 0 ||
m_h26XParamSets.sps_size > 0 ||
m_h26XParamSets.pps_size > 0)
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
}
}

View File

@@ -0,0 +1,93 @@
/***************************************************************************************
*
* 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"
class CRtspPlayer : public CVideoPlayer
{
Q_OBJECT
public:
CRtspPlayer(QObject * parent = 0);
virtual ~CRtspPlayer();
BOOL open(QString fileName, WId hWnd);
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);
#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();
void onRecordFileSwitch();
private:
char m_ip[32];
int m_port;
CRtspClient m_rtsp;
};
#endif

View File

@@ -0,0 +1,436 @@
/***************************************************************************************
*
* 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(QObject * parent)
: CVideoPlayer(parent)
, m_port(-1)
{
memset(m_ip, 0, sizeof(m_ip));
}
CSrtPlayer::~CSrtPlayer()
{
close();
}
BOOL CSrtPlayer::open(QString fileName, WId hWnd)
{
close();
CVideoPlayer::open(fileName, hWnd);
int port;
char proto[32], username[100], password[100], host[100], path[200];
url_split(fileName.toStdString().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;
}
void CSrtPlayer::close()
{
// stop connection
m_srt.srt_stop();
m_srt.srt_close();
m_bPlaying = FALSE;
m_bPaused = FALSE;
m_port = -1;
memset(m_ip, 0, sizeof(m_ip));
CVideoPlayer::close();
}
BOOL CSrtPlayer::play()
{
if (m_srt.srt_start(m_sFileName.toStdString().c_str(), m_acct.toStdString().c_str(), m_pass.toStdString().c_str()))
{
m_bPlaying = TRUE;
}
return m_bPlaying;
}
void CSrtPlayer::stop()
{
if (m_bPlaying || m_bPaused)
{
m_srt.srt_stop();
}
m_bPlaying = FALSE;
m_bPaused = FALSE;
}
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;
}
}
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)
{
BOOL ret;
uint8 extradata[1024];
int extradata_size = 0;
if (VIDEO_CODEC_H264 == videoCodec)
{
ret = m_srt.get_h264_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else if (VIDEO_CODEC_H265 == videoCodec)
{
ret = m_srt.get_h265_params(m_h26XParamSets.sps, &m_h26XParamSets.sps_size,
m_h26XParamSets.pps, &m_h26XParamSets.pps_size,
m_h26XParamSets.vps, &m_h26XParamSets.vps_size);
if (ret)
{
memcpy(extradata, m_h26XParamSets.vps, m_h26XParamSets.vps_size);
extradata_size += m_h26XParamSets.vps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
// init video decoder
openVideo(videoCodec, extradata, extradata_size);
}
}
else
{
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());
}
}
emit notify(evt);
}
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);
}
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;
}
void CSrtPlayer::onRecordFileSwitch()
{
AVICTX * p_ctx;
AVICTX * p_oldctx = m_pAviCtx;
QString path = getRecordPath();
QString file = path + "/" + getTempFile(m_ip, ".avi");
p_ctx = avi_write_open(file.toLocal8Bit().toStdString().c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
p_ctx->ctxf_audio = p_oldctx->ctxf_audio;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
if (p_ctx->ctxf_audio)
{
avi_set_audio_info(p_ctx, p_oldctx->a_chns, p_oldctx->a_rate, p_oldctx->a_fmt);
avi_set_audio_extra_info(p_ctx, p_oldctx->a_extra, p_oldctx->a_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
if (m_h26XParamSets.vps_size > 0 ||
m_h26XParamSets.sps_size > 0 ||
m_h26XParamSets.pps_size > 0)
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
}
}

View File

@@ -0,0 +1,63 @@
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#ifndef SRT_PLAYER_H
#define SRT_PLAYER_H
#include "video_player.h"
#include "srt_cln.h"
class CSrtPlayer : public CVideoPlayer
{
Q_OBJECT
public:
CSrtPlayer(QObject * parent = 0);
virtual ~CSrtPlayer();
BOOL open(QString fileName, WId hWnd);
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 onNotify(int evt);
void onAudio(uint8 * pdata, int len, uint32 ts);
void onVideo(uint8 * pdata, int len, uint32 ts);
BOOL onRecord();
void onRecordFileSwitch();
private:
char m_ip[32];
int m_port;
CSrtClient m_srt;
};
#endif

View File

@@ -0,0 +1,569 @@
/***************************************************************************************
*
* 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 "video_decoder.h"
#include "avcodec_mutex.h"
#include "lock.h"
#include "media_codec.h"
#include "media_parse.h"
/***************************************************************************************/
uint32 g_hw_decoder_nums = 0;
uint32 g_hw_decoder_max = 2; // Hardware decoding resources are limited, Limit up to 2 hardware decoding sessions
void * g_hw_decoder_mutex = sys_os_create_mutex();
/***************************************************************************************/
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;
}
CVideoDecoder::CVideoDecoder()
{
m_bInited = FALSE;
m_pCodec = NULL;
m_pContext = NULL;
m_pFrame = NULL;
m_pSoftFrame = NULL;
m_pCallback = NULL;
m_pUserdata = NULL;
m_hwPixFmt = AV_PIX_FMT_NONE;
m_pHWDeviceCtx = NULL;
}
CVideoDecoder::~CVideoDecoder()
{
uninit();
}
void CVideoDecoder::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_pSoftFrame)
{
av_frame_free(&m_pSoftFrame);
}
if (m_pHWDeviceCtx)
{
av_buffer_unref(&m_pHWDeviceCtx);
CLock lock(g_hw_decoder_mutex);
g_hw_decoder_nums--;
}
m_bInited = FALSE;
}
BOOL CVideoDecoder::init(enum AVCodecID codec, uint8 * extradata, int extradata_size, int hwMode)
{
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\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->width = width;
m_pContext->height = height;
m_pContext->flags |= AV_CODEC_FLAG_LOW_DELAY;
m_pContext->flags2 |= AV_CODEC_FLAG2_FAST;
/* ***** Output always the frames ***** */
m_pContext->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
av_opt_set_int(m_pContext, "refcounted_frames", 1, 0);
if (HW_DECODING_DISABLE != hwMode && hwDecoderInit(m_pContext, hwMode) < 0)
{
log_print(HT_LOG_WARN, "%s, hwDecoderInit failed\r\n", __FUNCTION__);
}
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);
}
}
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;
}
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_pSoftFrame = av_frame_alloc();
if (NULL == m_pSoftFrame)
{
log_print(HT_LOG_ERR, "%s, av_frame_alloc failed\r\n", __FUNCTION__);
return FALSE;
}
m_bInited = TRUE;
return TRUE;
}
BOOL CVideoDecoder::init(int codec, uint8 * extradata, int extradata_size, int hwMode)
{
return init(to_video_avcodecid(codec), extradata, extradata_size, hwMode);
}
int CVideoDecoder::hwDecoderInit(AVCodecContext *ctx, int hwMode)
{
int i, err = 0;
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
if (HW_DECODING_DISABLE == hwMode)
{
return 0;
}
CLock lock(g_hw_decoder_mutex);
if (g_hw_decoder_max > 0)
{
if (g_hw_decoder_nums >= g_hw_decoder_max)
{
return 0;
}
}
else
{
return 0;
}
char hwtype[32] = {'\0'};
#if __WINDOWS_OS__
if (HW_DECODING_D3D11 == hwMode)
{
strcpy(hwtype, "d3d11va");
}
else if (HW_DECODING_DXVA == hwMode)
{
strcpy(hwtype, "dxva2");
}
else if (HW_DECODING_AUTO == hwMode)
{
strcpy(hwtype, "d3d11va");
type = av_hwdevice_find_type_by_name(hwtype);
if (AV_HWDEVICE_TYPE_NONE == type)
{
strcpy(hwtype, "dxva2");
}
}
#elif defined(IOS)
if (HW_DECODING_VIDEOTOOLBOX == hwMode)
{
strcpy(hwtype, "videotoolbox");
}
else if (HW_DECODING_OPENCL == hwMode)
{
strcpy(hwtype, "opencl");
}
else if (HW_DECODING_AUTO == hwMode)
{
strcpy(hwtype, "videotoolbox");
type = av_hwdevice_find_type_by_name(hwtype);
if (AV_HWDEVICE_TYPE_NONE == type)
{
strcpy(hwtype, "opencl");
}
}
#elif defined(ANDROID)
if (HW_DECODING_MEDIACODEC == hwMode)
{
strcpy(hwtype, "mediacodec");
}
else if (HW_DECODING_AUTO == hwMode)
{
strcpy(hwtype, "mediacodec");
}
#elif __LINUX_OS__
if (HW_DECODING_VAAPI == hwMode)
{
strcpy(hwtype, "vaapi");
}
else if (HW_DECODING_OPENCL == hwMode)
{
strcpy(hwtype, "opencl");
}
else if (HW_DECODING_AUTO == hwMode)
{
strcpy(hwtype, "vaapi");
type = av_hwdevice_find_type_by_name(hwtype);
if (AV_HWDEVICE_TYPE_NONE == type)
{
strcpy(hwtype, "opencl");
}
}
#endif
type = av_hwdevice_find_type_by_name(hwtype);
if (AV_HWDEVICE_TYPE_NONE == type)
{
log_print(HT_LOG_WARN, "%s, hwdevice type %s not support\r\n", __FUNCTION__, hwtype);
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));
}
return -1;
}
for (i = 0;; i++)
{
const AVCodecHWConfig * config = avcodec_get_hw_config(m_pCodec, i);
if (!config)
{
log_print(HT_LOG_WARN, "%s, decoder %s does not support device type %s\r\n", __FUNCTION__,
m_pCodec->long_name, av_hwdevice_get_type_name(type));
return -1;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == type)
{
m_hwPixFmt = config->pix_fmt;
break;
}
}
err = av_hwdevice_ctx_create(&m_pHWDeviceCtx, type, NULL, NULL, 0);
if (err < 0)
{
log_print(HT_LOG_ERR, "%s, Failed to create specified HW device, type=%s\r\n",
__FUNCTION__, av_hwdevice_get_type_name(type));
return err;
}
ctx->opaque = this;
ctx->get_format = ::getHWFormat;
ctx->hw_device_ctx = av_buffer_ref(m_pHWDeviceCtx);
g_hw_decoder_nums++;
return err;
}
BOOL CVideoDecoder::getHWFormat(AVCodecContext *ctx, const AVPixelFormat *pix_fmts, AVPixelFormat *dst)
{
const AVPixelFormat *p;
*dst = AV_PIX_FMT_NONE;
for (p = pix_fmts; *p != -1; p++)
{
if (*p == m_hwPixFmt)
{
*dst = *p;
return TRUE;
}
}
for (p = pix_fmts; *p != -1; p++)
{
if (*p == AV_PIX_FMT_YUV420P)
{
*dst = *p;
return TRUE;
}
}
for (p = pix_fmts; *p != -1; p++)
{
if (*p == AV_PIX_FMT_YUVJ420P || *p == AV_PIX_FMT_YUVJ422P)
{
*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()
{
if (m_pContext)
{
return m_pContext->width;
}
return 0;
}
int CVideoDecoder::getHeight()
{
if (m_pContext)
{
return m_pContext->height;
}
return 0;
}
double CVideoDecoder::getFrameRate()
{
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)
{
if (!m_bInited)
{
return FALSE;
}
int cnt = 0;
int ret;
RETRY:
ret = avcodec_send_packet(m_pContext, pkt);
if (ret == AVERROR(EAGAIN))
{
readFrame();
if (cnt++ < 3)
{
goto RETRY;
}
}
else if (ret < 0)
{
return FALSE;
}
if (ret >= 0)
{
return readFrame();
}
else
{
return FALSE;
}
}
BOOL CVideoDecoder::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 CVideoDecoder::readFrame()
{
int ret = 0;
AVFrame * tmp_frame = NULL;
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, avcodec_receive_frame, ret=%d\r\n", __FUNCTION__, ret);
return FALSE;
}
if (m_pFrame->format == m_hwPixFmt)
{
/* retrieve data from GPU to CPU */
ret = av_hwframe_transfer_data(m_pSoftFrame, m_pFrame, 0);
if (ret < 0)
{
log_print(HT_LOG_ERR, "%s, error transferring the data to system memory, ret=%d\r\n", __FUNCTION__, ret);
return FALSE;
}
m_pSoftFrame->pts = m_pFrame->pts;
m_pSoftFrame->pkt_dts = m_pFrame->pkt_dts;
tmp_frame = m_pSoftFrame;
}
else
{
tmp_frame = m_pFrame;
}
render(tmp_frame);
av_frame_unref(tmp_frame);
}
return TRUE;
}
int CVideoDecoder::render(AVFrame * frame)
{
if (m_pCallback)
{
m_pCallback(frame, m_pUserdata);
}
return 1;
}
void CVideoDecoder::flush()
{
if (NULL == m_pContext ||
NULL == m_pContext->codec ||
!(m_pContext->codec->capabilities | AV_CODEC_CAP_DELAY))
{
return;
}
decode(NULL);
}

View File

@@ -0,0 +1,89 @@
/***************************************************************************************
*
* 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_DECODER_H
#define VIDEO_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 <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_DISABLE -1 // disable video acceleration
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);
BOOL init(enum AVCodecID codec, uint8 * extradata = NULL, int extradata_size = 0, int hwMode = HW_DECODING_AUTO);
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);
private:
BOOL readFrame();
int render(AVFrame * frame);
void flush();
int hwDecoderInit(AVCodecContext *ctx, int hwMode);
private:
BOOL m_bInited;
AVCodec * m_pCodec;
AVCodecContext* m_pContext;
AVFrame * m_pFrame;
AVFrame * m_pSoftFrame;
VDCB m_pCallback;
void * m_pUserdata;
AVPixelFormat m_hwPixFmt;
AVBufferRef * m_pHWDeviceCtx;
};
#endif

View File

@@ -0,0 +1,742 @@
/***************************************************************************************
*
* 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_player.h"
#include "utils.h"
#include "media_util.h"
#include "media_parse.h"
#include "media_codec.h"
#include "h264.h"
#include "h265.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/avstring.h>
#include <libavutil/base64.h>
#include <libavutil/imgutils.h>
}
#if __WINDOWS_OS__
#include "video_render_d3d.h"
#include "video_render_gdi.h"
#include "audio_play_win.h"
#elif defined (IOS)
#include "video_render.h"
#include "audio_play_qt.h"
#elif defined (ANDROID)
#include "video_render.h"
#include "audio_play_qt.h"
#elif __LINUX_OS__
#include "video_render_sdl.h"
#include "audio_play_qt.h"
#endif
void VideoDecoderCallback(AVFrame * frame, void * userdata)
{
CVideoPlayer * pPlayer = (CVideoPlayer *) userdata;
pPlayer->onVideoFrame(frame);
}
void AudioDecoderCallback(AVFrame * frame, void * userdata)
{
CVideoPlayer * pPlayer = (CVideoPlayer *) userdata;
pPlayer->onAudioFrame(frame);
}
CVideoPlayer::CVideoPlayer(QObject * parent)
: QObject(parent)
, m_bVideoInited(FALSE)
, m_bAudioInited(FALSE)
, m_pVideoDecoder(NULL)
, m_pAudioDecoder(NULL)
, m_pAudioPlay(NULL)
, m_bPlaying(FALSE)
, m_bPaused(FALSE)
, m_nVideoWnd(0)
, m_bSizeChanged(FALSE)
, m_nHWDecoding(HW_DECODING_AUTO)
, m_nDstVideoFmt(AV_PIX_FMT_YUV420P)
, m_bUpdown(FALSE)
, m_bSnapshot(FALSE)
, m_nSnapVideoFmt(VIDEO_FMT_BGR24)
, m_nVideoCodec(VIDEO_CODEC_NONE)
, m_nAudioCodec(AUDIO_CODEC_NONE)
, m_nSampleRate(0)
, m_nChannel(0)
, m_nBitPerSample(0)
, m_pSnapFrame(NULL)
, m_pRenderFrame(NULL)
, m_bRecording(FALSE)
, m_bNalFlag(FALSE)
, m_pAviCtx(NULL)
{
m_pRecordMutex = sys_os_create_mutex();
memset(&m_h26XParamSets, 0, sizeof(H26XParamSets));
memset(&m_audioClock, 0, sizeof(HTCLOCK));
memset(&m_videoClock, 0, sizeof(HTCLOCK));
}
CVideoPlayer::~CVideoPlayer()
{
close();
}
BOOL CVideoPlayer::open(QString fileName, WId hWnd)
{
m_sFileName = fileName;
m_nVideoWnd = hWnd;
return TRUE;
}
void CVideoPlayer::close()
{
closeVideo();
closeAudio();
if (m_pSnapFrame)
{
av_frame_free(&m_pSnapFrame);
}
if (m_pRenderFrame)
{
av_frame_free(&m_pRenderFrame);
}
stopRecord();
sys_os_destroy_sig_mutex(m_pRecordMutex);
m_pRecordMutex = NULL;
}
void CVideoPlayer::setVolume(int volume)
{
if (m_pAudioPlay)
{
m_pAudioPlay->setVolume(volume);
}
}
void CVideoPlayer::snapshot(int videofmt)
{
m_bSnapshot = TRUE;
m_nSnapVideoFmt = videofmt;
}
BOOL CVideoPlayer::record(QString filepath)
{
if (m_bRecording)
{
return TRUE;
}
m_pAviCtx = avi_write_open(filepath.toLocal8Bit().toStdString().c_str());
if (NULL == m_pAviCtx)
{
log_print(HT_LOG_ERR, "%s, avi_write_open failed. %s\r\n",
__FUNCTION__, filepath.toLocal8Bit().toStdString().c_str());
return FALSE;
}
if (!onRecord())
{
avi_write_close(m_pAviCtx);
m_pAviCtx = NULL;
return FALSE;
}
m_bRecording = TRUE;
return m_bRecording;
}
void CVideoPlayer::stopRecord()
{
sys_os_mutex_enter(m_pRecordMutex);
m_bRecording = FALSE;
m_bNalFlag = FALSE;
memset(&m_h26XParamSets, 0, sizeof(H26XParamSets));
if (m_pAviCtx)
{
avi_write_close(m_pAviCtx);
m_pAviCtx = NULL;
}
sys_os_mutex_leave(m_pRecordMutex);
}
void CVideoPlayer::recordVideo(uint8 * data, int len, uint32 ts, uint16 seq)
{
int codec = VIDEO_CODEC_NONE;
if (!memcmp(m_pAviCtx->v_fcc, "H264", 4))
{
codec = VIDEO_CODEC_H264;
}
else if (!memcmp(m_pAviCtx->v_fcc, "H265", 4))
{
codec = VIDEO_CODEC_H265;
}
if ((VIDEO_CODEC_H264 == codec || VIDEO_CODEC_H265 == codec) && !m_bNalFlag)
{
if (avc_get_h26x_paramsets(data, len, codec, &m_h26XParamSets))
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
m_bNalFlag = 1;
}
}
recordVideoEx(data, len, ts, seq);
if (recordSwitchCheck())
{
recordFileSwitch();
}
}
void CVideoPlayer::recordVideoEx(uint8 * data, int len, uint32 ts, uint16 seq)
{
AVICTX * p_avictx = m_pAviCtx;
if (p_avictx->v_width == 0 || p_avictx->v_height == 0)
{
int codec = VIDEO_CODEC_NONE;
if (memcmp(p_avictx->v_fcc, "H264", 4) == 0)
{
codec = VIDEO_CODEC_H264;
}
else if (memcmp(p_avictx->v_fcc, "H265", 4) == 0)
{
codec = VIDEO_CODEC_H265;
}
else if (memcmp(p_avictx->v_fcc, "JPEG", 4) == 0)
{
codec = VIDEO_CODEC_JPEG;
}
else if (memcmp(p_avictx->v_fcc, "MP4V", 4) == 0)
{
codec = VIDEO_CODEC_MP4;
}
avc_parse_video_size(codec, data, len, &p_avictx->v_width, &p_avictx->v_height);
if (p_avictx->v_width && p_avictx->v_height)
{
avi_update_header(p_avictx);
}
}
int key = 0;
if (memcmp(p_avictx->v_fcc, "H264", 4) == 0)
{
uint8 nalu_t = (data[4] & 0x1F);
key = (nalu_t == 5 || nalu_t == 7 || nalu_t == 8);
}
else if (memcmp(p_avictx->v_fcc, "H265", 4) == 0)
{
uint8 nalu_t = (data[4] >> 1) & 0x3F;
key = ((nalu_t >= 16 && nalu_t <= 21) || nalu_t == 32 || nalu_t == 33 || nalu_t == 34);
}
else if (memcmp(p_avictx->v_fcc, "MP4V", 4) == 0)
{
key = 1;
}
else if (memcmp(p_avictx->v_fcc, "JPEG", 4) == 0)
{
key = 1;
}
avi_write_video(p_avictx, data, len, ts, key);
}
void CVideoPlayer::recordAudio(uint8 * data, int len, uint32 ts, uint16 seq)
{
AVICTX * p_avictx = m_pAviCtx;
avi_write_audio(p_avictx, data, len, ts);
if (recordSwitchCheck())
{
recordFileSwitch();
}
}
BOOL CVideoPlayer::recordSwitchCheck()
{
uint64 tlen = avi_get_file_length(m_pAviCtx);
uint32 mtime = avi_get_media_time(m_pAviCtx);
uint32 recordSize = getRecordSize();
if (recordSize == 0)
{
recordSize = 1048576; // max 1G file size
}
// Switch according to the recording size
if (tlen > recordSize * 1024)
{
return TRUE;
}
uint32 recordTime = getRecordTime();
// Switch according to the recording duration
if (recordTime > 0 && mtime > recordTime * 1000)
{
return TRUE;
}
return FALSE;
}
void CVideoPlayer::recordFileSwitch()
{
onRecordFileSwitch();
}
BOOL CVideoPlayer::openVideo(enum AVCodecID codec, uint8 * extradata, int extradata_size)
{
if (m_bVideoInited)
{
return TRUE;
}
m_pVideoDecoder = new CVideoDecoder();
if (m_pVideoDecoder)
{
m_bVideoInited = m_pVideoDecoder->init(codec, extradata, extradata_size, m_nHWDecoding);
}
if (m_bVideoInited)
{
m_pVideoDecoder->setCallback(VideoDecoderCallback, this);
}
m_nVideoCodec = to_video_codec(codec);
return m_bVideoInited;
}
BOOL CVideoPlayer::openVideo(int codec, uint8 * extradata, int extradata_size)
{
return openVideo(to_video_avcodecid(codec), extradata, extradata_size);
}
void CVideoPlayer::closeVideo()
{
if (m_pVideoDecoder)
{
delete m_pVideoDecoder;
m_pVideoDecoder = NULL;
}
m_bVideoInited = FALSE;
}
BOOL CVideoPlayer::openAudio(enum AVCodecID codec, int samplerate, int channels, int bitpersample)
{
if (m_bAudioInited)
{
return TRUE;
}
m_pAudioDecoder = new CAudioDecoder();
if (m_pAudioDecoder)
{
m_bAudioInited = m_pAudioDecoder->init(codec, samplerate, channels, bitpersample);
}
if (m_bAudioInited)
{
m_pAudioDecoder->setCallback(AudioDecoderCallback, this);
#if __WINDOWS_OS__
m_pAudioPlay = new CWAudioPlay();
#elif __LINUX_OS__
m_pAudioPlay = new CQAudioPlay();
#endif
if (m_pAudioPlay)
{
m_pAudioPlay->startPlay(samplerate, channels);
}
}
m_nAudioCodec = to_audio_codec(codec);
m_nSampleRate = samplerate;
m_nChannel = channels;
m_nBitPerSample = bitpersample;
return m_bAudioInited;
}
BOOL CVideoPlayer::openAudio(int codec, int samplerate, int channels, int bitpersample)
{
return openAudio(to_audio_avcodecid(codec), samplerate, channels, bitpersample);
}
void CVideoPlayer::closeAudio()
{
if (m_pAudioDecoder)
{
delete m_pAudioDecoder;
m_pAudioDecoder = NULL;
}
if (m_pAudioPlay)
{
delete m_pAudioPlay;
m_pAudioPlay = NULL;
}
m_bAudioInited = FALSE;
}
void CVideoPlayer::setWindowSize(QSize size)
{
m_bSizeChanged = TRUE;
m_size = size;
}
int CVideoPlayer::getVideoWidth()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getWidth();
}
return 0;
}
int CVideoPlayer::getVideoHeight()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getHeight();
}
return 0;
}
double CVideoPlayer::getFrameRate()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getFrameRate();
}
return 0;
}
void CVideoPlayer::playVideo(uint8 * data, int len, uint32 ts, uint16 seq)
{
emit updateStatistics(len);
if (m_bRecording)
{
sys_os_mutex_enter(m_pRecordMutex);
recordVideo(data, len, ts, seq);
sys_os_mutex_leave(m_pRecordMutex);
}
updateClock(&m_videoClock, ts, getVideoClock());
if (m_bVideoInited)
{
m_pVideoDecoder->decode(data, len, m_videoClock.SyncTime.tv_sec * 1000000 + m_videoClock.SyncTime.tv_usec);
}
else
{
int codec = getVideoCodec();
if (VIDEO_CODEC_H264 == codec || VIDEO_CODEC_H265 == codec)
{
if (avc_get_h26x_paramsets(data, len, codec, &m_h26XParamSets))
{
int extradata_size = 0;
uint8 extradata[1024];
if (VIDEO_CODEC_H264 == codec)
{
memcpy(extradata, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
if (openVideo(codec, extradata, extradata_size))
{
m_pVideoDecoder->decode(m_h26XParamSets.sps, m_h26XParamSets.sps_size);
m_pVideoDecoder->decode(m_h26XParamSets.pps, m_h26XParamSets.pps_size);
m_pVideoDecoder->decode(data, len);
}
}
else if (VIDEO_CODEC_H265 == codec)
{
int extradata_size = 0;
uint8 extradata[2048];
memcpy(extradata, m_h26XParamSets.vps, m_h26XParamSets.vps_size);
extradata_size += m_h26XParamSets.vps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.sps, m_h26XParamSets.sps_size);
extradata_size += m_h26XParamSets.sps_size;
memcpy(extradata+extradata_size, m_h26XParamSets.pps, m_h26XParamSets.pps_size);
extradata_size += m_h26XParamSets.pps_size;
if (openVideo(codec, extradata, extradata_size))
{
m_pVideoDecoder->decode(m_h26XParamSets.vps, m_h26XParamSets.vps_size);
m_pVideoDecoder->decode(m_h26XParamSets.sps, m_h26XParamSets.sps_size);
m_pVideoDecoder->decode(m_h26XParamSets.pps, m_h26XParamSets.pps_size);
m_pVideoDecoder->decode(data, len);
}
}
}
}
}
}
void CVideoPlayer::playAudio(uint8 * data, int len, uint32 ts, uint16 seq)
{
emit updateStatistics(len);
if (m_bRecording)
{
sys_os_mutex_enter(m_pRecordMutex);
recordAudio(data, len, ts, seq);
sys_os_mutex_leave(m_pRecordMutex);
}
updateClock(&m_audioClock, ts, getAudioClock());
if (m_bAudioInited)
{
m_pAudioDecoder->decode(data, len, m_audioClock.SyncTime.tv_sec * 1000000 + m_audioClock.SyncTime.tv_usec);
}
}
void CVideoPlayer::updateClock(HTCLOCK * clock, uint32 ts, int frequency)
{
if (ts == 0)
{
return;
}
if (clock->SyncTime.tv_sec == 0 && clock->SyncTime.tv_usec == 0)
{
clock->SyncTimestamp = ts;
gettimeofday(&clock->SyncTime, NULL);
}
int timestampDiff = ts - clock->SyncTimestamp;
// Divide this by the timestamp frequency to get real time:
double timeDiff = timestampDiff / (double)frequency;
uint32 const million = 1000000;
uint32 seconds, uSeconds;
if (timeDiff >= 0.0)
{
seconds = clock->SyncTime.tv_sec + (uint32)(timeDiff);
uSeconds = clock->SyncTime.tv_usec + (uint32)((timeDiff - (uint32)timeDiff)*million);
if (uSeconds >= million)
{
uSeconds -= million;
++seconds;
}
}
else
{
timeDiff = -timeDiff;
seconds = clock->SyncTime.tv_sec - (uint32)(timeDiff);
uSeconds = clock->SyncTime.tv_usec - (uint32)((timeDiff - (uint32)timeDiff)*million);
if ((int)uSeconds < 0)
{
uSeconds += million;
--seconds;
}
}
// Save these as the new synchronization timestamp & time:
clock->SyncTimestamp = ts;
clock->SyncTime.tv_sec = seconds;
clock->SyncTime.tv_usec = uSeconds;
}
BOOL CVideoPlayer::initFrame(AVFrame *& frame, int width, int height, AVPixelFormat pixfmt)
{
if (width == 0 || height == 0 || pixfmt == AV_PIX_FMT_NONE)
{
return FALSE;
}
if (NULL == frame || frame->width != width || frame->height != height || frame->format != pixfmt)
{
if (frame)
{
av_frame_free(&frame);
}
frame = av_frame_alloc();
if (NULL == frame)
{
return FALSE;
}
frame->format = pixfmt;
frame->width = width;
frame->height = height;
if (0 != av_frame_get_buffer(frame, 0))
{
av_frame_free(&frame);
return FALSE;
}
av_frame_make_writable(frame);
}
return TRUE;
}
BOOL CVideoPlayer::initVideoRender(int width, int height)
{
return TRUE;
}
BOOL CVideoPlayer::doSnapshot(AVFrame * frame)
{
if (!initFrame(m_pSnapFrame, frame->width, frame->height, to_avpixelformat(m_nSnapVideoFmt)))
{
return FALSE;
}
if (NULL == convertFrame(frame, m_pSnapFrame, FALSE))
{
return FALSE;
}
emit snapshoted(m_pSnapFrame);
return TRUE;
}
AVFrame * CVideoPlayer::convertFrame(AVFrame * srcframe, AVFrame * dstframe, BOOL updown)
{
if (NULL == srcframe || NULL == dstframe)
{
return NULL;
}
SwsContext * swsctx = sws_getContext(srcframe->width, srcframe->height, (enum AVPixelFormat)srcframe->format,
srcframe->width, srcframe->height, (enum AVPixelFormat)dstframe->format, SWS_BICUBIC, NULL, NULL, NULL);
if (swsctx)
{
if (updown)
{
srcframe->data[0] += srcframe->linesize[0] * (srcframe->height - 1);
srcframe->linesize[0] *= -1;
srcframe->data[1] += srcframe->linesize[1] * (srcframe->height / 2 - 1);
srcframe->linesize[1] *= -1;
srcframe->data[2] += srcframe->linesize[2] * (srcframe->height / 2 - 1);
srcframe->linesize[2] *= -1;
}
int ret = sws_scale(swsctx, srcframe->data, srcframe->linesize, 0, srcframe->height, dstframe->data, dstframe->linesize);
if (ret > 0)
{
dstframe->pts = srcframe->pts;
dstframe->pkt_dts = srcframe->pkt_dts;
sws_freeContext(swsctx);
return dstframe;
}
else
{
log_print(HT_LOG_ERR, "%s, sws_scale failed\r\n", __FUNCTION__);
sws_freeContext(swsctx);
return NULL;
}
}
return NULL;
}
void CVideoPlayer::onVideoFrame(AVFrame * frame)
{
// Perform snapshot request
if (m_bSnapshot)
{
if (doSnapshot(frame))
{
m_bSnapshot = FALSE;
}
}
AVFrame * dst = NULL;
if (m_nDstVideoFmt != frame->format)
{
if (initFrame(m_pRenderFrame, frame->width, frame->height, m_nDstVideoFmt))
{
if (NULL != convertFrame(frame, m_pRenderFrame, m_bUpdown))
{
dst = av_frame_clone(m_pRenderFrame);
}
}
}
else
{
dst = av_frame_clone(frame);
}
emit imageReady(dst);
}
void CVideoPlayer::onAudioFrame(AVFrame * frame)
{
if (m_pAudioPlay)
{
// Will wait for the playback to complete before returning
m_pAudioPlay->playAudio(frame->data[0], frame->nb_samples * frame->channels * 2);
}
}

View File

@@ -0,0 +1,190 @@
/***************************************************************************************
*
* 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_PLAYER_H
#define VIDEO_PLAYER_H
#include "sys_inc.h"
#include "media_util.h"
#include "hqueue.h"
#include "video_decoder.h"
#include "audio_decoder.h"
#include "video_render.h"
#include "audio_play.h"
#include "avi_write.h"
#include <QObject>
#include <QWidget>
#include <list>
typedef struct
{
uint32 SyncTimestamp;
struct timeval SyncTime;
} HTCLOCK;
typedef std::list<AVFrame*> FRAMELIST;
class CVideoPlayer : public QObject
{
Q_OBJECT
public:
CVideoPlayer(QObject * parent = 0);
virtual ~CVideoPlayer();
virtual BOOL open(QString fileName, WId hWnd);
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(QString filepath);
virtual void stopRecord();
virtual BOOL isRecording() {return m_bRecording;}
virtual BOOL onRecord() {return FALSE;}
virtual void onRecordFileSwitch() {}
virtual int getVideoClock() {return 1000;}
virtual int getAudioClock() {return 1000;}
virtual void setWindowSize(QSize 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(QString acct, QString pass) {m_acct=acct; m_pass=pass;}
virtual void setHWDecoding(int mode) {m_nHWDecoding=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 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();
double getFrameRate();
int getSampleRate() {return m_nSampleRate;}
int getChannel() {return m_nChannel;}
void onVideoFrame(AVFrame * frame);
void onAudioFrame(AVFrame * frame);
void audioPlayThread();
void videoPlayThread();
signals:
void notify(int);
void snapshoted(AVFrame *);
void imageReady(AVFrame *);
void updateStatistics(int);
protected:
void updateClock(HTCLOCK * clock, uint32 ts, int frequency);
BOOL initFrame(AVFrame *& frame, int width, int height, AVPixelFormat pixfmt);
BOOL initVideoRender(int width, int height);
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();
protected:
BOOL m_bVideoInited;
BOOL m_bAudioInited;
CVideoDecoder * m_pVideoDecoder;
CAudioDecoder * m_pAudioDecoder;
CAudioPlay * m_pAudioPlay;
BOOL m_bPlaying;
BOOL m_bPaused;
QString m_acct;
QString m_pass;
QString m_sFileName;
WId m_nVideoWnd;
QSize m_size;
BOOL m_bSizeChanged;
int m_nHWDecoding;
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;
AVFrame * m_pRenderFrame;
BOOL m_bRecording;
BOOL m_bNalFlag;
AVICTX * m_pAviCtx;
void * m_pRecordMutex;
HTCLOCK m_audioClock;
HTCLOCK m_videoClock;
};
#endif // end of VIDEO_PLAYER_H

View File

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

View File

@@ -0,0 +1,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 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) {}
protected:
BOOL m_bInited;
WId m_hWnd;
int m_nVideoWidth;
int m_nVideoHeight;
int m_nVideoFmt;
};
#endif // end of VIDEO_RENDER_H