1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 13:51:47 -05:00
sdrangel/plugins/channelrx/demoddatv/datvideorender.cpp

599 lines
16 KiB
C++
Raw Normal View History

2018-02-22 16:52:49 -05:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 F4HKW //
// for F4EXB / SDRAngel //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "datvideorender.h"
DATVideoRender::DATVideoRender(QWidget * parent):
2018-03-11 11:39:02 -04:00
TVScreen(true, parent)
2018-02-22 16:52:49 -05:00
{
installEventFilter(this);
2019-03-19 18:12:54 -04:00
m_blnIsFullScreen = false;
m_blnRunning = false;
m_blnIsFFMPEGInitialized = false;
m_blnIsOpen = false;
m_objFormatCtx = nullptr;
m_objDecoderCtx = nullptr;
m_objSwsCtx = nullptr;
m_audioBuffer.resize(1<<14);
m_audioBufferFill = 0;
m_audioFifo = nullptr;
m_intVideoStreamIndex = -1;
m_audioStreamIndex = -1;
2018-02-22 16:52:49 -05:00
m_intCurrentRenderWidth=-1;
m_intCurrentRenderHeight=-1;
2019-03-16 20:36:44 -04:00
m_objFrame=nullptr;
2018-02-22 16:52:49 -05:00
m_intFrameCount=-1;
}
bool DATVideoRender::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease)
{
SetFullScreen(false);
return true;
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
void DATVideoRender::SetFullScreen(bool blnFullScreen)
{
2019-03-16 20:36:44 -04:00
if (m_blnIsFullScreen == blnFullScreen) {
2018-02-22 16:52:49 -05:00
return;
}
2019-03-16 20:36:44 -04:00
if (blnFullScreen == true)
2018-02-22 16:52:49 -05:00
{
setWindowFlags(Qt::Window);
setWindowState(Qt::WindowFullScreen);
show();
m_blnIsFullScreen=true;
}
else
{
setWindowFlags(Qt::Widget);
setWindowState(Qt::WindowNoState);
show();
m_blnIsFullScreen=false;
}
}
static int ReadFunction(void *opaque, uint8_t *buf, int buf_size)
{
QIODevice* objStream = reinterpret_cast<QIODevice*>(opaque);
int intNbBytes = objStream->read((char*)buf, buf_size);
return intNbBytes;
}
static int64_t SeekFunction(void* opaque, int64_t offset, int whence)
{
QIODevice* objStream = reinterpret_cast<QIODevice*>(opaque);
2019-03-16 20:36:44 -04:00
if (whence == AVSEEK_SIZE) {
2018-02-22 16:52:49 -05:00
return -1;
}
2019-03-16 20:36:44 -04:00
if (objStream->isSequential()) {
2018-02-22 16:52:49 -05:00
return -1;
}
2019-03-16 20:36:44 -04:00
if (objStream->seek(offset) == false) {
2018-02-22 16:52:49 -05:00
return -1;
}
return objStream->pos();
}
2018-02-25 19:04:45 -05:00
void DATVideoRender::ResetMetaData()
2018-02-22 16:52:49 -05:00
{
MetaData.CodecID=-1;
MetaData.PID=-1;
MetaData.Program="";
MetaData.Stream="";
MetaData.Width=-1;
MetaData.Height=-1;
MetaData.BitRate=-1;
MetaData.Channels=-1;
MetaData.CodecDescription= "";
2018-02-25 19:04:45 -05:00
MetaData.OK_Decoding=false;
MetaData.OK_TransportStream=false;
MetaData.OK_VideoStream=false;
emit onMetaDataChanged(&MetaData);
}
bool DATVideoRender::InitializeFFMPEG()
{
ResetMetaData();
2019-03-16 20:36:44 -04:00
if (m_blnIsFFMPEGInitialized) {
2018-02-22 16:52:49 -05:00
return false;
}
avcodec_register_all();
av_register_all();
av_log_set_level(AV_LOG_FATAL);
//av_log_set_level(AV_LOG_ERROR);
m_blnIsFFMPEGInitialized=true;
return true;
}
bool DATVideoRender::PreprocessStream()
{
2019-03-16 20:36:44 -04:00
AVDictionary *objOpts = nullptr;
AVCodec *objCodec = nullptr;
2018-02-22 16:52:49 -05:00
int intRet=-1;
2019-03-16 20:36:44 -04:00
char *objBuffer=nullptr;
2018-02-22 16:52:49 -05:00
//Identify stream
2019-03-16 20:36:44 -04:00
if (avformat_find_stream_info(m_objFormatCtx, nullptr) < 0)
2018-02-22 16:52:49 -05:00
{
avformat_close_input(&m_objFormatCtx);
2019-03-16 20:36:44 -04:00
m_objFormatCtx=nullptr;
2018-02-22 16:52:49 -05:00
qDebug() << "DATVideoProcess::PreprocessStream cannot find stream info";
return false;
}
//Find video stream
2019-03-16 20:36:44 -04:00
intRet = av_find_best_stream(m_objFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
2018-02-22 16:52:49 -05:00
if (intRet < 0)
{
avformat_close_input(&m_objFormatCtx);
qDebug() << "DATVideoProcess::PreprocessStream cannot find video stream";
return false;
}
m_intVideoStreamIndex = intRet;
2019-03-19 18:12:54 -04:00
//Find audio stream
intRet = av_find_best_stream(m_objFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (intRet < 0) {
qDebug() << "DATVideoProcess::PreprocessStream cannot find audio stream";
}
m_audioStreamIndex = intRet;
2018-02-22 16:52:49 -05:00
//Prepare Codec and extract meta data
// FIXME: codec is depreecated but replacement fails
// AVCodecParameters *parms = m_objFormatCtx->streams[m_intVideoStreamIndex]->codecpar;
// m_objDecoderCtx = new AVCodecContext();
// avcodec_parameters_to_context(m_objDecoderCtx, parms);
2018-02-22 16:52:49 -05:00
m_objDecoderCtx = m_objFormatCtx->streams[m_intVideoStreamIndex]->codec;
//Meta Data
MetaData.PID = m_objFormatCtx->streams[m_intVideoStreamIndex]->id;
MetaData.CodecID = m_objDecoderCtx->codec_id;
2018-02-25 19:04:45 -05:00
MetaData.OK_TransportStream = true;
2018-02-22 16:52:49 -05:00
MetaData.Program="";
MetaData.Stream="";
if(m_objFormatCtx->programs)
{
2019-03-16 20:36:44 -04:00
objBuffer=nullptr;
2018-02-22 16:52:49 -05:00
av_dict_get_string(m_objFormatCtx->programs[m_intVideoStreamIndex]->metadata,&objBuffer,':','\n');
2019-03-16 20:36:44 -04:00
if(objBuffer!=nullptr) {
2018-02-22 16:52:49 -05:00
MetaData.Program = QString("%1").arg(objBuffer);
}
}
2019-03-16 20:36:44 -04:00
objBuffer=nullptr;
2018-02-22 16:52:49 -05:00
av_dict_get_string(m_objFormatCtx->streams[m_intVideoStreamIndex]->metadata,&objBuffer,':','\n');
2019-03-16 20:36:44 -04:00
if (objBuffer != nullptr) {
2018-02-22 16:52:49 -05:00
MetaData.Stream = QString("%1").arg(objBuffer);
}
2018-02-25 19:04:45 -05:00
emit onMetaDataChanged(&MetaData);
2018-02-22 16:52:49 -05:00
//Decoder
objCodec = avcodec_find_decoder(m_objDecoderCtx->codec_id);
2019-03-16 20:36:44 -04:00
if (objCodec == nullptr)
2018-02-22 16:52:49 -05:00
{
avformat_close_input(&m_objFormatCtx);
2019-03-16 20:36:44 -04:00
m_objFormatCtx=nullptr;
2018-02-22 16:52:49 -05:00
qDebug() << "DATVideoProcess::PreprocessStream cannot find associated CODEC";
return false;
}
av_dict_set(&objOpts, "refcounted_frames", "1", 0);
if (avcodec_open2(m_objDecoderCtx, objCodec, &objOpts) < 0)
{
avformat_close_input(&m_objFormatCtx);
2019-03-16 20:36:44 -04:00
m_objFormatCtx=nullptr;
2018-02-22 16:52:49 -05:00
qDebug() << "DATVideoProcess::PreprocessStream cannot open associated CODEC";
return false;
}
//Allocate Frame
m_objFrame = av_frame_alloc();
if (!m_objFrame)
{
avformat_close_input(&m_objFormatCtx);
2019-03-16 20:36:44 -04:00
m_objFormatCtx=nullptr;
2018-02-22 16:52:49 -05:00
qDebug() << "DATVideoProcess::PreprocessStream cannot allocate frame";
return false;
}
m_intFrameCount=0;
MetaData.Width=m_objDecoderCtx->width;
MetaData.Height=m_objDecoderCtx->height;
2018-02-25 19:04:45 -05:00
MetaData.BitRate= m_objDecoderCtx->bit_rate;
2018-02-22 16:52:49 -05:00
MetaData.Channels=m_objDecoderCtx->channels;
MetaData.CodecDescription= QString("%1").arg(objCodec->long_name);
2018-02-25 19:04:45 -05:00
MetaData.OK_VideoStream = true;
emit onMetaDataChanged(&MetaData);
2018-02-22 16:52:49 -05:00
return true;
}
bool DATVideoRender::OpenStream(DATVideostream *objDevice)
{
int intIOBufferSize = 32768;
2019-03-16 20:36:44 -04:00
unsigned char * ptrIOBuffer = nullptr;
AVIOContext * objIOCtx = nullptr;
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
if(m_blnRunning) {
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (objDevice == nullptr)
2018-02-22 16:52:49 -05:00
{
2019-03-16 20:36:44 -04:00
qDebug() << "DATVideoProcess::OpenStream QIODevice is nullptr";
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (m_blnIsOpen)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::OpenStream already open";
return false;
}
2019-03-16 20:36:44 -04:00
if (objDevice->bytesAvailable() <= 0)
2018-02-25 19:04:45 -05:00
{
qDebug() << "DATVideoProcess::OpenStream no data available";
2019-03-16 20:36:44 -04:00
MetaData.OK_Data = false;
2018-02-25 19:04:45 -05:00
emit onMetaDataChanged(&MetaData);
return false;
}
//Only once execution
2019-03-16 20:36:44 -04:00
m_blnRunning = true;
2018-02-25 19:04:45 -05:00
2019-03-16 20:36:44 -04:00
MetaData.OK_Data = true;
2018-02-25 19:04:45 -05:00
emit onMetaDataChanged(&MetaData);
2018-02-22 16:52:49 -05:00
InitializeFFMPEG();
2019-03-16 20:36:44 -04:00
if (!m_blnIsFFMPEGInitialized)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::OpenStream FFMPEG not initialized";
2019-03-16 20:36:44 -04:00
m_blnRunning = false;
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (!objDevice->open(QIODevice::ReadOnly))
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::OpenStream cannot open QIODevice";
2019-03-16 20:36:44 -04:00
m_blnRunning = false;
2018-02-22 16:52:49 -05:00
return false;
}
//Connect QIODevice to FFMPEG Reader
m_objFormatCtx = avformat_alloc_context();
2019-03-16 20:36:44 -04:00
if (m_objFormatCtx == nullptr)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::OpenStream cannot alloc format FFMPEG context";
2019-03-16 20:36:44 -04:00
m_blnRunning = false;
2018-02-22 16:52:49 -05:00
return false;
}
ptrIOBuffer = (unsigned char *)av_malloc(intIOBufferSize+ AV_INPUT_BUFFER_PADDING_SIZE);
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
objIOCtx = avio_alloc_context(ptrIOBuffer,
intIOBufferSize,
0,
reinterpret_cast<void *>(objDevice),
&ReadFunction,
nullptr,
&SeekFunction);
2018-02-22 16:52:49 -05:00
m_objFormatCtx->pb = objIOCtx;
m_objFormatCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
2019-03-16 20:36:44 -04:00
if (avformat_open_input(&m_objFormatCtx, nullptr , nullptr, nullptr) < 0)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::OpenStream cannot open stream";
2018-02-25 19:04:45 -05:00
m_blnRunning=false;
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (!PreprocessStream())
2018-02-22 16:52:49 -05:00
{
2018-02-25 19:04:45 -05:00
m_blnRunning=false;
2018-02-22 16:52:49 -05:00
return false;
}
m_blnIsOpen=true;
m_blnRunning=false;
return true;
}
bool DATVideoRender::RenderStream()
{
AVPacket objPacket;
int intGotFrame;
bool blnNeedRenderingSetup;
2018-02-25 19:04:45 -05:00
if(!m_blnIsOpen)
2018-02-22 16:52:49 -05:00
{
2018-02-25 19:04:45 -05:00
qDebug() << "DATVideoProcess::RenderStream Stream not open";
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if(m_blnRunning) {
2018-02-22 16:52:49 -05:00
return false;
}
//Only once execution
m_blnRunning=true;
//********** Rendering **********
if (av_read_frame(m_objFormatCtx, &objPacket) < 0)
{
qDebug() << "DATVideoProcess::RenderStream reading packet error";
m_blnRunning=false;
return false;
}
//Video channel
if (objPacket.stream_index == m_intVideoStreamIndex)
{
memset(m_objFrame, 0, sizeof(AVFrame));
av_frame_unref(m_objFrame);
2019-03-16 20:36:44 -04:00
intGotFrame = 0;
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
if (new_decode( m_objDecoderCtx, m_objFrame, &intGotFrame, &objPacket)<0)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::RenderStream decoding packet error";
m_blnRunning=false;
return false;
}
2019-03-16 20:36:44 -04:00
if (intGotFrame)
2018-02-22 16:52:49 -05:00
{
//Rendering and RGB Converter setup
blnNeedRenderingSetup=(m_intFrameCount==0);
2019-03-16 20:36:44 -04:00
blnNeedRenderingSetup|=(m_objSwsCtx==nullptr);
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
if ((m_intCurrentRenderWidth!=m_objFrame->width) || (m_intCurrentRenderHeight!=m_objFrame->height)) {
2018-02-22 16:52:49 -05:00
blnNeedRenderingSetup=true;
}
2019-03-16 20:36:44 -04:00
if (blnNeedRenderingSetup)
2018-02-22 16:52:49 -05:00
{
2019-03-16 20:36:44 -04:00
if (m_objSwsCtx != nullptr)
2018-02-22 16:52:49 -05:00
{
sws_freeContext(m_objSwsCtx);
2019-03-16 20:36:44 -04:00
m_objSwsCtx=nullptr;
2018-02-22 16:52:49 -05:00
}
//Convertisseur YUV -> RGB
m_objSwsCtx = sws_alloc_context();
av_opt_set_int(m_objSwsCtx,"srcw",m_objFrame->width,0);
av_opt_set_int(m_objSwsCtx,"srch",m_objFrame->height,0);
av_opt_set_int(m_objSwsCtx,"src_format",m_objFrame->format,0);
av_opt_set_int(m_objSwsCtx,"dstw",m_objFrame->width,0);
av_opt_set_int(m_objSwsCtx,"dsth",m_objFrame->height,0);
av_opt_set_int(m_objSwsCtx,"dst_format",AV_PIX_FMT_RGB24 ,0);
2018-02-25 19:04:45 -05:00
av_opt_set_int(m_objSwsCtx,"sws_flag", SWS_FAST_BILINEAR /* SWS_BICUBIC*/,0);
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
if (sws_init_context(m_objSwsCtx, nullptr, nullptr) < 0)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::RenderStream cannont init video data converter";
2019-03-16 20:36:44 -04:00
m_objSwsCtx=nullptr;
2018-02-22 16:52:49 -05:00
m_blnRunning=false;
return false;
}
2019-03-16 20:36:44 -04:00
if ((m_intCurrentRenderHeight>0) && (m_intCurrentRenderWidth>0))
2018-02-22 16:52:49 -05:00
{
//av_freep(&m_pbytDecodedData[0]);
//av_freep(&m_pintDecodedLineSize[0]);
}
2019-03-16 20:36:44 -04:00
if (av_image_alloc(m_pbytDecodedData, m_pintDecodedLineSize,m_objFrame->width, m_objFrame->height, AV_PIX_FMT_RGB24, 1)<0)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::RenderStream cannont init video image buffer";
sws_freeContext(m_objSwsCtx);
2019-03-16 20:36:44 -04:00
m_objSwsCtx=nullptr;
2018-02-22 16:52:49 -05:00
m_blnRunning=false;
return false;
}
//Rendering device setup
2019-03-16 20:36:44 -04:00
resizeTVScreen(m_objFrame->width,m_objFrame->height);
update();
resetImage();
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
m_intCurrentRenderWidth=m_objFrame->width;
m_intCurrentRenderHeight=m_objFrame->height;
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
MetaData.Width = m_objFrame->width;
MetaData.Height = m_objFrame->height;
MetaData.OK_Decoding = true;
emit onMetaDataChanged(&MetaData);
2018-02-22 16:52:49 -05:00
}
//Frame rendering
2019-03-16 20:36:44 -04:00
if (sws_scale(m_objSwsCtx, m_objFrame->data, m_objFrame->linesize, 0, m_objFrame->height, m_pbytDecodedData, m_pintDecodedLineSize)<0)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::RenderStream error converting video frame to RGB";
m_blnRunning=false;
return false;
}
renderImage(m_pbytDecodedData[0]);
av_frame_unref(m_objFrame);
m_intFrameCount ++;
}
}
2019-03-19 18:12:54 -04:00
else if (objPacket.stream_index == m_audioStreamIndex)
{
}
2018-02-22 16:52:49 -05:00
av_packet_unref(&objPacket);
2018-02-22 16:52:49 -05:00
//********** Rendering **********
m_blnRunning=false;
return true;
}
bool DATVideoRender::CloseStream(QIODevice *objDevice)
{
2019-03-16 20:36:44 -04:00
if (m_blnRunning) {
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (!objDevice)
2018-02-22 16:52:49 -05:00
{
2019-03-16 20:36:44 -04:00
qDebug() << "DATVideoProcess::CloseStream QIODevice is nullptr";
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (!m_blnIsOpen)
2018-02-22 16:52:49 -05:00
{
2018-02-25 19:04:45 -05:00
qDebug() << "DATVideoProcess::CloseStream Stream not open";
2018-02-22 16:52:49 -05:00
return false;
}
2019-03-16 20:36:44 -04:00
if (!m_objFormatCtx)
2018-02-22 16:52:49 -05:00
{
qDebug() << "DATVideoProcess::CloseStream FFMEG Context is not initialized";
return false;
}
2018-02-25 19:04:45 -05:00
//Only once execution
m_blnRunning=true;
// maybe done in the avcodec_close
// avformat_close_input(&m_objFormatCtx);
2019-03-16 20:36:44 -04:00
// m_objFormatCtx=nullptr;
2018-02-22 16:52:49 -05:00
2019-03-16 20:36:44 -04:00
if (m_objDecoderCtx)
2018-02-22 16:52:49 -05:00
{
avcodec_close(m_objDecoderCtx);
2019-03-16 20:36:44 -04:00
m_objDecoderCtx = nullptr;
2018-02-22 16:52:49 -05:00
}
2019-03-16 20:36:44 -04:00
if (m_objFrame)
2018-02-22 16:52:49 -05:00
{
av_frame_unref(m_objFrame);
av_frame_free(&m_objFrame);
}
2019-03-16 20:36:44 -04:00
if (m_objSwsCtx != nullptr)
2018-02-22 16:52:49 -05:00
{
sws_freeContext(m_objSwsCtx);
2019-03-16 20:36:44 -04:00
m_objSwsCtx = nullptr;
2018-02-22 16:52:49 -05:00
}
objDevice->close();
m_blnIsOpen=false;
m_blnRunning=false;
m_intCurrentRenderWidth=-1;
m_intCurrentRenderHeight=-1;
2018-02-25 19:04:45 -05:00
ResetMetaData();
emit onMetaDataChanged(&MetaData);
2018-02-22 16:52:49 -05:00
return true;
}
/**
* Replacement of deprecated avcodec_decode_video2 with the same signature
* https://blogs.gentoo.org/lu_zero/2016/03/29/new-avcodec-api/
*/
int DATVideoRender::new_decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
{
int ret;
*got_frame = 0;
2019-03-16 20:36:44 -04:00
if (pkt)
{
ret = avcodec_send_packet(avctx, pkt);
// In particular, we don't expect AVERROR(EAGAIN), because we read all
// decoded frames with avcodec_receive_frame() until done.
2019-03-16 20:36:44 -04:00
if (ret < 0) {
return ret == AVERROR_EOF ? 0 : ret;
2019-03-16 20:36:44 -04:00
}
}
ret = avcodec_receive_frame(avctx, frame);
2019-03-16 20:36:44 -04:00
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
return ret;
2019-03-16 20:36:44 -04:00
}
if (ret >= 0) {
*got_frame = 1;
2019-03-16 20:36:44 -04:00
}
return 0;
}