Update MediaClient
This commit is contained in:
119
MediaClient/MediaClientForMobile/media/audio_capture.cpp
Normal file
119
MediaClient/MediaClientForMobile/media/audio_capture.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "audio_capture.h"
|
||||
#include "lock.h"
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
CAudioCapture * CAudioCapture::m_pInstance[] = {NULL, NULL, NULL, NULL};
|
||||
void * CAudioCapture::m_pInstMutex = sys_os_create_mutex();
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
CAudioCapture::CAudioCapture()
|
||||
{
|
||||
m_nDevIndex = 0;
|
||||
m_nRefCnt = 0;
|
||||
|
||||
m_nChannels = 2;
|
||||
m_nSampleRate = 8000;
|
||||
m_nBitrate = 0;
|
||||
m_nSampleSize = 16;
|
||||
|
||||
m_pMutex = sys_os_create_mutex();
|
||||
m_bInited = FALSE;
|
||||
m_bCapture = FALSE;
|
||||
m_hCapture = 0;
|
||||
}
|
||||
|
||||
CAudioCapture::~CAudioCapture()
|
||||
{
|
||||
sys_os_destroy_sig_mutex(m_pMutex);
|
||||
}
|
||||
|
||||
CAudioCapture * CAudioCapture::getInstance(int devid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CAudioCapture::freeInstance(int devid)
|
||||
{
|
||||
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pInstance[devid])
|
||||
{
|
||||
sys_os_mutex_enter(m_pInstMutex);
|
||||
|
||||
if (m_pInstance[devid])
|
||||
{
|
||||
m_pInstance[devid]->m_nRefCnt--;
|
||||
|
||||
if (m_pInstance[devid]->m_nRefCnt <= 0)
|
||||
{
|
||||
delete m_pInstance[devid];
|
||||
m_pInstance[devid] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sys_os_mutex_leave(m_pInstMutex);
|
||||
}
|
||||
}
|
||||
|
||||
char * CAudioCapture::getAuxSDPLine(int rtp_pt)
|
||||
{
|
||||
return m_encoder.getAuxSDPLine(rtp_pt);
|
||||
}
|
||||
|
||||
int CAudioCapture::getDeviceNums()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CAudioCapture::startCapture()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CAudioCapture::stopCapture(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CAudioCapture::addCallback(AudioDataCallback pCallback, void * pUserdata)
|
||||
{
|
||||
m_encoder.addCallback(pCallback, pUserdata);
|
||||
}
|
||||
|
||||
void CAudioCapture::delCallback(AudioDataCallback pCallback, void * pUserdata)
|
||||
{
|
||||
m_encoder.delCallback(pCallback, pUserdata);
|
||||
}
|
||||
|
||||
|
||||
|
||||
87
MediaClient/MediaClientForMobile/media/audio_capture.h
Normal file
87
MediaClient/MediaClientForMobile/media/audio_capture.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _AUDIO_CAPTURE_H
|
||||
#define _AUDIO_CAPTURE_H
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
#include "audio_encoder.h"
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
#define MAX_AUDIO_DEV_NUMS 8
|
||||
#define DEF_AUDIO_CAP_DEV (MAX_AUDIO_DEV_NUMS-1)
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
class CAudioCapture
|
||||
{
|
||||
public:
|
||||
virtual ~CAudioCapture();
|
||||
|
||||
// get audio capture devcie nubmers
|
||||
static int getDeviceNums();
|
||||
|
||||
// get single instance
|
||||
static CAudioCapture * getInstance(int devid);
|
||||
// free instance
|
||||
virtual void freeInstance(int devid);
|
||||
|
||||
virtual BOOL initCapture(int codec, int sampleRate, int channels, int bitrate);
|
||||
virtual BOOL startCapture();
|
||||
|
||||
virtual char * getAuxSDPLine(int rtp_pt);
|
||||
|
||||
virtual void addCallback(AudioDataCallback pCallback, void * pUserdata);
|
||||
virtual void delCallback(AudioDataCallback pCallback, void * pUserdata);
|
||||
|
||||
virtual BOOL isCapturing() {return m_bCapture;}
|
||||
|
||||
protected:
|
||||
CAudioCapture();
|
||||
CAudioCapture(CAudioCapture &obj);
|
||||
|
||||
virtual void stopCapture();
|
||||
|
||||
public:
|
||||
int m_nDevIndex;
|
||||
int m_nRefCnt; // single instance ref count
|
||||
|
||||
static void * m_pInstMutex; // instance mutex
|
||||
static CAudioCapture * m_pInstance[MAX_AUDIO_DEV_NUMS];
|
||||
|
||||
protected:
|
||||
int m_nChannels; // channel number
|
||||
int m_nSampleRate; // sample rate
|
||||
int m_nBitrate; // bitrate
|
||||
int m_nSampleSize; // sample size
|
||||
|
||||
CAudioEncoder m_encoder; // audio encoder
|
||||
|
||||
void * m_pMutex; // mutex
|
||||
BOOL m_bInited; // inited flag
|
||||
BOOL m_bCapture; // capturing flag
|
||||
pthread_t m_hCapture; // capture thread handler
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
176
MediaClient/MediaClientForMobile/media/audio_capture_android.cpp
Normal file
176
MediaClient/MediaClientForMobile/media/audio_capture_android.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "lock.h"
|
||||
#include "audio_capture_android.h"
|
||||
|
||||
|
||||
void dataCallback(uint8 * pdata, int len, void * puser)
|
||||
{
|
||||
CAAudioCapture * pthis = (CAAudioCapture *) puser;
|
||||
|
||||
pthis->dataCallback(pdata, len);
|
||||
}
|
||||
|
||||
CAAudioCapture::CAAudioCapture()
|
||||
: CAudioCapture()
|
||||
, m_pInput(0)
|
||||
{
|
||||
}
|
||||
|
||||
CAAudioCapture::~CAAudioCapture()
|
||||
{
|
||||
stopCapture();
|
||||
}
|
||||
|
||||
CAudioCapture * CAAudioCapture::getInstance(int devid)
|
||||
{
|
||||
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sys_os_mutex_enter(m_pInstMutex);
|
||||
|
||||
if (NULL == m_pInstance[devid])
|
||||
{
|
||||
m_pInstance[devid] = new CAAudioCapture;
|
||||
if (m_pInstance[devid])
|
||||
{
|
||||
m_pInstance[devid]->m_nRefCnt++;
|
||||
m_pInstance[devid]->m_nDevIndex = devid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pInstance[devid]->m_nRefCnt++;
|
||||
}
|
||||
|
||||
sys_os_mutex_leave(m_pInstMutex);
|
||||
|
||||
return m_pInstance[devid];
|
||||
}
|
||||
|
||||
int CAAudioCapture::getDeviceNums()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CAAudioCapture::listDevice()
|
||||
{
|
||||
}
|
||||
|
||||
int CAAudioCapture::getDeviceIndex(const char * name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CAAudioCapture::getDeviceName(int index, char * name, int namesize)
|
||||
{
|
||||
strncpy(name, "Default", namesize);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CAAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
|
||||
{
|
||||
CLock lock(m_pMutex);
|
||||
|
||||
if (m_bInited)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
m_nChannels = channels;
|
||||
m_nSampleRate = samplerate;
|
||||
m_nBitrate = bitrate;
|
||||
|
||||
AudioEncoderParam params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
params.SrcChannels = 2;
|
||||
params.SrcSamplefmt = AV_SAMPLE_FMT_S16;
|
||||
params.SrcSamplerate = samplerate;
|
||||
params.DstChannels = channels;
|
||||
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
|
||||
params.DstSamplerate = samplerate;
|
||||
params.DstBitrate = bitrate;
|
||||
params.DstCodec = codec;
|
||||
|
||||
if (m_encoder.init(¶ms) == FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_bInited = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CAAudioCapture::startCapture()
|
||||
{
|
||||
CLock lock(m_pMutex);
|
||||
|
||||
if (!m_bInited)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (m_pInput)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
m_pInput = new GlesInput();
|
||||
if (NULL == m_pInput)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pInput->setCallback(::dataCallback, this);
|
||||
if (!m_pInput->start(m_nSampleRate))
|
||||
{
|
||||
log_print(HT_LOG_ERR, "%s, start audio recoding failed\r\n", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CAAudioCapture::stopCapture(void)
|
||||
{
|
||||
CLock lock(m_pMutex);
|
||||
|
||||
if (m_pInput)
|
||||
{
|
||||
delete m_pInput;
|
||||
m_pInput = NULL;
|
||||
}
|
||||
|
||||
m_bInited = FALSE;
|
||||
}
|
||||
|
||||
void CAAudioCapture::dataCallback(uint8 * pdata, int len)
|
||||
{
|
||||
m_encoder.encode(pdata, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
249
MediaClient/MediaClientForMobile/media/audio_decoder.cpp
Normal file
249
MediaClient/MediaClientForMobile/media/audio_decoder.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
MediaClient/MediaClientForMobile/media/audio_decoder.h
Normal file
73
MediaClient/MediaClientForMobile/media/audio_decoder.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef AUDIO_DECODER_H
|
||||
#define AUDIO_DECODER_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "media_format.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/avutil.h"
|
||||
#include "libswscale/swscale.h"
|
||||
#include "libavformat/avformat.h"
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
typedef void (*ADCB)(AVFrame * frame, void * userdata);
|
||||
|
||||
class CAudioDecoder
|
||||
{
|
||||
public:
|
||||
CAudioDecoder();
|
||||
~CAudioDecoder();
|
||||
|
||||
BOOL init(int codec, int samplerate, int channels, int bitpersample = 0);
|
||||
BOOL init(enum AVCodecID codec, int samplerate, int channels, int bitpersample = 0);
|
||||
void uninit();
|
||||
|
||||
BOOL decode(uint8 * data, int len, int64_t pts = AV_NOPTS_VALUE);
|
||||
BOOL decode(AVPacket *pkt);
|
||||
void setCallback(ADCB pCallback, void * pUserdata) {m_pCallback = pCallback; m_pUserdata = pUserdata;}
|
||||
|
||||
private:
|
||||
void flush();
|
||||
void render(AVFrame * frame);
|
||||
|
||||
private:
|
||||
BOOL m_bInited;
|
||||
int m_nSamplerate;
|
||||
int m_nChannels;
|
||||
|
||||
AVCodec * m_pCodec;
|
||||
AVCodecContext* m_pContext;
|
||||
AVFrame * m_pFrame;
|
||||
AVFrame * m_pResampleFrame;
|
||||
SwrContext * m_pSwrCtx;
|
||||
AVSampleFormat m_nDstFormat;
|
||||
|
||||
ADCB m_pCallback;
|
||||
void * m_pUserdata;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
577
MediaClient/MediaClientForMobile/media/audio_encoder.cpp
Normal file
577
MediaClient/MediaClientForMobile/media/audio_encoder.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "audio_encoder.h"
|
||||
#include "media_format.h"
|
||||
#include "avcodec_mutex.h"
|
||||
#include "media_codec.h"
|
||||
|
||||
CAudioEncoder::CAudioEncoder()
|
||||
{
|
||||
m_nCodecId = AV_CODEC_ID_NONE;
|
||||
m_bInited = FALSE;
|
||||
|
||||
memset(&m_EncoderParams, 0, sizeof(AudioEncoderParam));
|
||||
|
||||
m_pCodecCtx = NULL;
|
||||
m_pFrame = NULL;
|
||||
m_pResampleFrame = NULL;
|
||||
m_pSwrCtx = NULL;
|
||||
m_pPkt = NULL;
|
||||
|
||||
memset(&m_AudioBuffer, 0, sizeof(AudioBuffer));
|
||||
|
||||
m_pCallbackMutex = sys_os_create_mutex();
|
||||
m_pCallbackList = h_list_create(FALSE);
|
||||
}
|
||||
|
||||
CAudioEncoder::~CAudioEncoder()
|
||||
{
|
||||
uninit();
|
||||
|
||||
h_list_free_container(m_pCallbackList);
|
||||
|
||||
sys_os_destroy_sig_mutex(m_pCallbackMutex);
|
||||
}
|
||||
|
||||
int CAudioEncoder::computeBitrate(AVCodecID codec, int samplerate, int channels, int quality)
|
||||
{
|
||||
int bitrate;
|
||||
|
||||
if (m_nCodecId == AV_CODEC_ID_ADPCM_G726)
|
||||
{
|
||||
bitrate = 16000; // G726 16kbit/s
|
||||
}
|
||||
else if (m_nCodecId == AV_CODEC_ID_ADPCM_G722)
|
||||
{
|
||||
bitrate = 64000; // G722 64kbit/s
|
||||
}
|
||||
else if (m_nCodecId == AV_CODEC_ID_PCM_ALAW || m_nCodecId == AV_CODEC_ID_PCM_MULAW)
|
||||
{
|
||||
bitrate = samplerate * channels * 8;
|
||||
}
|
||||
else if (m_nCodecId == AV_CODEC_ID_AAC)
|
||||
{
|
||||
bitrate = samplerate * channels * 16 / 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitrate = samplerate * channels;
|
||||
}
|
||||
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::init(AudioEncoderParam * params)
|
||||
{
|
||||
memcpy(&m_EncoderParams, params, sizeof(AudioEncoderParam));
|
||||
|
||||
m_nCodecId = to_audio_avcodecid(params->DstCodec);
|
||||
|
||||
if (AV_CODEC_ID_AAC == m_nCodecId)
|
||||
{
|
||||
// the ffmepg AAC encoder only support AV_SAMPLE_FMT_FLTP
|
||||
m_EncoderParams.DstSamplefmt = AV_SAMPLE_FMT_FLTP;
|
||||
}
|
||||
|
||||
AVCodec * pCodec = avcodec_find_encoder(m_nCodecId);
|
||||
if (pCodec == NULL)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "avcodec_find_encoder failed, %d\r\n", m_nCodecId);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pCodecCtx = avcodec_alloc_context3(pCodec);
|
||||
if (m_pCodecCtx == NULL)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "avcodec_alloc_context3 failed\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pCodecCtx->codec_id = m_nCodecId;
|
||||
m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
m_pCodecCtx->qblur = 0.5f;
|
||||
m_pCodecCtx->time_base.num = 1;
|
||||
m_pCodecCtx->time_base.den = m_EncoderParams.DstSamplerate;
|
||||
m_pCodecCtx->sample_rate = m_EncoderParams.DstSamplerate;
|
||||
m_pCodecCtx->channels = m_EncoderParams.DstChannels;
|
||||
m_pCodecCtx->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
|
||||
m_pCodecCtx->sample_fmt = m_EncoderParams.DstSamplefmt;
|
||||
|
||||
if (m_EncoderParams.DstBitrate > 0)
|
||||
{
|
||||
m_pCodecCtx->bit_rate = m_EncoderParams.DstBitrate * 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pCodecCtx->bit_rate = computeBitrate(m_nCodecId, m_EncoderParams.DstSamplerate, m_EncoderParams.DstChannels, 80);
|
||||
}
|
||||
|
||||
m_pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||
|
||||
av_opt_set(m_pCodecCtx->priv_data, "preset", "superfast", 0);
|
||||
av_opt_set(m_pCodecCtx->priv_data, "tune", "zerolatency", 0);
|
||||
|
||||
if (AV_CODEC_ID_AAC == m_nCodecId)
|
||||
{
|
||||
m_pCodecCtx->profile = FF_PROFILE_AAC_LOW; // AAC -LC
|
||||
}
|
||||
else if (AV_CODEC_ID_ADPCM_G726 == m_nCodecId)
|
||||
{
|
||||
m_pCodecCtx->bits_per_coded_sample = m_pCodecCtx->bit_rate / 8000;
|
||||
m_pCodecCtx->bit_rate = m_pCodecCtx->bits_per_coded_sample * 8000;
|
||||
}
|
||||
|
||||
if (avcodec_thread_open(m_pCodecCtx, pCodec, NULL) < 0)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "avcodec_thread_open failed, audio encoder\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pFrame = av_frame_alloc();
|
||||
if (NULL == m_pFrame)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (m_EncoderParams.SrcSamplerate != m_EncoderParams.DstSamplerate ||
|
||||
m_EncoderParams.SrcChannels != m_EncoderParams.DstChannels ||
|
||||
m_EncoderParams.SrcSamplefmt != m_EncoderParams.DstSamplefmt)
|
||||
{
|
||||
m_pSwrCtx = swr_alloc_set_opts(NULL,
|
||||
av_get_default_channel_layout(m_EncoderParams.DstChannels), m_EncoderParams.DstSamplefmt, m_EncoderParams.DstSamplerate,
|
||||
av_get_default_channel_layout(m_EncoderParams.SrcChannels), m_EncoderParams.SrcSamplefmt, m_EncoderParams.SrcSamplerate, 0, NULL);
|
||||
|
||||
swr_init(m_pSwrCtx);
|
||||
}
|
||||
|
||||
if (m_pCodecCtx->frame_size == 0)
|
||||
{
|
||||
m_pCodecCtx->frame_size = 1024;
|
||||
}
|
||||
|
||||
m_AudioBuffer.tlen = 64 * m_pCodecCtx->frame_size * m_EncoderParams.DstChannels * av_get_bytes_per_sample(m_EncoderParams.DstSamplefmt);
|
||||
m_AudioBuffer.data[0] = (uint8 *)av_malloc(m_AudioBuffer.tlen);
|
||||
m_AudioBuffer.data[1] = (uint8 *)av_malloc(m_AudioBuffer.tlen);
|
||||
m_AudioBuffer.size[0] = 0;
|
||||
m_AudioBuffer.size[1] = 0;
|
||||
m_AudioBuffer.samples = 0;
|
||||
|
||||
/* packet for holding encoded output */
|
||||
m_pPkt = av_packet_alloc();
|
||||
if (!m_pPkt)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "could not allocate the packet\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_bInited = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CAudioEncoder::uninit()
|
||||
{
|
||||
flush();
|
||||
|
||||
if (m_pCodecCtx)
|
||||
{
|
||||
avcodec_thread_close(m_pCodecCtx);
|
||||
avcodec_free_context(&m_pCodecCtx);
|
||||
}
|
||||
|
||||
if (m_pFrame)
|
||||
{
|
||||
av_frame_free(&m_pFrame);
|
||||
}
|
||||
|
||||
if (m_pResampleFrame)
|
||||
{
|
||||
av_frame_free(&m_pResampleFrame);
|
||||
}
|
||||
|
||||
if (m_pSwrCtx)
|
||||
{
|
||||
swr_free(&m_pSwrCtx);
|
||||
}
|
||||
|
||||
if (m_pPkt)
|
||||
{
|
||||
av_packet_free(&m_pPkt);
|
||||
}
|
||||
|
||||
if (m_AudioBuffer.data[0])
|
||||
{
|
||||
av_freep(&m_AudioBuffer.data[0]);
|
||||
}
|
||||
|
||||
if (m_AudioBuffer.data[1])
|
||||
{
|
||||
av_freep(&m_AudioBuffer.data[1]);
|
||||
}
|
||||
|
||||
m_bInited = FALSE;
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::bufferFrame(AVFrame * pFrame)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
int samplesize = av_get_bytes_per_sample((AVSampleFormat)pFrame->format);
|
||||
|
||||
int size = pFrame->nb_samples * samplesize;
|
||||
|
||||
assert(m_AudioBuffer.size[0] + size <= m_AudioBuffer.tlen);
|
||||
|
||||
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
|
||||
{
|
||||
memcpy(m_AudioBuffer.data[0]+m_AudioBuffer.size[0], pFrame->data[0], size);
|
||||
m_AudioBuffer.size[0] += size;
|
||||
|
||||
memcpy(m_AudioBuffer.data[1]+m_AudioBuffer.size[1], pFrame->data[1], size);
|
||||
m_AudioBuffer.size[1] += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(m_AudioBuffer.data[0]+m_AudioBuffer.size[0], pFrame->data[0], size * m_EncoderParams.DstChannels);
|
||||
m_AudioBuffer.size[0] += size * m_EncoderParams.DstChannels;
|
||||
}
|
||||
|
||||
m_AudioBuffer.samples += pFrame->nb_samples;
|
||||
|
||||
while (m_AudioBuffer.samples >= m_pCodecCtx->frame_size)
|
||||
{
|
||||
int linesize;
|
||||
|
||||
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
|
||||
{
|
||||
linesize = samplesize * m_pCodecCtx->frame_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
linesize = samplesize * m_pCodecCtx->frame_size * m_EncoderParams.DstChannels;
|
||||
}
|
||||
|
||||
m_pFrame->data[0] = m_AudioBuffer.data[0];
|
||||
m_pFrame->data[1] = m_AudioBuffer.data[1];
|
||||
m_pFrame->linesize[0] = linesize;
|
||||
m_pFrame->linesize[1] = linesize;
|
||||
m_pFrame->nb_samples = m_pCodecCtx->frame_size;
|
||||
m_pFrame->format = m_EncoderParams.DstSamplefmt;
|
||||
m_pFrame->key_frame = 1;
|
||||
m_pFrame->sample_rate = m_EncoderParams.DstSamplerate;
|
||||
m_pFrame->channels = m_EncoderParams.DstChannels;
|
||||
m_pFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
|
||||
|
||||
ret = encodeInternal(m_pFrame);
|
||||
|
||||
m_AudioBuffer.size[0] -= linesize;
|
||||
|
||||
if (m_AudioBuffer.size[0] > 0)
|
||||
{
|
||||
memmove(m_AudioBuffer.data[0], m_AudioBuffer.data[0]+linesize, m_AudioBuffer.size[0]);
|
||||
}
|
||||
|
||||
if (av_sample_fmt_is_planar((AVSampleFormat)pFrame->format) && m_EncoderParams.DstChannels > 1)
|
||||
{
|
||||
m_AudioBuffer.size[1] -= linesize;
|
||||
|
||||
if (m_AudioBuffer.size[1] > 0)
|
||||
{
|
||||
memmove(m_AudioBuffer.data[1], m_AudioBuffer.data[1]+linesize, m_AudioBuffer.size[1]);
|
||||
}
|
||||
}
|
||||
|
||||
m_AudioBuffer.samples -= m_pCodecCtx->frame_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CAudioEncoder::flush()
|
||||
{
|
||||
if (NULL == m_pCodecCtx ||
|
||||
NULL == m_pCodecCtx->codec ||
|
||||
!(m_pCodecCtx->codec->capabilities | AV_CODEC_CAP_DELAY))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
encodeInternal(NULL);
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::encodeInternal(AVFrame * pFrame)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* send the frame for encoding */
|
||||
ret = avcodec_send_frame(m_pCodecCtx, pFrame);
|
||||
if (ret < 0)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "error sending the frame to the encoder\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* read all the available output packets (in general there may be any
|
||||
* number of them */
|
||||
while (ret >= 0)
|
||||
{
|
||||
ret = avcodec_receive_packet(m_pCodecCtx, m_pPkt);
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "error encoding audio frame\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (m_pPkt->data && m_pPkt->size > 0)
|
||||
{
|
||||
procData(m_pPkt->data, m_pPkt->size, pFrame ? pFrame->nb_samples : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_print(HT_LOG_WARN, "%s, data is null\r\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
av_packet_unref(m_pPkt);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::encode(uint8 * data, int size)
|
||||
{
|
||||
if (!m_bInited)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pFrame->data[0] = data;
|
||||
m_pFrame->linesize[0] = size;
|
||||
m_pFrame->nb_samples = size / (m_EncoderParams.SrcChannels * av_get_bytes_per_sample(m_EncoderParams.SrcSamplefmt));
|
||||
m_pFrame->format = m_EncoderParams.SrcSamplefmt;
|
||||
m_pFrame->key_frame = 1;
|
||||
m_pFrame->sample_rate = m_EncoderParams.SrcSamplerate;
|
||||
m_pFrame->channels = m_EncoderParams.SrcChannels;
|
||||
m_pFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.SrcChannels);
|
||||
|
||||
return encode(m_pFrame);
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::encode(AVFrame * pFrame)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
|
||||
if (!m_bInited)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (m_pSwrCtx)
|
||||
{
|
||||
if (NULL == m_pResampleFrame)
|
||||
{
|
||||
m_pResampleFrame = av_frame_alloc();
|
||||
if (NULL == m_pResampleFrame)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
m_pResampleFrame->sample_rate = m_EncoderParams.DstSamplerate;
|
||||
m_pResampleFrame->format = m_EncoderParams.DstSamplefmt;
|
||||
m_pResampleFrame->channels = m_EncoderParams.DstChannels;
|
||||
m_pResampleFrame->channel_layout = av_get_default_channel_layout(m_EncoderParams.DstChannels);
|
||||
|
||||
int swrret = swr_convert_frame(m_pSwrCtx, m_pResampleFrame, pFrame);
|
||||
if (swrret == 0)
|
||||
{
|
||||
ret = bufferFrame(m_pResampleFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = FALSE;
|
||||
}
|
||||
|
||||
av_frame_unref(m_pResampleFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = bufferFrame(pFrame);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CAudioEncoder::procData(uint8 * data, int size, int nbsamples)
|
||||
{
|
||||
AudioEncoderCB * p_cb = NULL;
|
||||
LINKED_NODE * p_node = NULL;
|
||||
|
||||
sys_os_mutex_enter(m_pCallbackMutex);
|
||||
|
||||
p_node = h_list_lookup_start(m_pCallbackList);
|
||||
while (p_node)
|
||||
{
|
||||
p_cb = (AudioEncoderCB *) p_node->p_data;
|
||||
if (p_cb->pCallback != NULL)
|
||||
{
|
||||
p_cb->pCallback(data, size, nbsamples, p_cb->pUserdata);
|
||||
}
|
||||
|
||||
p_node = h_list_lookup_next(m_pCallbackList, p_node);
|
||||
}
|
||||
h_list_lookup_end(m_pCallbackList);
|
||||
|
||||
sys_os_mutex_leave(m_pCallbackMutex);
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::isCallbackExist(AudioDataCallback pCallback, void *pUserdata)
|
||||
{
|
||||
BOOL exist = FALSE;
|
||||
AudioEncoderCB * p_cb = NULL;
|
||||
LINKED_NODE * p_node = NULL;
|
||||
|
||||
sys_os_mutex_enter(m_pCallbackMutex);
|
||||
|
||||
p_node = h_list_lookup_start(m_pCallbackList);
|
||||
while (p_node)
|
||||
{
|
||||
p_cb = (AudioEncoderCB *) p_node->p_data;
|
||||
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
|
||||
{
|
||||
exist = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
p_node = h_list_lookup_next(m_pCallbackList, p_node);
|
||||
}
|
||||
h_list_lookup_end(m_pCallbackList);
|
||||
|
||||
sys_os_mutex_leave(m_pCallbackMutex);
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
void CAudioEncoder::addCallback(AudioDataCallback pCallback, void *pUserdata)
|
||||
{
|
||||
if (isCallbackExist(pCallback, pUserdata))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AudioEncoderCB * p_cb = (AudioEncoderCB *) malloc(sizeof(AudioEncoderCB));
|
||||
if (NULL == p_cb)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p_cb->pCallback = pCallback;
|
||||
p_cb->pUserdata = pUserdata;
|
||||
p_cb->bFirst = TRUE;
|
||||
|
||||
sys_os_mutex_enter(m_pCallbackMutex);
|
||||
h_list_add_at_back(m_pCallbackList, p_cb);
|
||||
sys_os_mutex_leave(m_pCallbackMutex);
|
||||
}
|
||||
|
||||
void CAudioEncoder::delCallback(AudioDataCallback pCallback, void *pUserdata)
|
||||
{
|
||||
AudioEncoderCB * p_cb = NULL;
|
||||
LINKED_NODE * p_node = NULL;
|
||||
|
||||
sys_os_mutex_enter(m_pCallbackMutex);
|
||||
|
||||
p_node = h_list_lookup_start(m_pCallbackList);
|
||||
while (p_node)
|
||||
{
|
||||
p_cb = (AudioEncoderCB *) p_node->p_data;
|
||||
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
|
||||
{
|
||||
free(p_cb);
|
||||
|
||||
h_list_remove(m_pCallbackList, p_node);
|
||||
break;
|
||||
}
|
||||
|
||||
p_node = h_list_lookup_next(m_pCallbackList, p_node);
|
||||
}
|
||||
h_list_lookup_end(m_pCallbackList);
|
||||
|
||||
sys_os_mutex_leave(m_pCallbackMutex);
|
||||
}
|
||||
|
||||
char * CAudioEncoder::getAACAuxSDPLine(int rtp_pt)
|
||||
{
|
||||
if (NULL == m_pCodecCtx || m_pCodecCtx->extradata_size == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char const* fmtpFmt =
|
||||
"a=fmtp:%d "
|
||||
"streamtype=5;profile-level-id=1;"
|
||||
"mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;"
|
||||
"config=";
|
||||
uint32 fmtpFmtSize = strlen(fmtpFmt)
|
||||
+ 3 /* max char len */
|
||||
+ 2*m_pCodecCtx->extradata_size; /* 2*, because each byte prints as 2 chars */
|
||||
|
||||
char* fmtp = new char[fmtpFmtSize+1];
|
||||
memset(fmtp, 0, fmtpFmtSize+1);
|
||||
|
||||
sprintf(fmtp, fmtpFmt, rtp_pt);
|
||||
char* endPtr = &fmtp[strlen(fmtp)];
|
||||
for (int i = 0; i < m_pCodecCtx->extradata_size; ++i)
|
||||
{
|
||||
sprintf(endPtr, "%02X", m_pCodecCtx->extradata[i]);
|
||||
endPtr += 2;
|
||||
}
|
||||
|
||||
return fmtp;
|
||||
}
|
||||
|
||||
char * CAudioEncoder::getAuxSDPLine(int rtp_pt)
|
||||
{
|
||||
if (m_nCodecId == AV_CODEC_ID_AAC)
|
||||
{
|
||||
return getAACAuxSDPLine(rtp_pt);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL CAudioEncoder::getExtraData(uint8 ** extradata, int * extralen)
|
||||
{
|
||||
if (m_pCodecCtx && m_pCodecCtx->extradata_size > 0)
|
||||
{
|
||||
*extradata = m_pCodecCtx->extradata;
|
||||
*extralen = m_pCodecCtx->extradata_size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
111
MediaClient/MediaClientForMobile/media/audio_encoder.h
Normal file
111
MediaClient/MediaClientForMobile/media/audio_encoder.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _AUDIO_ENCODER_H
|
||||
#define _AUDIO_ENCODER_H
|
||||
|
||||
#include "linked_list.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
typedef void (*AudioDataCallback)(uint8 *data, int size, int nbsamples, void *pUserdata);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioDataCallback pCallback; // callback function pointer
|
||||
void * pUserdata; // user data
|
||||
BOOL bFirst; //
|
||||
} AudioEncoderCB;
|
||||
|
||||
/**
|
||||
* Some encoders require a fixed frame size
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int tlen; // buffer total length
|
||||
uint8 * data[2]; // audio data buffer
|
||||
int size[2]; // audio data size
|
||||
int samples; // audio sample numbers
|
||||
} AudioBuffer;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int SrcSamplerate; // source sample rate
|
||||
int SrcChannels; // source channels
|
||||
AVSampleFormat SrcSamplefmt; // source sample format, see AVSampleFormat
|
||||
|
||||
int DstCodec; // output codec
|
||||
int DstSamplerate; // output sample rate
|
||||
int DstChannels; // output channels
|
||||
int DstBitrate; // output bitrate, unit is bits per second
|
||||
AVSampleFormat DstSamplefmt; // output sample format, see AVSampleFormat
|
||||
} AudioEncoderParam;
|
||||
|
||||
class CAudioEncoder
|
||||
{
|
||||
public:
|
||||
CAudioEncoder();
|
||||
~CAudioEncoder();
|
||||
|
||||
BOOL init(AudioEncoderParam * params);
|
||||
void uninit();
|
||||
BOOL encode(uint8 * data, int size);
|
||||
BOOL encode(AVFrame * pFrame);
|
||||
void addCallback(AudioDataCallback pCallback, void *pUserdata);
|
||||
void delCallback(AudioDataCallback pCallback, void *pUserdata);
|
||||
char * getAuxSDPLine(int rtp_pt);
|
||||
BOOL getExtraData(uint8 ** extradata, int * extralen);
|
||||
|
||||
private:
|
||||
void flush();
|
||||
BOOL encodeInternal(AVFrame * pFrame);
|
||||
void procData(uint8 * data, int size, int nbsamples);
|
||||
BOOL isCallbackExist(AudioDataCallback pCallback, void *pUserdata);
|
||||
char * getAACAuxSDPLine(int rtp_pt);
|
||||
BOOL bufferFrame(AVFrame * pFrame);
|
||||
int computeBitrate(AVCodecID codec, int samplerate, int channels, int quality);
|
||||
|
||||
private:
|
||||
AVCodecID m_nCodecId;
|
||||
BOOL m_bInited;
|
||||
|
||||
AudioEncoderParam m_EncoderParams;
|
||||
|
||||
AVCodecContext * m_pCodecCtx;
|
||||
AVFrame * m_pFrame;
|
||||
AVFrame * m_pResampleFrame;
|
||||
SwrContext * m_pSwrCtx;
|
||||
AudioBuffer m_AudioBuffer;
|
||||
AVPacket * m_pPkt;
|
||||
|
||||
void * m_pCallbackMutex;
|
||||
LINKED_LIST * m_pCallbackList;
|
||||
};
|
||||
|
||||
#endif // _AUDIO_ENCODER_H
|
||||
|
||||
|
||||
|
||||
68
MediaClient/MediaClientForMobile/media/audio_play.cpp
Normal file
68
MediaClient/MediaClientForMobile/media/audio_play.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "audio_play.h"
|
||||
|
||||
|
||||
CAudioPlay::CAudioPlay()
|
||||
{
|
||||
m_bInited = FALSE;
|
||||
m_nSamplerate = 8000;
|
||||
m_nChannels = 1;
|
||||
}
|
||||
|
||||
CAudioPlay::~CAudioPlay(void)
|
||||
{
|
||||
}
|
||||
|
||||
BOOL CAudioPlay::startPlay(int samplerate, int channels)
|
||||
{
|
||||
m_nSamplerate = samplerate;
|
||||
m_nChannels = channels;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CAudioPlay::stopPlay(void)
|
||||
{
|
||||
m_bInited = FALSE;
|
||||
}
|
||||
|
||||
BOOL CAudioPlay::setVolume(int volume)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int CAudioPlay::getVolume()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CAudioPlay::playAudio(uint8 * data, int size)
|
||||
{
|
||||
if (!m_bInited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
53
MediaClient/MediaClientForMobile/media/audio_play.h
Normal file
53
MediaClient/MediaClientForMobile/media/audio_play.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _AUDIO_PLAY_H
|
||||
#define _AUDIO_PLAY_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "media_format.h"
|
||||
|
||||
|
||||
#define HTVOLUME_MIN 0
|
||||
#define HTVOLUME_MAX 255
|
||||
|
||||
|
||||
class CAudioPlay
|
||||
{
|
||||
public:
|
||||
CAudioPlay();
|
||||
virtual ~CAudioPlay();
|
||||
|
||||
public:
|
||||
virtual BOOL startPlay(int samplerate, int channels);
|
||||
virtual void stopPlay();
|
||||
virtual BOOL setVolume(int volume);
|
||||
virtual int getVolume();
|
||||
virtual void playAudio(uint8 * pData, int len);
|
||||
|
||||
protected:
|
||||
BOOL m_bInited;
|
||||
int m_nSamplerate;
|
||||
int m_nChannels;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
157
MediaClient/MediaClientForMobile/media/audio_play_qt.cpp
Normal file
157
MediaClient/MediaClientForMobile/media/audio_play_qt.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "audio_play_qt.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QMediaDevices>
|
||||
|
||||
|
||||
CQAudioPlay::CQAudioPlay(QObject * parent) : QObject(parent), CAudioPlay()
|
||||
, m_pSink(NULL)
|
||||
, m_pBuffer(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CQAudioPlay::~CQAudioPlay()
|
||||
{
|
||||
stopPlay();
|
||||
}
|
||||
|
||||
BOOL CQAudioPlay::startPlay(int samplerate, int channels)
|
||||
{
|
||||
QAudioFormat format;
|
||||
|
||||
format.setSampleRate(samplerate);
|
||||
format.setChannelCount(channels);
|
||||
format.setSampleFormat(QAudioFormat::Int16);
|
||||
|
||||
QAudioDevice device(QMediaDevices::defaultAudioOutput());
|
||||
if (!device.isFormatSupported(format))
|
||||
{
|
||||
log_print(HT_LOG_ERR, "Raw audio format not supported by backend, cannot play audio\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_nSamplerate = samplerate;
|
||||
m_nChannels = channels;
|
||||
|
||||
m_pSink = new QAudioSink(device, format, NULL);
|
||||
|
||||
m_pSink->setBufferSize(8192);
|
||||
m_pBuffer = m_pSink->start();
|
||||
if (NULL == m_pBuffer)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "%s, audio output start failed\r\n", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_bInited = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CQAudioPlay::stopPlay()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_pSink)
|
||||
{
|
||||
m_pSink->stop();
|
||||
delete m_pSink;
|
||||
|
||||
m_pSink = NULL;
|
||||
}
|
||||
|
||||
m_pBuffer = NULL;
|
||||
|
||||
m_bInited = FALSE;
|
||||
}
|
||||
|
||||
void CQAudioPlay::playAudio(uint8 * data, int size)
|
||||
{
|
||||
if (!m_bInited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
int rlen = m_pBuffer->write((char *)data, size);
|
||||
while (rlen < size)
|
||||
{
|
||||
if (rlen < 0)
|
||||
{
|
||||
break; // error
|
||||
}
|
||||
else
|
||||
{
|
||||
size -= rlen;
|
||||
data += rlen;
|
||||
}
|
||||
|
||||
rlen = m_pBuffer->write((char *)data, size);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CQAudioPlay::setVolume(int volume)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_pSink)
|
||||
{
|
||||
double db = (double) volume / (HTVOLUME_MAX - HTVOLUME_MIN);
|
||||
if (db < 0)
|
||||
{
|
||||
db = 0.0;
|
||||
}
|
||||
else if (db > 1.0)
|
||||
{
|
||||
db = 1.0;
|
||||
}
|
||||
|
||||
log_print(HT_LOG_DBG, "%s, volume=%d, db=%f\r\n", __FUNCTION__, volume, db);
|
||||
|
||||
m_pSink->setVolume(db);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int CQAudioPlay::getVolume()
|
||||
{
|
||||
double volume = HTVOLUME_MIN;
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_pSink)
|
||||
{
|
||||
volume = m_pSink->volume();
|
||||
}
|
||||
|
||||
int nv = (HTVOLUME_MAX - HTVOLUME_MIN) * volume + HTVOLUME_MIN;
|
||||
|
||||
return nv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
MediaClient/MediaClientForMobile/media/audio_play_qt.h
Normal file
56
MediaClient/MediaClientForMobile/media/audio_play_qt.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef AUDIO_PLAY_QT_H
|
||||
#define AUDIO_PLAY_QT_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "linked_list.h"
|
||||
#include "media_format.h"
|
||||
#include "audio_play.h"
|
||||
#include <QObject>
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioSink>
|
||||
#include <QBuffer>
|
||||
#include <QMutex>
|
||||
|
||||
|
||||
class CQAudioPlay : public QObject, public CAudioPlay
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CQAudioPlay(QObject * parent = NULL);
|
||||
~CQAudioPlay();
|
||||
|
||||
BOOL startPlay(int samplerate, int channels);
|
||||
void stopPlay();
|
||||
BOOL setVolume(int volume);
|
||||
int getVolume();
|
||||
void playAudio(uint8 * pData, int len);
|
||||
|
||||
private:
|
||||
QAudioSink * m_pSink;
|
||||
QIODevice * m_pBuffer;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
48
MediaClient/MediaClientForMobile/media/avcodec_mutex.cpp
Normal file
48
MediaClient/MediaClientForMobile/media/avcodec_mutex.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "avcodec_mutex.h"
|
||||
|
||||
void * g_avcodec_mutex = sys_os_create_mutex();
|
||||
|
||||
int avcodec_thread_open(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sys_os_mutex_enter(g_avcodec_mutex);
|
||||
ret = avcodec_open2(avctx, codec, options);
|
||||
sys_os_mutex_leave(g_avcodec_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int avcodec_thread_close(AVCodecContext *avctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sys_os_mutex_enter(g_avcodec_mutex);
|
||||
ret = avcodec_close(avctx);
|
||||
sys_os_mutex_leave(g_avcodec_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
41
MediaClient/MediaClientForMobile/media/avcodec_mutex.h
Normal file
41
MediaClient/MediaClientForMobile/media/avcodec_mutex.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef AVCODEC_MUTEX_H
|
||||
#define AVCODEC_MUTEX_H
|
||||
|
||||
extern "C" {
|
||||
#include "libavcodec/avcodec.h"
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int avcodec_thread_open(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
|
||||
int avcodec_thread_close(AVCodecContext *avctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
188
MediaClient/MediaClientForMobile/media/avi.h
Normal file
188
MediaClient/MediaClientForMobile/media/avi.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef AVI_H
|
||||
#define AVI_H
|
||||
|
||||
#include "format.h"
|
||||
|
||||
/* Flags in avih */
|
||||
#define AVIF_HASINDEX 0x00000010 // Index at end of file?
|
||||
#define AVIF_ISINTERLEAVED 0x00000100 // is interleaved?
|
||||
#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames?
|
||||
|
||||
#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame.
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct avi_riff
|
||||
{
|
||||
uint32 riff;
|
||||
uint32 len;
|
||||
uint32 type;
|
||||
} AVIRIFF;
|
||||
|
||||
typedef struct avi_pkt
|
||||
{
|
||||
uint32 type; // packet type : PACKET_TYPE_UNKNOW, PACKET_TYPE_VIDEO,PACKET_TYPE_AUDIO
|
||||
uint8 * rbuf; // buffer pointer
|
||||
uint32 mlen; // buffer size
|
||||
uint8 * dbuf; // data pointer
|
||||
uint32 len; // packet length
|
||||
} AVIPKT;
|
||||
|
||||
typedef struct avi_main_header
|
||||
{
|
||||
uint32 dwMicroSecPerFrame; // render time for per frame, unit is ns
|
||||
uint32 dwMaxBytesPerSec; // maximum data transfer rate
|
||||
uint32 dwPaddingGranularity; // The length of the record block needs to be a multiple of this value, usually 2048
|
||||
uint32 dwFlags; // Special attributes of AVI files, such as whether to include index blocks, whether audio and video data is interleaved
|
||||
uint32 dwTotalFrames; // The total number of frames in the file
|
||||
uint32 dwInitialFrames; // How much frames is needed before starting to play
|
||||
uint32 dwStreams; // The number of data streams contained in the file
|
||||
uint32 dwSuggestedBufferSize; // The recommended size of the buffer, usually the sum of the data needed to store an image and synchronize the sound
|
||||
uint32 dwWidth; // Image width
|
||||
uint32 dwHeight; // Image height
|
||||
uint32 dwReserved[4]; // Reserved
|
||||
} AVIMHDR;
|
||||
|
||||
typedef struct avi_stream_header
|
||||
{
|
||||
uint32 fccType; // 4 bytes, indicating the type of data stream, vids for video data stream, auds audio data stream
|
||||
uint32 fccHandler; // 4 bytes, indicating the driver code for data stream decompression
|
||||
uint32 dwFlags; // Data stream attribute
|
||||
uint16 wPriority; // Play priority of this stream
|
||||
uint16 wLanguage; // Audio language code
|
||||
uint32 dwInitialFrames; // How much frames is needed before starting to play
|
||||
uint32 dwScale; // The amount of data, the size of each video or the sample size of the audio
|
||||
uint32 dwRate; // dwScale / dwRate = Number of samples per second
|
||||
uint32 dwStart; // The location where the data stream starts playing, in dwScale
|
||||
uint32 dwLength; // The amount of data in the data stream, in dwScale
|
||||
uint32 dwSuggestedBufferSize; // Recommended buffer size
|
||||
uint32 dwQuality; // Decompress quality parameters, the larger the value, the better the quality
|
||||
uint32 dwSampleSize; // Sample size of the audio
|
||||
|
||||
struct
|
||||
{
|
||||
short left;
|
||||
short top;
|
||||
short right;
|
||||
short bottom;
|
||||
} rcFrame;
|
||||
} AVISHDR;
|
||||
|
||||
typedef struct bitmap_info_header
|
||||
{
|
||||
uint32 biSize;
|
||||
int biWidth;
|
||||
int biHeight;
|
||||
uint16 biPlanes;
|
||||
uint16 biBitCount;
|
||||
uint32 biCompression;
|
||||
uint32 biSizeImage;
|
||||
int biXPelsPerMeter;
|
||||
int biYPelsPerMeter;
|
||||
uint32 biClrUsed;
|
||||
uint32 biClrImportant;
|
||||
} BMPHDR;
|
||||
|
||||
typedef struct wave_format
|
||||
{
|
||||
uint16 wFormatTag; // format type
|
||||
uint16 nChannels; // number of channels (i.e. mono, stereo...)
|
||||
uint32 nSamplesPerSec; // sample rate
|
||||
uint32 nAvgBytesPerSec; // for buffer estimation
|
||||
uint16 nBlockAlign; // block size of data
|
||||
uint16 wBitsPerSample; // number of bits per sample of mono data
|
||||
uint16 cbSize; // the count in bytes of the size of
|
||||
|
||||
// extra information (after cbSize)
|
||||
} WAVEFMT;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct avi_file_context
|
||||
{
|
||||
uint32 ctxf_read : 1; // Read mode
|
||||
uint32 ctxf_write : 1; // Write mode
|
||||
uint32 ctxf_idx : 1; // File has index
|
||||
uint32 ctxf_audio : 1; // Has audio stream
|
||||
uint32 ctxf_video : 1; // Has video stream
|
||||
uint32 ctxf_idx_m : 1; // Index data write mode: = 1, memory mode; = 0, temporary file
|
||||
uint32 ctxf_calcfps: 1; // Calc video framerate
|
||||
uint32 ctxf_nalu : 1; // For H.26x, VPS, SPS,PPS written flag
|
||||
uint32 ctxf_iframe : 1; // For H.26x, key frame written flag
|
||||
uint32 ctxf_res : 23;
|
||||
|
||||
AVIMHDR avi_hdr; // AVI main header
|
||||
AVISHDR str_v; // Video stream header
|
||||
BMPHDR bmp; // BITMAP format
|
||||
AVISHDR str_a; // Audio stream header
|
||||
WAVEFMT wave; // WAVE format
|
||||
|
||||
FILE * fp; // Read and write file handle
|
||||
uint32 flen; // Total file length, used when reading
|
||||
char filename[256]; // File full path
|
||||
void * mutex; // Write, close mutex
|
||||
|
||||
uint32 v_fps; // Video frame rate
|
||||
char v_fcc[4]; // Video compression standard, "H264","H265","JPEG","MP4V"
|
||||
int v_width; // Video width
|
||||
int v_height; // Video height
|
||||
uint8 * v_extra; // Video extra data
|
||||
int v_extra_len; // Video extra data length
|
||||
|
||||
int a_rate; // Sampling frequency
|
||||
uint16 a_fmt; // Audio compression standard
|
||||
int a_chns; // Number of audio channels
|
||||
uint8 * a_extra; // Audio extra data
|
||||
int a_extra_len; // Audio extra data length
|
||||
|
||||
int i_movi; // Where the real data starts
|
||||
int i_movi_end; // End of data, where the index starts
|
||||
int i_riff; // After the index, the length of the entire file
|
||||
|
||||
int pkt_offset; // Packet offset when reading
|
||||
int index_offset; // Index position when reading
|
||||
int back_index; // The first read index position when reading in reverse order, usually sps/vps
|
||||
|
||||
int i_frame_video; // Video frame read and write count
|
||||
int i_frame_audio; // Audio frame read and write count
|
||||
uint32 audio_total_bytes; // Audio total bytes
|
||||
|
||||
uint32 prev_ts; // previous timestamp
|
||||
uint32 v_s_time; // Video start recording time = first packet write time
|
||||
uint32 v_e_time; // The time when video package was recently written
|
||||
uint32 a_s_time; // Audio start recording time = first packet write time
|
||||
uint32 a_e_time; // The time when audio package was recently written
|
||||
|
||||
int i_idx_max; // The maximum number of indexes currently allocated
|
||||
int i_idx; // Current index number (video index + audio index)
|
||||
int * idx; // Index array
|
||||
|
||||
FILE * idx_fp; // Index temporary file
|
||||
int idx_fix[128]; // Index file data is enough to write once for one sector
|
||||
int idx_fix_off; // The index data has been stored in the offset of idx_fix
|
||||
} AVICTX;
|
||||
|
||||
#endif // AVI_H
|
||||
|
||||
|
||||
|
||||
1144
MediaClient/MediaClientForMobile/media/avi_write.cpp
Normal file
1144
MediaClient/MediaClientForMobile/media/avi_write.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
MediaClient/MediaClientForMobile/media/avi_write.h
Normal file
61
MediaClient/MediaClientForMobile/media/avi_write.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef AVI_WRITE_H
|
||||
#define AVI_WRITE_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "avi.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void avi_free_idx(AVICTX * p_ctx);
|
||||
int avi_write_idx(AVICTX * p_ctx);
|
||||
void avi_set_dw(void * p, uint32 dw);
|
||||
int avi_end(AVICTX * p_ctx);
|
||||
AVICTX* avi_write_open(const char * filename);
|
||||
int avi_write_video(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts, int b_key);
|
||||
int avi_write_nalu(AVICTX * p_ctx, uint8 * vps, int vps_len, uint8 * sps, int sps_len, uint8 * pps, int pps_len);
|
||||
int avi_write_audio(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts);
|
||||
void avi_write_close(AVICTX * p_ctx);
|
||||
void avi_set_video_info(AVICTX * p_ctx, int fps, int width, int height, const char fcc[4]);
|
||||
void avi_set_video_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len);
|
||||
void avi_set_audio_info(AVICTX * p_ctx, int chns, int rate, uint16 fmt);
|
||||
void avi_set_audio_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len);
|
||||
void avi_build_video_hdr(AVICTX * p_ctx);
|
||||
void avi_build_audio_hdr(AVICTX * p_ctx);
|
||||
int avi_write_header(AVICTX * p_ctx);
|
||||
int avi_update_header(AVICTX * p_ctx);
|
||||
int avi_calc_fps(AVICTX * p_ctx);
|
||||
uint64 avi_get_file_length(AVICTX * p_ctx);
|
||||
uint32 avi_get_media_time(AVICTX * p_ctx);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // AVI_WRITE_H
|
||||
|
||||
|
||||
|
||||
831
MediaClient/MediaClientForMobile/media/file_player.cpp
Normal file
831
MediaClient/MediaClientForMobile/media/file_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
86
MediaClient/MediaClientForMobile/media/file_player.h
Normal file
86
MediaClient/MediaClientForMobile/media/file_player.h
Normal 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
|
||||
|
||||
|
||||
45
MediaClient/MediaClientForMobile/media/format.h
Normal file
45
MediaClient/MediaClientForMobile/media/format.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef FORMAT_H
|
||||
#define FORMAT_H
|
||||
|
||||
#define PACKET_TYPE_UNKNOW -1
|
||||
#define PACKET_TYPE_VIDEO 0
|
||||
#define PACKET_TYPE_AUDIO 1
|
||||
|
||||
#define AUDIO_FORMAT_PCM (0x0001)
|
||||
#define AUDIO_FORMAT_G726 (0x0064)
|
||||
#define AUDIO_FORMAT_G722 (0x028F)
|
||||
#define AUDIO_FORMAT_MP3 (0x0055)
|
||||
#define AUDIO_FORMAT_AAC (0x00FF)
|
||||
#define AUDIO_FORMAT_ALAW (0x0006)
|
||||
#define AUDIO_FORMAT_MULAW (0x0007)
|
||||
#define AUDIO_FORMAT_OPUS (0x00AD)
|
||||
|
||||
#ifndef MAKEFOURCC
|
||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
|
||||
((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) | \
|
||||
((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 ))
|
||||
#define mmioFOURCC(ch0, ch1, ch2, ch3) MAKEFOURCC(ch0, ch1, ch2, ch3)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
94
MediaClient/MediaClientForMobile/media/gles_engine.cpp
Normal file
94
MediaClient/MediaClientForMobile/media/gles_engine.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "gles_engine.h"
|
||||
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#define CheckError(message) if (result != SL_RESULT_SUCCESS) { log_print(HT_LOG_ERR, "%s\r\n", message); return; }
|
||||
|
||||
|
||||
static GlesEngine * g_glesEngine;
|
||||
|
||||
GlesEngine::GlesEngine()
|
||||
: m_engineObject(0)
|
||||
, m_engine(0)
|
||||
{
|
||||
SLresult result;
|
||||
|
||||
result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0);
|
||||
CheckError("Failed to create engine");
|
||||
|
||||
result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE);
|
||||
CheckError("Failed to realize engine");
|
||||
|
||||
result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine);
|
||||
CheckError("Failed to get engine interface");
|
||||
}
|
||||
|
||||
GlesEngine::~GlesEngine()
|
||||
{
|
||||
if (m_engineObject)
|
||||
{
|
||||
(*m_engineObject)->Destroy(m_engineObject);
|
||||
}
|
||||
}
|
||||
|
||||
GlesEngine *GlesEngine::instance()
|
||||
{
|
||||
if (NULL == g_glesEngine)
|
||||
{
|
||||
g_glesEngine = new GlesEngine();
|
||||
}
|
||||
|
||||
return g_glesEngine;
|
||||
}
|
||||
|
||||
bool GlesEngine::inputFormatIsSupported(SLDataFormat_PCM format)
|
||||
{
|
||||
SLresult result;
|
||||
SLObjectItf recorder = 0;
|
||||
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
|
||||
SLDataSource audioSrc = { &loc_dev, NULL };
|
||||
|
||||
#ifdef ANDROID
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
|
||||
#else
|
||||
SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 };
|
||||
#endif
|
||||
SLDataSink audioSnk = { &loc_bq, &format };
|
||||
|
||||
result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0);
|
||||
if (result == SL_RESULT_SUCCESS)
|
||||
{
|
||||
result = (*recorder)->Realize(recorder, false);
|
||||
}
|
||||
|
||||
if (result == SL_RESULT_SUCCESS)
|
||||
{
|
||||
(*recorder)->Destroy(recorder);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
43
MediaClient/MediaClientForMobile/media/gles_engine.h
Normal file
43
MediaClient/MediaClientForMobile/media/gles_engine.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _GLES_ENGINE_H_
|
||||
#define _GLES_ENGINE_H_
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
|
||||
class GlesEngine
|
||||
{
|
||||
public:
|
||||
GlesEngine();
|
||||
~GlesEngine();
|
||||
|
||||
static GlesEngine *instance();
|
||||
|
||||
SLEngineItf slEngine() const { return m_engine; }
|
||||
|
||||
bool inputFormatIsSupported(SLDataFormat_PCM format);
|
||||
|
||||
SLObjectItf m_engineObject;
|
||||
SLEngineItf m_engine;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
325
MediaClient/MediaClientForMobile/media/gles_input.cpp
Normal file
325
MediaClient/MediaClientForMobile/media/gles_input.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "gles_input.h"
|
||||
#include "gles_engine.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
|
||||
#else
|
||||
static void bufferQueueCallback(SLBufferQueueItf, void *context)
|
||||
#endif
|
||||
{
|
||||
GlesInput *audioInput = reinterpret_cast<GlesInput*>(context);
|
||||
|
||||
audioInput->processBuffer();
|
||||
}
|
||||
|
||||
GlesInput::GlesInput()
|
||||
: m_recorderObject(0)
|
||||
, m_recorder(0)
|
||||
, m_bufferQueue(0)
|
||||
, m_bufferSize(1600)
|
||||
, m_currentBuffer(0)
|
||||
, m_pCallback(NULL)
|
||||
, m_pUserdata(NULL)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < NUM_BUFFERS; i++)
|
||||
{
|
||||
m_buffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GlesInput::~GlesInput()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool GlesInput::start(int samplerete)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (startRecording(samplerete))
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_print(HT_LOG_ERR, "start audio recoding faild\r\n");
|
||||
|
||||
stopRecording();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GlesInput::stop()
|
||||
{
|
||||
stopRecording();
|
||||
}
|
||||
|
||||
bool GlesInput::startRecording(int samplerete)
|
||||
{
|
||||
SLEngineItf engine = GlesEngine::instance()->slEngine();
|
||||
if (!engine)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "No engine\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
SLresult result;
|
||||
|
||||
// configure audio source
|
||||
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
|
||||
SLDataSource audioSrc = { &loc_dev, NULL };
|
||||
|
||||
// configure audio sink
|
||||
#ifdef ANDROID
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
NUM_BUFFERS };
|
||||
#else
|
||||
SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
|
||||
#endif
|
||||
|
||||
SLDataFormat_PCM format_pcm;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = 2;
|
||||
format_pcm.samplesPerSec = samplerete * 1000;
|
||||
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format_pcm.channelMask = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
|
||||
SLDataSink audioSnk = { &loc_bq, &format_pcm };
|
||||
|
||||
// create audio recorder
|
||||
// (requires the RECORD_AUDIO permission)
|
||||
#ifdef ANDROID
|
||||
const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
#else
|
||||
const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
|
||||
#endif
|
||||
|
||||
result = (*engine)->CreateAudioRecorder(engine, &m_recorderObject,
|
||||
&audioSrc, &audioSnk,
|
||||
sizeof(req) / sizeof(SLboolean), id, req);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "create audio recorder failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
// configure recorder source
|
||||
SLAndroidConfigurationItf configItf;
|
||||
result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION,
|
||||
&configItf);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "get audio configuration interface failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&m_recorderPreset, sizeof(SLuint32));
|
||||
|
||||
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE;
|
||||
SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big
|
||||
result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&presetSize, (void*)&presetValue);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "set audio record configuration failed\r\n");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// realize the audio recorder
|
||||
result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "realize audio record object failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the record interface
|
||||
result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "get audio record object failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the buffer queue interface
|
||||
#ifdef ANDROID
|
||||
SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
#else
|
||||
SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE;
|
||||
#endif
|
||||
result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "get audio record buff queue failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// register callback on the buffer queue
|
||||
result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, ::bufferQueueCallback, this);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "set buffer queue callback failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bufferSize <= 0)
|
||||
{
|
||||
m_bufferSize = (16*2/8)*50*samplerete/1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
int minimumBufSize = (16*2/8)*5*samplerete/1000;
|
||||
if (m_bufferSize < minimumBufSize)
|
||||
m_bufferSize = minimumBufSize;
|
||||
}
|
||||
|
||||
// enqueue empty buffers to be filled by the recorder
|
||||
for (int i = 0; i < NUM_BUFFERS; ++i)
|
||||
{
|
||||
m_buffers[i] = (uint8 *)malloc(m_bufferSize);
|
||||
if (NULL == m_buffers[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(m_buffers[i], 0, m_bufferSize);
|
||||
|
||||
result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i], m_bufferSize);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "audio record enqueue failed\r\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// start recording
|
||||
result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "set audio record state failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlesInput::stopRecording()
|
||||
{
|
||||
if (m_recorder)
|
||||
{
|
||||
(*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED);
|
||||
}
|
||||
|
||||
if (m_bufferQueue)
|
||||
{
|
||||
(*m_bufferQueue)->Clear(m_bufferQueue);
|
||||
}
|
||||
|
||||
if (m_recorderObject)
|
||||
{
|
||||
(*m_recorderObject)->Destroy(m_recorderObject);
|
||||
}
|
||||
|
||||
m_recorder = NULL;
|
||||
m_bufferQueue = NULL;
|
||||
m_recorderObject = NULL;
|
||||
|
||||
for (int i = 0; i < NUM_BUFFERS; ++i)
|
||||
{
|
||||
if (m_buffers[i])
|
||||
{
|
||||
free(m_buffers[i]);
|
||||
m_buffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_currentBuffer = 0;
|
||||
}
|
||||
|
||||
void GlesInput::setBufferSize(int value)
|
||||
{
|
||||
m_bufferSize = value;
|
||||
}
|
||||
|
||||
int GlesInput::bufferSize() const
|
||||
{
|
||||
return m_bufferSize;
|
||||
}
|
||||
|
||||
void GlesInput::processBuffer()
|
||||
{
|
||||
uint8 *processedBuffer = m_buffers[m_currentBuffer];
|
||||
|
||||
if (m_pCallback)
|
||||
{
|
||||
m_pCallback(processedBuffer, m_bufferSize, m_pUserdata);
|
||||
}
|
||||
|
||||
// Re-enqueue the buffer
|
||||
SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
|
||||
processedBuffer,
|
||||
m_bufferSize);
|
||||
|
||||
m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS;
|
||||
|
||||
// If the buffer queue is empty (shouldn't happen), stop recording.
|
||||
#ifdef ANDROID
|
||||
SLAndroidSimpleBufferQueueState state;
|
||||
#else
|
||||
SLBufferQueueState state;
|
||||
#endif
|
||||
result = (*m_bufferQueue)->GetState(m_bufferQueue, &state);
|
||||
if (result != SL_RESULT_SUCCESS || state.count == 0)
|
||||
{
|
||||
log_print(HT_LOG_ERR, "processBuffer::state.count == 0\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GlesInput::setCallback(GlesInputCB cb, void * puser)
|
||||
{
|
||||
m_pCallback = cb;
|
||||
m_pUserdata = puser;
|
||||
}
|
||||
|
||||
|
||||
|
||||
72
MediaClient/MediaClientForMobile/media/gles_input.h
Normal file
72
MediaClient/MediaClientForMobile/media/gles_input.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _GLES_INPUT_H_
|
||||
#define _GLES_INPUT_H_
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include <SLES/OpenSLES.h>
|
||||
#ifdef ANDROID
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#endif
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
typedef void (*GlesInputCB)(uint8 * pdata, int len, void * puser);
|
||||
|
||||
|
||||
class GlesInput
|
||||
{
|
||||
public:
|
||||
GlesInput();
|
||||
~GlesInput();
|
||||
|
||||
bool start(int samplerete);
|
||||
void stop();
|
||||
void setCallback(GlesInputCB cb, void * puser);
|
||||
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void processBuffer();
|
||||
|
||||
private:
|
||||
bool startRecording(int samplerete);
|
||||
void stopRecording();
|
||||
|
||||
|
||||
private:
|
||||
SLObjectItf m_recorderObject;
|
||||
SLRecordItf m_recorder;
|
||||
#ifdef ANDROID
|
||||
SLuint32 m_recorderPreset;
|
||||
SLAndroidSimpleBufferQueueItf m_bufferQueue;
|
||||
#else
|
||||
SLBufferQueueItf m_bufferQueue;
|
||||
#endif
|
||||
|
||||
int m_bufferSize;
|
||||
uint8 * m_buffers[NUM_BUFFERS];
|
||||
int m_currentBuffer;
|
||||
|
||||
GlesInputCB m_pCallback;
|
||||
void * m_pUserdata;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
434
MediaClient/MediaClientForMobile/media/http_flv_player.cpp
Normal file
434
MediaClient/MediaClientForMobile/media/http_flv_player.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
60
MediaClient/MediaClientForMobile/media/http_flv_player.h
Normal file
60
MediaClient/MediaClientForMobile/media/http_flv_player.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#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
|
||||
|
||||
|
||||
|
||||
183
MediaClient/MediaClientForMobile/media/http_mjpeg_player.cpp
Normal file
183
MediaClient/MediaClientForMobile/media/http_mjpeg_player.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
56
MediaClient/MediaClientForMobile/media/http_mjpeg_player.h
Normal file
56
MediaClient/MediaClientForMobile/media/http_mjpeg_player.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef 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
|
||||
|
||||
|
||||
|
||||
37
MediaClient/MediaClientForMobile/media/lock.h
Normal file
37
MediaClient/MediaClientForMobile/media/lock.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _LOCK_H
|
||||
#define _LOCK_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
|
||||
class CLock
|
||||
{
|
||||
public:
|
||||
CLock(void * pMutex) : m_pMutex(pMutex) {sys_os_mutex_enter(m_pMutex);}
|
||||
~CLock() {sys_os_mutex_leave(m_pMutex);}
|
||||
|
||||
private:
|
||||
void * m_pMutex;
|
||||
};
|
||||
|
||||
#endif // _LOCK_H
|
||||
|
||||
|
||||
215
MediaClient/MediaClientForMobile/media/media_codec.cpp
Normal file
215
MediaClient/MediaClientForMobile/media/media_codec.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "media_codec.h"
|
||||
#include "media_format.h"
|
||||
|
||||
|
||||
AVCodecID to_audio_avcodecid(int codec)
|
||||
{
|
||||
AVCodecID codecid = AV_CODEC_ID_NONE;
|
||||
|
||||
switch (codec)
|
||||
{
|
||||
case AUDIO_CODEC_G711A:
|
||||
codecid = AV_CODEC_ID_PCM_ALAW;
|
||||
break;
|
||||
|
||||
case AUDIO_CODEC_G711U:
|
||||
codecid = AV_CODEC_ID_PCM_MULAW;
|
||||
break;
|
||||
|
||||
case AUDIO_CODEC_G726:
|
||||
codecid = AV_CODEC_ID_ADPCM_G726;
|
||||
break;
|
||||
|
||||
case AUDIO_CODEC_G722:
|
||||
codecid = AV_CODEC_ID_ADPCM_G722;
|
||||
break;
|
||||
|
||||
case AUDIO_CODEC_OPUS:
|
||||
codecid = AV_CODEC_ID_OPUS;
|
||||
break;
|
||||
|
||||
case AUDIO_CODEC_AAC:
|
||||
codecid = AV_CODEC_ID_AAC;
|
||||
break;
|
||||
}
|
||||
|
||||
return codecid;
|
||||
}
|
||||
|
||||
AVCodecID to_video_avcodecid(int codec)
|
||||
{
|
||||
AVCodecID codecid = AV_CODEC_ID_NONE;
|
||||
|
||||
switch (codec)
|
||||
{
|
||||
case VIDEO_CODEC_H264:
|
||||
codecid = AV_CODEC_ID_H264;
|
||||
break;
|
||||
|
||||
case VIDEO_CODEC_H265:
|
||||
codecid = AV_CODEC_ID_HEVC;
|
||||
break;
|
||||
|
||||
case VIDEO_CODEC_MP4:
|
||||
codecid = AV_CODEC_ID_MPEG4;
|
||||
break;
|
||||
|
||||
case VIDEO_CODEC_JPEG:
|
||||
codecid = AV_CODEC_ID_MJPEG;
|
||||
break;
|
||||
}
|
||||
|
||||
return codecid;
|
||||
}
|
||||
|
||||
int to_audio_codec(AVCodecID codecid)
|
||||
{
|
||||
int codec = AUDIO_CODEC_NONE;
|
||||
|
||||
switch (codecid)
|
||||
{
|
||||
case AV_CODEC_ID_PCM_ALAW:
|
||||
codec = AUDIO_CODEC_G711A;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_PCM_MULAW:
|
||||
codec = AUDIO_CODEC_G711U;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_ADPCM_G726:
|
||||
codec = AUDIO_CODEC_G726;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_ADPCM_G722:
|
||||
codec = AUDIO_CODEC_G722;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_OPUS:
|
||||
codec = AUDIO_CODEC_OPUS;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_AAC:
|
||||
codec = AUDIO_CODEC_AAC;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
int to_video_codec(AVCodecID codecid)
|
||||
{
|
||||
int codec = VIDEO_CODEC_NONE;
|
||||
|
||||
switch (codecid)
|
||||
{
|
||||
case AV_CODEC_ID_H264:
|
||||
codec = VIDEO_CODEC_H264;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_HEVC:
|
||||
codec = VIDEO_CODEC_H265;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_MPEG4:
|
||||
codec = VIDEO_CODEC_MP4;
|
||||
break;
|
||||
|
||||
case AV_CODEC_ID_MJPEG:
|
||||
codec = VIDEO_CODEC_JPEG;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
AVPixelFormat to_avpixelformat(int fmt)
|
||||
{
|
||||
AVPixelFormat pixfmt = AV_PIX_FMT_NONE;
|
||||
|
||||
if (VIDEO_FMT_BGR24 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_BGR24;
|
||||
}
|
||||
else if (VIDEO_FMT_YUV420P == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_YUV420P;
|
||||
}
|
||||
else if (VIDEO_FMT_YUYV422 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_YUYV422;
|
||||
}
|
||||
else if (VIDEO_FMT_YVYU422 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_YVYU422;
|
||||
}
|
||||
else if (VIDEO_FMT_UYVY422 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_UYVY422;
|
||||
}
|
||||
else if (VIDEO_FMT_NV12 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_NV12;
|
||||
}
|
||||
else if (VIDEO_FMT_NV21 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_NV21;
|
||||
}
|
||||
else if (VIDEO_FMT_RGB24 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_RGB24;
|
||||
}
|
||||
else if (VIDEO_FMT_RGB32 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_RGB32;
|
||||
}
|
||||
else if (VIDEO_FMT_ARGB == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_ARGB;
|
||||
}
|
||||
else if (VIDEO_FMT_BGRA == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_BGRA;
|
||||
}
|
||||
else if (VIDEO_FMT_YV12 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_YUV420P;
|
||||
}
|
||||
else if (VIDEO_FMT_BGR32 == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_BGR32;
|
||||
}
|
||||
else if (VIDEO_FMT_YUV422P == fmt)
|
||||
{
|
||||
pixfmt = AV_PIX_FMT_YUV422P;
|
||||
}
|
||||
|
||||
return pixfmt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
43
MediaClient/MediaClientForMobile/media/media_codec.h
Normal file
43
MediaClient/MediaClientForMobile/media/media_codec.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef MEDIA_CODEC_H
|
||||
#define MEDIA_CODEC_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
AVCodecID to_audio_avcodecid(int codec);
|
||||
AVCodecID to_video_avcodecid(int codec);
|
||||
int to_audio_codec(AVCodecID codecid);
|
||||
int to_video_codec(AVCodecID codecid);
|
||||
AVPixelFormat to_avpixelformat(int fmt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
64
MediaClient/MediaClientForMobile/media/media_format.h
Normal file
64
MediaClient/MediaClientForMobile/media/media_format.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef _MEDIA_FORMAT_H
|
||||
#define _MEDIA_FORMAT_H
|
||||
|
||||
// video pixel format
|
||||
#define VIDEO_FMT_NONE 0
|
||||
#define VIDEO_FMT_YUYV422 1
|
||||
#define VIDEO_FMT_YUV420P 2
|
||||
#define VIDEO_FMT_YVYU422 3
|
||||
#define VIDEO_FMT_UYVY422 4
|
||||
#define VIDEO_FMT_NV12 5
|
||||
#define VIDEO_FMT_NV21 6
|
||||
#define VIDEO_FMT_RGB24 7
|
||||
#define VIDEO_FMT_RGB32 8
|
||||
#define VIDEO_FMT_ARGB 9
|
||||
#define VIDEO_FMT_BGRA 10
|
||||
#define VIDEO_FMT_YV12 11
|
||||
#define VIDEO_FMT_BGR24 12
|
||||
#define VIDEO_FMT_BGR32 13
|
||||
#define VIDEO_FMT_YUV422P 14
|
||||
#define VIDEO_FMT_MJPG 15
|
||||
|
||||
// video codec
|
||||
#define VIDEO_CODEC_NONE 0
|
||||
#define VIDEO_CODEC_H264 1
|
||||
#define VIDEO_CODEC_MP4 2
|
||||
#define VIDEO_CODEC_JPEG 3
|
||||
#define VIDEO_CODEC_H265 4
|
||||
|
||||
// audio codec
|
||||
#define AUDIO_CODEC_NONE 0
|
||||
#define AUDIO_CODEC_G711A 1
|
||||
#define AUDIO_CODEC_G711U 2
|
||||
#define AUDIO_CODEC_G726 3
|
||||
#define AUDIO_CODEC_AAC 4
|
||||
#define AUDIO_CODEC_G722 5
|
||||
#define AUDIO_CODEC_OPUS 6
|
||||
|
||||
|
||||
#define DATA_TYPE_VIDEO 0
|
||||
#define DATA_TYPE_AUDIO 1
|
||||
#define DATA_TYPE_METADATA 2
|
||||
|
||||
#endif // _MEDIA_FORMAT_H
|
||||
|
||||
|
||||
259
MediaClient/MediaClientForMobile/media/media_parse.cpp
Normal file
259
MediaClient/MediaClientForMobile/media/media_parse.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "media_parse.h"
|
||||
#include "media_format.h"
|
||||
#include "media_util.h"
|
||||
#include "h264.h"
|
||||
#include "h265.h"
|
||||
#include "mjpeg.h"
|
||||
#include "h264_util.h"
|
||||
#include "h265_util.h"
|
||||
#include "bs.h"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
int avc_parse_video_size(int codec, uint8 * p_data, uint32 len, int * width, int * height)
|
||||
{
|
||||
uint8 nalu_t;
|
||||
|
||||
if (p_data == NULL || len < 5)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Need to parse width X height
|
||||
|
||||
if (VIDEO_CODEC_H264 == codec)
|
||||
{
|
||||
int s_len = 0, n_len = 0, parse_len = len;
|
||||
uint8 * p_cur = p_data;
|
||||
uint8 * p_end = p_data + len;
|
||||
|
||||
while (p_cur && p_cur < p_end && parse_len > 0)
|
||||
{
|
||||
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
|
||||
|
||||
nalu_t = (p_cur[s_len] & 0x1F);
|
||||
|
||||
int b_start;
|
||||
nal_t nal;
|
||||
|
||||
nal.i_payload = n_len-s_len-1;
|
||||
nal.p_payload = p_cur+s_len+1;
|
||||
nal.i_type = nalu_t;
|
||||
|
||||
if (nalu_t == H264_NAL_SPS)
|
||||
{
|
||||
h264_t parse;
|
||||
h264_parser_init(&parse);
|
||||
|
||||
h264_parser_parse(&parse, &nal, &b_start);
|
||||
log_print(HT_LOG_INFO, "%s, H264 width[%d],height[%d]\r\n", __FUNCTION__, parse.i_width, parse.i_height);
|
||||
*width = parse.i_width;
|
||||
*height = parse.i_height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
parse_len -= n_len;
|
||||
p_cur = p_next;
|
||||
}
|
||||
}
|
||||
else if (VIDEO_CODEC_H265 == codec)
|
||||
{
|
||||
int s_len, n_len = 0, parse_len = len;
|
||||
uint8 * p_cur = p_data;
|
||||
uint8 * p_end = p_data + len;
|
||||
|
||||
while (p_cur && p_cur < p_end && parse_len > 0)
|
||||
{
|
||||
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
|
||||
|
||||
nalu_t = (p_cur[s_len] >> 1) & 0x3F;
|
||||
|
||||
if (nalu_t == HEVC_NAL_SPS)
|
||||
{
|
||||
h265_t parse;
|
||||
h265_parser_init(&parse);
|
||||
|
||||
if (h265_parser_parse(&parse, p_cur+s_len, n_len-s_len) == 0)
|
||||
{
|
||||
log_print(HT_LOG_INFO, "%s, H265 width[%d],height[%d]\r\n", __FUNCTION__, parse.pic_width_in_luma_samples, parse.pic_height_in_luma_samples);
|
||||
*width = parse.pic_width_in_luma_samples;
|
||||
*height = parse.pic_height_in_luma_samples;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
parse_len -= n_len;
|
||||
p_cur = p_next;
|
||||
}
|
||||
}
|
||||
else if (VIDEO_CODEC_JPEG == codec)
|
||||
{
|
||||
uint32 offset = 0;
|
||||
int size_chunk = 0;
|
||||
|
||||
while (offset < len - 8 && p_data[offset] == 0xFF)
|
||||
{
|
||||
if (p_data[offset+1] == MARKER_SOF0)
|
||||
{
|
||||
int h = ((p_data[offset+5] << 8) | p_data[offset+6]);
|
||||
int w = ((p_data[offset+7] << 8) | p_data[offset+8]);
|
||||
log_print(HT_LOG_INFO, "%s, MJPEG width[%d],height[%d]\r\n", __FUNCTION__, w, h);
|
||||
*width = w;
|
||||
*height = h;
|
||||
break;
|
||||
}
|
||||
else if (p_data[offset+1] == MARKER_SOI)
|
||||
{
|
||||
offset += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_chunk = ((p_data[offset+2] << 8) | p_data[offset+3]);
|
||||
offset += 2 + size_chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (VIDEO_CODEC_MP4 == codec)
|
||||
{
|
||||
uint32 pos = 0;
|
||||
int vol_f = 0;
|
||||
int vol_pos = 0;
|
||||
int vol_len = 0;
|
||||
|
||||
while (pos < len - 4)
|
||||
{
|
||||
if (p_data[pos] == 0 && p_data[pos+1] == 0 && p_data[pos+2] == 1)
|
||||
{
|
||||
if (p_data[pos+3] >= 0x20 && p_data[pos+3] <= 0x2F)
|
||||
{
|
||||
vol_f = 1;
|
||||
vol_pos = pos+4;
|
||||
}
|
||||
else if (vol_f)
|
||||
{
|
||||
vol_len = pos - vol_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (!vol_f)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (vol_len <= 0)
|
||||
{
|
||||
vol_len = len - vol_pos;
|
||||
}
|
||||
|
||||
int vo_ver_id = 0;
|
||||
|
||||
bs_t bs;
|
||||
bs_init(&bs, &p_data[vol_pos], vol_len * 8);
|
||||
|
||||
bs_skip(&bs, 1); /* random access */
|
||||
bs_skip(&bs, 8); /* vo_type */
|
||||
|
||||
if (bs_read1(&bs)) /* is_ol_id */
|
||||
{
|
||||
vo_ver_id = bs_read(&bs, 4); /* vo_ver_id */
|
||||
bs_skip(&bs, 3); /* vo_priority */
|
||||
}
|
||||
|
||||
if (bs_read(&bs, 4) == 15) // aspect_ratio_info
|
||||
{
|
||||
bs_skip(&bs, 8); // par_width
|
||||
bs_skip(&bs, 8); // par_height
|
||||
}
|
||||
|
||||
if (bs_read1(&bs)) /* vol control parameter */
|
||||
{
|
||||
bs_read(&bs, 2);
|
||||
|
||||
bs_skip(&bs, 1); /* low_delay */
|
||||
|
||||
if (bs_read1(&bs))
|
||||
{
|
||||
/* vbv parameters */
|
||||
bs_read(&bs, 15); /* first_half_bitrate */
|
||||
bs_skip(&bs, 1);
|
||||
bs_read(&bs, 15); /* latter_half_bitrate */
|
||||
bs_skip(&bs, 1);
|
||||
bs_read(&bs, 15); /* first_half_vbv_buffer_size */
|
||||
bs_skip(&bs, 1);
|
||||
bs_read(&bs, 3); /* latter_half_vbv_buffer_size */
|
||||
bs_read(&bs, 11); /* first_half_vbv_occupancy */
|
||||
bs_skip(&bs, 1);
|
||||
bs_read(&bs, 15); /* latter_half_vbv_occupancy */
|
||||
bs_skip(&bs, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int shape = bs_read(&bs, 2); /* vol shape */
|
||||
|
||||
if (shape == 3 && vo_ver_id != 1)
|
||||
{
|
||||
bs_skip(&bs, 4); /* video_object_layer_shape_extension */
|
||||
}
|
||||
|
||||
bs_skip(&bs, 1);
|
||||
|
||||
int framerate = bs_read(&bs, 16);
|
||||
|
||||
int time_increment_bits = (int) (log(framerate - 1.0) * 1.44269504088896340736 + 1); // log2(framerate - 1) + 1
|
||||
if (time_increment_bits < 1)
|
||||
{
|
||||
time_increment_bits = 1;
|
||||
}
|
||||
|
||||
bs_skip(&bs, 1);
|
||||
|
||||
if (bs_read1(&bs) != 0) /* fixed_vop_rate */
|
||||
{
|
||||
bs_skip(&bs, time_increment_bits);
|
||||
}
|
||||
|
||||
if (shape != 2)
|
||||
{
|
||||
if (shape == 0)
|
||||
{
|
||||
bs_skip(&bs, 1);
|
||||
int w = bs_read(&bs, 13);
|
||||
bs_skip(&bs, 1);
|
||||
int h = bs_read(&bs, 13);
|
||||
|
||||
log_print(HT_LOG_INFO, "%s, MPEG4 width[%d],height[%d]\r\n", __FUNCTION__, w, h);
|
||||
*width = w;
|
||||
*height = h;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
37
MediaClient/MediaClientForMobile/media/media_parse.h
Normal file
37
MediaClient/MediaClientForMobile/media/media_parse.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef MEDIA_PARSE_H
|
||||
#define MEDIA_PARSE_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int avc_parse_video_size(int codec, uint8 * p_data, uint32 len, int * width, int * height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
253
MediaClient/MediaClientForMobile/media/media_util.cpp
Normal file
253
MediaClient/MediaClientForMobile/media/media_util.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "media_util.h"
|
||||
#include "media_format.h"
|
||||
#include "h264.h"
|
||||
#include "h265.h"
|
||||
|
||||
uint32 remove_emulation_bytes(uint8* to, uint32 toMaxSize, uint8* from, uint32 fromSize)
|
||||
{
|
||||
uint32 toSize = 0;
|
||||
uint32 i = 0;
|
||||
|
||||
while (i < fromSize && toSize+1 < toMaxSize)
|
||||
{
|
||||
if (i+2 < fromSize && from[i] == 0 && from[i+1] == 0 && from[i+2] == 3)
|
||||
{
|
||||
to[toSize] = to[toSize+1] = 0;
|
||||
toSize += 2;
|
||||
i += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
to[toSize] = from[i];
|
||||
toSize += 1;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return toSize;
|
||||
}
|
||||
|
||||
static uint8 * avc_find_startcode_internal(uint8 *p, uint8 *end)
|
||||
{
|
||||
uint8 *a = p + 4 - ((intptr_t)p & 3);
|
||||
|
||||
for (end -= 3; p < a && p < end; p++)
|
||||
{
|
||||
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
for (end -= 3; p < end; p += 4)
|
||||
{
|
||||
uint32 x = *(const uint32*)p;
|
||||
|
||||
if ((x - 0x01010101) & (~x) & 0x80808080)
|
||||
{ // generic
|
||||
if (p[1] == 0)
|
||||
{
|
||||
if (p[0] == 0 && p[2] == 1)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
if (p[2] == 0 && p[3] == 1)
|
||||
{
|
||||
return p+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p[3] == 0)
|
||||
{
|
||||
if (p[2] == 0 && p[4] == 1)
|
||||
{
|
||||
return p+2;
|
||||
}
|
||||
|
||||
if (p[4] == 0 && p[5] == 1)
|
||||
{
|
||||
return p+3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (end += 3; p < end; p++)
|
||||
{
|
||||
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return end + 3;
|
||||
}
|
||||
|
||||
uint8 * avc_find_startcode(uint8 *p, uint8 *end)
|
||||
{
|
||||
uint8 *out = avc_find_startcode_internal(p, end);
|
||||
if (p<out && out<end && !out[-1])
|
||||
{
|
||||
out--;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
uint8 * avc_split_nalu(uint8 * e_buf, int e_len, int * s_len, int * d_len)
|
||||
{
|
||||
uint8 *r, *r1 = NULL, *end = e_buf + e_len;
|
||||
|
||||
*s_len = 0;
|
||||
*d_len = 0;
|
||||
|
||||
r = avc_find_startcode(e_buf, end);
|
||||
if (r < end)
|
||||
{
|
||||
if (r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1)
|
||||
{
|
||||
*s_len = 4;
|
||||
}
|
||||
else if (r[0] == 0 && r[1] == 0 && r[2] == 1)
|
||||
{
|
||||
*s_len = 3;
|
||||
}
|
||||
|
||||
while (!*(r++));
|
||||
|
||||
r1 = avc_find_startcode(r, end);
|
||||
|
||||
*d_len = r1 - r + *s_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
*d_len = e_len;
|
||||
}
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
uint8 avc_h264_nalu_type(uint8 * e_buf, int len)
|
||||
{
|
||||
if (len > 4 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 0 && e_buf[3] == 1)
|
||||
{
|
||||
return (e_buf[4] & 0x1f);
|
||||
}
|
||||
else if (len > 3 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 1)
|
||||
{
|
||||
return (e_buf[3] & 0x1f);
|
||||
}
|
||||
else if (len > 0)
|
||||
{
|
||||
return (e_buf[0] & 0x1f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 avc_h265_nalu_type(uint8 * e_buf, int len)
|
||||
{
|
||||
if (len > 4 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 0 && e_buf[3] == 1)
|
||||
{
|
||||
return ((e_buf[4] >> 1) & 0x3f);
|
||||
}
|
||||
else if (len > 3 && e_buf[0] == 0 && e_buf[1] == 0 && e_buf[2] == 1)
|
||||
{
|
||||
return ((e_buf[3] >> 1) & 0x3f);
|
||||
}
|
||||
else if (len > 0)
|
||||
{
|
||||
return ((e_buf[0] >> 1) & 0x3f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL avc_get_h26x_paramsets(uint8 * data, int len, int codec, H26XParamSets * ps)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
int s_len = 0, n_len = 0, parse_len = len;
|
||||
uint8 * p_cur = data;
|
||||
uint8 * p_end = data + len;
|
||||
|
||||
while (p_cur && p_cur < p_end && parse_len > 0)
|
||||
{
|
||||
uint8 nalu;
|
||||
uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
|
||||
|
||||
if (VIDEO_CODEC_H264 == codec)
|
||||
{
|
||||
nalu = avc_h264_nalu_type(p_cur, n_len);
|
||||
|
||||
if (nalu == H264_NAL_SPS && ps->sps_size == 0 && n_len <= (int)sizeof(ps->sps))
|
||||
{
|
||||
memcpy(ps->sps, p_cur, n_len);
|
||||
ps->sps_size = n_len;
|
||||
}
|
||||
else if (nalu == H264_NAL_PPS && ps->pps_size == 0 && n_len <= (int)sizeof(ps->pps))
|
||||
{
|
||||
memcpy(ps->pps, p_cur, n_len);
|
||||
ps->pps_size = n_len;
|
||||
}
|
||||
|
||||
if (ps->sps_size > 0 && ps->pps_size > 0)
|
||||
{
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (VIDEO_CODEC_H265 == codec)
|
||||
{
|
||||
nalu = avc_h265_nalu_type(p_cur, n_len);
|
||||
|
||||
if (nalu == HEVC_NAL_VPS && ps->vps_size == 0 && n_len <= (int)sizeof(ps->vps))
|
||||
{
|
||||
memcpy(ps->vps, p_cur, n_len);
|
||||
ps->vps_size = n_len;
|
||||
}
|
||||
else if (nalu == HEVC_NAL_SPS && ps->sps_size == 0 && n_len <= (int)sizeof(ps->sps))
|
||||
{
|
||||
memcpy(ps->sps, p_cur, n_len);
|
||||
ps->sps_size = n_len;
|
||||
}
|
||||
else if (nalu == HEVC_NAL_PPS && ps->pps_size == 0 && n_len <= (int)sizeof(ps->pps))
|
||||
{
|
||||
memcpy(ps->pps, p_cur, n_len);
|
||||
ps->pps_size = n_len;
|
||||
}
|
||||
|
||||
if (ps->vps_size > 0 && ps->sps_size > 0 && ps->pps_size > 0)
|
||||
{
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parse_len -= n_len;
|
||||
p_cur = p_next;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
53
MediaClient/MediaClientForMobile/media/media_util.h
Normal file
53
MediaClient/MediaClientForMobile/media/media_util.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef MEDIA_UTIL_H
|
||||
#define MEDIA_UTIL_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 vps[512];
|
||||
int vps_size;
|
||||
uint8 sps[512];
|
||||
int sps_size;
|
||||
uint8 pps[512];
|
||||
int pps_size;
|
||||
} H26XParamSets;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint32 remove_emulation_bytes(uint8* to, uint32 toMaxSize, uint8* from, uint32 fromSize);
|
||||
uint8 * avc_find_startcode(uint8 *p, uint8 *end);
|
||||
uint8 * avc_split_nalu(uint8 * e_buf, int e_len, int * s_len, int * d_len);
|
||||
uint8 avc_h264_nalu_type(uint8 * e_buf, int len);
|
||||
uint8 avc_h265_nalu_type(uint8 * e_buf, int len);
|
||||
BOOL avc_get_h26x_paramsets(uint8 * data, int len, int codec, H26XParamSets * ps);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
434
MediaClient/MediaClientForMobile/media/rtmp_player.cpp
Normal file
434
MediaClient/MediaClientForMobile/media/rtmp_player.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
60
MediaClient/MediaClientForMobile/media/rtmp_player.h
Normal file
60
MediaClient/MediaClientForMobile/media/rtmp_player.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#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
|
||||
|
||||
|
||||
|
||||
683
MediaClient/MediaClientForMobile/media/rtsp_player.cpp
Normal file
683
MediaClient/MediaClientForMobile/media/rtsp_player.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
93
MediaClient/MediaClientForMobile/media/rtsp_player.h
Normal file
93
MediaClient/MediaClientForMobile/media/rtsp_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
436
MediaClient/MediaClientForMobile/media/srt_player.cpp
Normal file
436
MediaClient/MediaClientForMobile/media/srt_player.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
63
MediaClient/MediaClientForMobile/media/srt_player.h
Normal file
63
MediaClient/MediaClientForMobile/media/srt_player.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef 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
|
||||
|
||||
|
||||
|
||||
569
MediaClient/MediaClientForMobile/media/video_decoder.cpp
Normal file
569
MediaClient/MediaClientForMobile/media/video_decoder.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
89
MediaClient/MediaClientForMobile/media/video_decoder.h
Normal file
89
MediaClient/MediaClientForMobile/media/video_decoder.h
Normal 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
|
||||
|
||||
|
||||
742
MediaClient/MediaClientForMobile/media/video_player.cpp
Normal file
742
MediaClient/MediaClientForMobile/media/video_player.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
190
MediaClient/MediaClientForMobile/media/video_player.h
Normal file
190
MediaClient/MediaClientForMobile/media/video_player.h
Normal 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
|
||||
|
||||
|
||||
|
||||
60
MediaClient/MediaClientForMobile/media/video_render.cpp
Normal file
60
MediaClient/MediaClientForMobile/media/video_render.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "video_render.h"
|
||||
|
||||
|
||||
CVideoRender::CVideoRender()
|
||||
: m_bInited(FALSE)
|
||||
, m_hWnd(0)
|
||||
, m_nVideoWidth(0)
|
||||
, m_nVideoHeight(0)
|
||||
, m_nVideoFmt(VIDEO_FMT_YUV420P)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CVideoRender::~CVideoRender()
|
||||
{
|
||||
unInit();
|
||||
}
|
||||
|
||||
BOOL CVideoRender::init(WId hWnd, int w, int h, int videofmt)
|
||||
{
|
||||
m_hWnd = hWnd;
|
||||
m_nVideoWidth = w;
|
||||
m_nVideoHeight = h;
|
||||
m_nVideoFmt = videofmt;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CVideoRender::unInit()
|
||||
{
|
||||
m_bInited = FALSE;
|
||||
}
|
||||
|
||||
BOOL CVideoRender::render(AVFrame * frame, int mode)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
62
MediaClient/MediaClientForMobile/media/video_render.h
Normal file
62
MediaClient/MediaClientForMobile/media/video_render.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef 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
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user