diff --git a/source/lib/PlaYUVerStream.cpp b/source/lib/PlaYUVerStream.cpp new file mode 100644 index 0000000..4f65926 --- /dev/null +++ b/source/lib/PlaYUVerStream.cpp @@ -0,0 +1,491 @@ +/* This file is a part of plaYUVer project + * Copyright (C) 2014 by Luis Lucas (luisfrlucas@gmail.com) + * Joao Carreira (jfmcarreira@gmail.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * \file PlaYUVerStream.cpp + * \brief Input stream handling + */ + +#include "config.h" + +#include +#include "PlaYUVerStream.h" +#ifdef USE_FFMPEG +#include "LibAvContextHandle.h" +#include "LibMemory.h" +#endif + +namespace plaYUVer +{ + +InputStream::InputStream() +{ + m_bInit = false; + m_bLoadAll = false; + m_iErrorStatus = 0; + + m_pFile = NULL; + m_uiWidth = 0; + m_uiHeight = 0; + m_uiTotalFrameNum = 0; + m_iCurrFrameNum = -1; + m_iPixelFormat = -1; + m_uiFrameRate = 30; + m_iFileFormat = INVALID; + m_pInputBuffer = NULL; + m_cFilename = QString( "" ); + m_cStreamInformationString = QString( "" ); + m_pcCurrFrame = NULL; + m_pcNextFrame = NULL; + m_ppcFrameBuffer = NULL; + m_uiFrameBufferSize = 2; + m_uiFrameBufferIndex = 0; + m_uiAveragePlayInterval = 0; +} + +InputStream::~InputStream() +{ + close(); +} + +QString InputStream::supportedReadFormats() +{ + QString formats; + formats = "*.yuv " // Raw video +#ifdef USE_FFMPEG + "*.avi " // Audio video interleaved + "*.mp4 " + "*.wmv "// Windows media video +#endif + ; + return formats; +} + +QString InputStream::supportedWriteFormats() +{ + QString formats; + formats = "*.bmp " // Windows Bitmap + "*.jpg "// Joint Photographic Experts Group + "*.jpeg "// Joint Photographic Experts Group + "*.png "// Portable Network Graphics + ; + return formats; +} + +QStringList InputStream::supportedReadFormatsList() +{ + QStringList formats; + formats << "Raw video (*.yuv)" +#ifdef USE_FFMPEG + << "Audio video interleaved (*.avi)" + << "MPEG4 (*.mp4)" + << "Windows media video (*.wmv)" +#endif + ; + return formats; +} + +QStringList InputStream::supportedWriteFormatsList() +{ + QStringList formats; + formats << "Windows Bitmap (*.bmp)" + << "Joint Photographic Experts Group (*.jpg *.jpeg)" + << "Portable Network Graphics (*.png)"; + return formats; +} + +Bool InputStream::open( QString filename, UInt width, UInt height, Int input_format, UInt frame_rate ) +{ + Bool avStatus = false; + + if( m_bInit ) + { + close(); + } + + m_bInit = false; + m_cFilename = filename; + m_uiWidth = width; + m_uiHeight = height; + m_uiFrameRate = frame_rate; + + m_iFileFormat = YUVFormat; + m_iPixelFormat = input_format; + +#ifdef USE_FFMPEG + if( QFileInfo( filename ).suffix().compare( QString( "yuv" ) ) ) + { + avStatus = m_cLibAvContext.initAvFormat( m_cFilename.toLocal8Bit().data(), m_uiWidth, m_uiHeight, m_iPixelFormat, m_uiFrameRate, m_uiTotalFrameNum ); + m_cFormatName = QFileInfo( filename ).completeSuffix().toUpper(); + m_cCodedName = QString::fromUtf8( m_cLibAvContext.getCodecName() ).toUpper(); + } +#endif + + if( m_uiWidth <= 0 || m_uiHeight <= 0 || m_iPixelFormat < 0 ) + { + close(); + return m_bInit; + } + + m_uiFrameBufferSize = 2; + getMem1D( &m_ppcFrameBuffer, m_uiFrameBufferSize ); + for( UInt i = 0; i < m_uiFrameBufferSize; i++ ) + { + m_ppcFrameBuffer[i] = new PlaYUVerFrame( m_uiWidth, m_uiHeight, m_iPixelFormat ); + if( !m_ppcFrameBuffer[i] ) + { + close(); + return m_bInit; + } + } + m_uiFrameBufferIndex = 0; + m_pcCurrFrame = m_ppcFrameBuffer[m_uiFrameBufferIndex]; + + UInt64 frame_bytes_input = m_pcCurrFrame->getBytesPerFrame(); + + if( !avStatus ) + { + m_pFile = NULL; + Char *filename = m_cFilename.toLocal8Bit().data(); + m_pFile = fopen( filename, "rb" ); + if( m_pFile == NULL ) + { + close(); + return m_bInit; + } + fseek( m_pFile, 0, SEEK_END ); + m_uiTotalFrameNum = ftell( m_pFile ) / ( frame_bytes_input ); + fseek( m_pFile, 0, SEEK_SET ); + m_cFormatName = QString::fromUtf8( "YUV" ); + m_cCodedName = QString::fromUtf8( "Raw Video" ); + } + if( !getMem1D( &m_pInputBuffer, m_pcCurrFrame->getBytesPerFrame() ) ) + { + close(); + return m_bInit; + } + + m_cStreamInformationString = QString( "[" ); + m_cStreamInformationString.append( m_cFormatName ); + m_cStreamInformationString.append( QString( " / " ) ); + m_cStreamInformationString.append( m_cCodedName ); + m_cStreamInformationString.append( QString( " / " ) ); + m_cStreamInformationString.append( PlaYUVerFrame::supportedPixelFormatList().at( m_iPixelFormat ) ); + m_cStreamInformationString.append( "] " ); + m_cStreamInformationString.append( QFileInfo( m_cFilename ).fileName() ); + + m_iCurrFrameNum = -1; + seekInput( 0 ); + //loadAll(); + + m_cTimer.start(); + m_uiAveragePlayInterval = 0; + + m_bInit = true; + return m_bInit; +} + +Void InputStream::close() +{ + if( m_pFile ) + fclose( m_pFile ); + +#ifdef USE_FFMPEG + m_cLibAvContext.closeAvFormat(); +#endif + + if( m_ppcFrameBuffer ) + { + for( UInt i = 0; i < m_uiFrameBufferSize; i++ ) + { + if( m_ppcFrameBuffer[i] ) + delete m_ppcFrameBuffer[i]; + m_ppcFrameBuffer[i] = NULL; + } + freeMem1D( m_ppcFrameBuffer ); + m_ppcFrameBuffer = NULL; + } + + if( m_pInputBuffer ) + freeMem1D( m_pInputBuffer ); + + qDebug( ) << "Frame read time: " + << QString::number( 1000 / ( m_uiAveragePlayInterval + 1 ) ) + << " fps"; + + m_bLoadAll = false; + m_bInit = false; +} + +Void InputStream::getFormat( UInt& rWidth, UInt& rHeight, Int& rInputFormat, UInt& rFrameRate ) +{ + if( m_bInit ) + { + rWidth = m_uiWidth; + rHeight = m_uiHeight; + rInputFormat = m_iPixelFormat; + rFrameRate = m_uiFrameRate; + } + else + { + rWidth = 0; + rHeight = 0; + rInputFormat = -1; + rFrameRate = 30; + } +} + +Bool InputStream::guessFormat( QString filename, UInt& rWidth, UInt& rHeight, Int& rInputFormat, UInt& rFrameRate ) +{ + Bool bRet = false; + QString fileExtension = QFileInfo( filename ).suffix(); + if( !fileExtension.compare( "yuv" ) ) + { + bRet = true; + // Guess resolution - match %dx%d +#if( QT_VERSION_PLAYUVER == 5 ) + QRegularExpressionMatch resolutionMatch = QRegularExpression( "_\\d*x\\d*_" ).match( filename ); + if( resolutionMatch.hasMatch() ) + { + QString resolutionString = resolutionMatch.captured( 0 ); + if( resolutionString.startsWith( "_" ) && resolutionString.endsWith( "_" ) ) + { + resolutionString.remove( "_" ); + QStringList resolutionArgs = resolutionString.split( "x" ); + qDebug( ) << "Found resolution = " + << resolutionArgs; + if( resolutionArgs.size() == 2 ) + { + rWidth = resolutionArgs.at( 0 ).toUInt(); + rHeight = resolutionArgs.at( 1 ).toUInt(); + } + } + } +#endif + // Guess pixel format + QStringList formats_list = PlaYUVerFrame::supportedPixelFormatList(); + for( Int i = 0; i < formats_list.size(); i++ ) + { + if( filename.contains( formats_list.at( i ), Qt::CaseInsensitive ) ) + { + rInputFormat = i; + break; + } + } + if( rWidth > 0 && rHeight > 0 && rInputFormat >= 0 ) + bRet = false; + } + return bRet; +} + +Void InputStream::loadAll() +{ + if( m_bLoadAll ) + return; + + if( m_ppcFrameBuffer ) + { + for( UInt i = 0; i < m_uiFrameBufferSize; i++ ) + { + if( m_ppcFrameBuffer[i] ) + delete m_ppcFrameBuffer[i]; + m_ppcFrameBuffer[i] = NULL; + } + freeMem1D( m_ppcFrameBuffer ); + m_ppcFrameBuffer = NULL; + } + m_uiFrameBufferSize = m_uiTotalFrameNum; + getMem1D( &m_ppcFrameBuffer, m_uiFrameBufferSize ); + for( UInt i = 0; i < m_uiFrameBufferSize; i++ ) + { + m_ppcFrameBuffer[i] = new PlaYUVerFrame( m_uiWidth, m_uiHeight, m_iPixelFormat ); + if( !m_ppcFrameBuffer[i] ) + { + close(); + } + } + m_uiFrameBufferIndex = 0; + m_pcCurrFrame = m_ppcFrameBuffer[m_uiFrameBufferIndex]; + m_iCurrFrameNum = -1; + seekInput( 0 ); + for( UInt i = 0; i < m_uiFrameBufferSize-1; i++ ) + { + readNextFrame(); + setNextFrame(); + } + m_bLoadAll = true; + seekInput( 0 ); +} + +Void InputStream::getDuration( Int* duration_array ) +{ + Int hours, mins, secs; +#ifdef USE_FFMPEG + if( m_cLibAvContext.getStatus() ) + { + secs = m_cLibAvContext.getStreamDuration(); + } + else +#endif + { + secs = m_uiTotalFrameNum / m_uiFrameRate; + } + mins = secs / 60; + secs %= 60; + hours = mins / 60; + mins %= 60; + *duration_array++ = hours; + *duration_array++ = mins; + *duration_array++ = secs; +} + +Void InputStream::readNextFrame() +{ + UInt64 bytes_read = 0; + //m_cTimer.restart(); + + if( m_iCurrFrameNum + 1 >= (Int64)m_uiTotalFrameNum ) + { + m_iErrorStatus = LAST_FRAME; + m_pcNextFrame = NULL; + return; + } + + if( m_bLoadAll ) + { + return; + } + +#ifdef USE_FFMPEG + if( m_cLibAvContext.getStatus() ) + { + m_cLibAvContext.decodeAvFormat(); + m_pcNextFrame->FrameFromBuffer( m_cLibAvContext.video_dst_data[0], m_iPixelFormat ); + } + else +#endif + { + UInt64 frame_bytes_input = m_pcNextFrame->getBytesPerFrame(); + bytes_read = fread( m_pInputBuffer, sizeof(Pel), frame_bytes_input, m_pFile ); + if( bytes_read != frame_bytes_input ) + { + m_iErrorStatus = READING; + qDebug( ) << " Reading error !!!" + << endl; + return; + } + m_pcNextFrame->FrameFromBuffer( m_pInputBuffer, m_iPixelFormat ); + } + //Int time = m_cTimer.elapsed(); + //m_uiAveragePlayInterval = ( m_uiAveragePlayInterval + time) / 2; + m_pcNextFrame->FrametoRGB8(); + return; +} + +Bool InputStream::writeFrame( const QString& filename ) +{ + //m_pcCurrFrame->getQimage().save( filename ); + return true; +} + +Void InputStream::setNextFrame() +{ + if( m_pcNextFrame ) + { + m_pcCurrFrame = m_pcNextFrame; + m_iCurrFrameNum++; + m_uiFrameBufferIndex++; + if( m_uiFrameBufferIndex == m_uiFrameBufferSize ) + { + m_uiFrameBufferIndex = 0; + } + m_pcNextFrame = m_ppcFrameBuffer[m_uiFrameBufferIndex]; + } + else + { + m_iErrorStatus = END_OF_SEQ; + seekInput( 0 ); + } +} + +PlaYUVerFrame* InputStream::getCurrFrame( PlaYUVerFrame *pyuv_image ) +{ + if( pyuv_image == NULL ) + pyuv_image = new PlaYUVerFrame( m_pcCurrFrame ); + else + pyuv_image->copyFrom( m_pcCurrFrame ); + return pyuv_image; +} + +PlaYUVerFrame* InputStream::getCurrFrame() +{ + return m_pcCurrFrame; +} + +Void InputStream::seekInput( UInt64 new_frame_num ) +{ + if( !m_pFile || new_frame_num < 0 || new_frame_num >= m_uiTotalFrameNum || (Int64)new_frame_num == m_iCurrFrameNum ) + return; + + m_iCurrFrameNum = new_frame_num - 1; + + if( m_bLoadAll ) + { + m_uiFrameBufferIndex = new_frame_num; + m_pcNextFrame = m_ppcFrameBuffer[m_uiFrameBufferIndex]; + setNextFrame(); + return; + } +#ifdef USE_FFMPEG + if( m_cLibAvContext.getStatus() ) + { + m_cLibAvContext.seekAvFormat( new_frame_num ); + } + else +#endif + { + UInt64 frame_bytes_input = m_ppcFrameBuffer[0]->getBytesPerFrame(); + UInt64 nbytes_seek = frame_bytes_input * new_frame_num; + fseek( m_pFile, nbytes_seek, SEEK_SET ); + } + m_uiFrameBufferIndex = 0; + m_pcCurrFrame = m_pcNextFrame = m_ppcFrameBuffer[m_uiFrameBufferIndex]; + + readNextFrame(); + setNextFrame(); + if( m_uiTotalFrameNum > 1 ) + readNextFrame(); +} + +Bool InputStream::checkErrors( Int error_type ) +{ + if( m_iErrorStatus == error_type ) + { + m_iErrorStatus = NO_STREAM_ERROR; + return true; + } + if( m_iErrorStatus == error_type ) + { + m_iErrorStatus = NO_STREAM_ERROR; + return true; + } + return false; +} + +} // NAMESPACE diff --git a/source/lib/PlaYUVerStream.h b/source/lib/PlaYUVerStream.h new file mode 100644 index 0000000..5a3e8c1 --- /dev/null +++ b/source/lib/PlaYUVerStream.h @@ -0,0 +1,188 @@ +/* This file is a part of plaYUVer project + * Copyright (C) 2014 by Luis Lucas (luisfrlucas@gmail.com) + * Joao Carreira (jfmcarreira@gmail.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * \file PlaYUVerStream.h + * \brief Input stream handling + */ + +#ifndef __INPUTSTREAM_H__ +#define __INPUTSTREAM_H__ + +#ifdef PLAYUVER +#include "config.h" +#endif +#include "PlaYUVerDefs.h" +#include +#include +#include +#ifdef USE_OPENCV +#include +#endif + +#ifdef USE_FFMPEG +#include "LibAvContextHandle.h" +#endif +#include "PlaYUVerFrame.h" + +namespace plaYUVer +{ + +class InputStream +{ +private: + + Bool m_bInit; + Bool m_bLoadAll; + Int m_iErrorStatus; + +#ifdef USE_FFMPEG + LibAvContextHandle m_cLibAvContext; +#endif + + QString m_cFilename; + QString m_cFormatName; + QString m_cCodedName; + QString m_cStreamInformationString; + + QTime m_cTimer; + UInt m_uiAveragePlayInterval; + + FILE* m_pFile; /**< The input file pointer >*/ + Int m_iFileFormat; + Int m_iPixelFormat; + + Pel* m_pInputBuffer; + + UInt m_uiFrameBufferSize; + PlaYUVerFrame **m_ppcFrameBuffer; + PlaYUVerFrame *m_pcCurrFrame; + PlaYUVerFrame *m_pcNextFrame; + UInt m_uiFrameBufferIndex; + + UInt m_uiWidth; + UInt m_uiHeight; + UInt m_uiFrameRate; + UInt64 m_uiTotalFrameNum; + Int64 m_iCurrFrameNum; + +public: + InputStream(); + ~InputStream(); + + static QString supportedReadFormats(); + static QString supportedWriteFormats(); + static QStringList supportedReadFormatsList(); + static QStringList supportedWriteFormatsList(); + + enum InputStreamFormats + { + INVALID = -1, + YUVFormat = 0, // Use color space. + }; + + enum InputStreamErrors + { + NO_STREAM_ERROR = 0, + READING, + LAST_FRAME, + END_OF_SEQ, + }; + + Bool open( QString filename, UInt width, UInt height, Int input_format, UInt frame_rate ); + Void close(); + + Bool guessFormat( QString filename, UInt& rWidth, UInt& rHeight, Int& rInputFormat, UInt& rFrameRate ); + Void getFormat( UInt& rWidth, UInt& rHeight, Int& rInputFormat, UInt& rFrameRate ); + + Void loadAll(); + + Void readNextFrame(); + + Bool writeFrame( const QString& filename ); + + Void setNextFrame(); + PlaYUVerFrame* getCurrFrame(); + PlaYUVerFrame* getCurrFrame( PlaYUVerFrame * ); + + Void seekInput( UInt64 new_frame_num ); + + Bool checkErrors( Int error_type ); + + Bool isInit() + { + return m_bInit; + } + QString getFileName() + { + return m_cFilename; + } + QString getStreamInformationString() + { + return m_cStreamInformationString; + } + QString getFormatName() + { + return m_cFormatName; + } + QString getCodecName() + { + return m_cCodedName; + } + UInt getFrameNum() + { + return m_uiTotalFrameNum; + } + UInt getWidth() const + { + return m_uiWidth; + } + UInt getHeight() const + { + return m_uiHeight; + } + Int getCurrFrameNum() + { + return m_iCurrFrameNum; + } + UInt getFrameRate() + { + return m_uiFrameRate; + } + Void getDuration( Int* duration_array ); + + Void setWidth( UInt new_width ) + { + if( new_width > 0 && new_width % 2 == 0 ) + { + m_uiWidth = new_width; + } + } + Void setHeight( UInt new_height ) + { + if( new_height > 0 && new_height % 2 == 0 ) + { + m_uiHeight = new_height; + } + } +}; + +} // NAMESPACE + +#endif // __INPUTSTREAM_H__