2014-07-13 09:57:25 -04:00
# include <iostream>
2015-08-14 16:44:20 -04:00
# include <assert.h>
2015-08-20 21:46:28 -04:00
# include <malloc.h>
2014-07-13 09:57:25 -04:00
# include <QByteArray>
# include <QBuffer>
# include <QImage>
# include "VideoProcessor.h"
# include "QVideoDevice.h"
2015-08-16 22:59:49 -04:00
# include "DaubechyWavelets.h"
2014-07-13 09:57:25 -04:00
2015-08-19 21:50:51 -04:00
# include <math.h>
2015-08-23 10:27:50 -04:00
2015-08-19 21:50:51 -04:00
extern " C " {
# include <libavcodec/avcodec.h>
# include <libavutil/opt.h>
# include <libavutil/channel_layout.h>
# include <libavutil/common.h>
# include <libavutil/imgutils.h>
# include <libavutil/mathematics.h>
# include <libavutil/samplefmt.h>
}
2015-08-20 22:59:51 -04:00
# define DEBUG_MPEG_VIDEO 1
2015-08-19 21:50:51 -04:00
2015-08-14 16:44:20 -04:00
VideoProcessor : : VideoProcessor ( )
2015-08-20 22:59:51 -04:00
: _encoded_frame_size ( 176 , 144 )
2014-07-20 16:50:36 -04:00
{
2015-08-14 16:44:20 -04:00
_decoded_output_device = NULL ;
2015-08-19 21:50:51 -04:00
//_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO;
//_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_DDWT_VIDEO;
_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO ;
2014-07-20 16:50:36 -04:00
}
2014-07-13 09:57:25 -04:00
2015-08-14 16:44:20 -04:00
bool VideoProcessor : : processImage ( const QImage & img , uint32_t size_hint , uint32_t & encoded_size )
2014-07-13 09:57:25 -04:00
{
2015-08-14 16:44:20 -04:00
VideoCodec * codec ;
2015-08-09 18:09:17 -04:00
2015-08-14 16:44:20 -04:00
switch ( _encoding_current_codec )
{
case VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO : codec = & _jpeg_video_codec ;
break ;
case VIDEO_PROCESSOR_CODEC_ID_DDWT_VIDEO : codec = & _ddwt_video_codec ;
break ;
2015-08-19 21:50:51 -04:00
case VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO : codec = & _mpeg_video_codec ;
break ;
2015-08-14 16:44:20 -04:00
default :
codec = NULL ;
}
2014-07-13 09:57:25 -04:00
2015-08-14 16:44:20 -04:00
// std::cerr << "reducing to " << _frame_size.width() << " x " << _frame_size.height() << std::endl;
encoded_size = 0 ;
if ( codec )
{
RsVOIPDataChunk chunk ;
2015-08-23 21:08:27 -04:00
if ( codec - > encodeData ( img . scaled ( _encoded_frame_size , Qt : : IgnoreAspectRatio , Qt : : SmoothTransformation ) , size_hint , chunk ) & & chunk . size > 0 )
{
encoded_size = chunk . size ;
_encoded_out_queue . push_back ( chunk ) ;
}
2015-08-14 16:44:20 -04:00
return true ;
}
else
2015-08-14 20:15:44 -04:00
{
std : : cerr < < " No codec for codec ID = " < < _encoding_current_codec < < " . Please call VideoProcessor::setCurrentCodec() " < < std : : endl ;
2015-08-14 16:44:20 -04:00
return false ;
2015-08-14 20:15:44 -04:00
}
2014-07-15 16:04:31 -04:00
}
2015-08-14 16:44:20 -04:00
bool VideoProcessor : : nextEncodedPacket ( RsVOIPDataChunk & chunk )
2014-07-15 16:04:31 -04:00
{
2015-08-14 16:44:20 -04:00
if ( _encoded_out_queue . empty ( ) )
2014-07-15 16:04:31 -04:00
return false ;
2015-08-14 16:44:20 -04:00
chunk = _encoded_out_queue . front ( ) ;
_encoded_out_queue . pop_front ( ) ;
2014-07-13 09:57:25 -04:00
return true ;
}
2015-08-14 16:44:20 -04:00
void VideoProcessor : : setInternalFrameSize ( QSize s )
2014-07-13 09:57:25 -04:00
{
2015-08-14 16:44:20 -04:00
_encoded_frame_size = s ;
2014-07-13 09:57:25 -04:00
}
2015-08-14 16:44:20 -04:00
void VideoProcessor : : receiveEncodedData ( const RsVOIPDataChunk & chunk )
2014-07-13 09:57:25 -04:00
{
2015-08-10 22:13:50 -04:00
static const int HEADER_SIZE = 4 ;
// read frame type. Use first 4 bytes to give info about content.
2015-08-14 16:44:20 -04:00
//
// Byte Meaning Values
// 00 Codec CODEC_ID_JPEG_VIDEO Basic Jpeg codec
// CODEC_ID_DDWT_VIDEO Differential wavelet compression
//
// 01 Unused Might be useful later
//
// 0203 Flags Codec specific flags.
//
if ( chunk . size < HEADER_SIZE )
{
std : : cerr < < " JPEGVideoDecoder::decodeData(): Too small a data packet. size= " < < chunk . size < < std : : endl ;
return ;
}
uint32_t codid = ( ( unsigned char * ) chunk . data ) [ 0 ] + ( ( ( unsigned char * ) chunk . data ) [ 1 ] < < 8 ) ;
2015-08-19 21:50:51 -04:00
//uint16_t flags = ((unsigned char *)chunk.data)[2] + (((unsigned char *)chunk.data)[3] << 8) ;
2015-08-14 16:44:20 -04:00
VideoCodec * codec ;
2015-08-10 22:13:50 -04:00
2015-08-14 16:44:20 -04:00
switch ( codid )
2015-08-10 22:13:50 -04:00
{
2015-08-14 16:44:20 -04:00
case VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO : codec = & _jpeg_video_codec ;
break ;
case VIDEO_PROCESSOR_CODEC_ID_DDWT_VIDEO : codec = & _ddwt_video_codec ;
break ;
2015-08-19 21:50:51 -04:00
case VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO : codec = & _mpeg_video_codec ;
break ;
2015-08-14 16:44:20 -04:00
default :
codec = NULL ;
2015-08-10 22:13:50 -04:00
}
2015-08-14 16:44:20 -04:00
QImage img ;
2015-08-10 22:13:50 -04:00
2015-08-20 21:46:28 -04:00
if ( codec = = NULL )
{
2015-08-16 22:59:49 -04:00
std : : cerr < < " Unknown decoding codec: " < < codid < < std : : endl ;
2015-08-20 21:46:28 -04:00
return ;
}
2015-08-23 10:27:50 -04:00
if ( ! codec - > decodeData ( chunk , img ) )
{
std : : cerr < < " No image decoded. Probably in the middle of something... " < < std : : endl ;
return ;
}
if ( _decoded_output_device )
_decoded_output_device - > showFrame ( img ) ;
2015-08-14 16:44:20 -04:00
}
void VideoProcessor : : setMaximumFrameRate ( uint32_t bytes_per_sec )
{
std : : cerr < < " Video Encoder: maximum frame rate is set to " < < bytes_per_sec < < " Bps " < < std : : endl ;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
JPEGVideo : : JPEGVideo ( )
2015-08-14 20:15:44 -04:00
: _encoded_ref_frame_max_distance ( 10 ) , _encoded_ref_frame_count ( 10 )
2015-08-14 16:44:20 -04:00
{
}
bool JPEGVideo : : decodeData ( const RsVOIPDataChunk & chunk , QImage & image )
{
// now see if the frame is a differential frame, or just a reference frame.
uint16_t codec = ( ( unsigned char * ) chunk . data ) [ 0 ] + ( ( ( unsigned char * ) chunk . data ) [ 1 ] < < 8 ) ;
uint16_t flags = ( ( unsigned char * ) chunk . data ) [ 2 ] + ( ( ( unsigned char * ) chunk . data ) [ 3 ] < < 8 ) ;
assert ( codec = = VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO ) ;
2015-08-10 22:13:50 -04:00
// un-compress image data
2015-08-14 16:44:20 -04:00
QByteArray qb ( ( char * ) & ( ( uint8_t * ) chunk . data ) [ HEADER_SIZE ] , ( int ) chunk . size - HEADER_SIZE ) ;
2015-08-10 22:13:50 -04:00
if ( ! image . loadFromData ( qb , " JPEG " ) )
{
std : : cerr < < " image.loadFromData(): returned an error.: " < < std : : endl ;
2015-08-14 16:44:20 -04:00
return false ;
2015-08-10 22:13:50 -04:00
}
2015-08-14 16:44:20 -04:00
2015-08-10 22:13:50 -04:00
if ( flags & JPEG_VIDEO_FLAGS_DIFFERENTIAL_FRAME )
{
2015-08-14 20:15:44 -04:00
if ( _decoded_reference_frame . size ( ) ! = image . size ( ) )
{
std : : cerr < < " Bad reference frame! " < < std : : endl ;
return false ;
}
2015-08-14 16:44:20 -04:00
QImage res = _decoded_reference_frame ;
2015-08-10 22:13:50 -04:00
2015-08-16 22:59:49 -04:00
for ( int i = 0 ; i < image . byteCount ( ) ; + + i )
2015-08-14 20:15:44 -04:00
{
int new_val = ( int ) res . bits ( ) [ i ] + ( ( int ) image . bits ( ) [ i ] - 128 ) ;
res . bits ( ) [ i ] = std : : max ( 0 , std : : min ( 255 , new_val ) ) ;
}
2015-08-10 22:13:50 -04:00
2015-08-14 16:44:20 -04:00
image = res ;
2015-08-10 22:13:50 -04:00
}
2015-08-14 20:15:44 -04:00
else
_decoded_reference_frame = image ;
2015-08-14 16:44:20 -04:00
return true ;
2014-07-13 09:57:25 -04:00
}
2015-08-14 16:44:20 -04:00
bool JPEGVideo : : encodeData ( const QImage & image , uint32_t /* size_hint */ , RsVOIPDataChunk & voip_chunk )
2014-07-13 09:57:25 -04:00
{
2015-08-09 18:09:17 -04:00
// check if we make a diff image, or if we use the full frame.
QImage encoded_frame ;
2015-08-10 22:13:50 -04:00
bool differential_frame ;
2015-08-09 18:09:17 -04:00
2015-08-14 16:44:20 -04:00
if ( _encoded_ref_frame_count + + < _encoded_ref_frame_max_distance & & image . size ( ) = = _encoded_reference_frame . size ( ) )
2015-08-09 18:09:17 -04:00
{
// compute difference with reference frame.
encoded_frame = image ;
2015-08-16 22:59:49 -04:00
for ( int i = 0 ; i < image . byteCount ( ) ; + + i )
2015-08-14 20:15:44 -04:00
{
2015-08-14 22:44:39 -04:00
// We cannot use basic modulo 256 arithmetic, because the decompressed JPeg frames do not follow the same rules (values are clamped)
// and cause color blotches when perturbated by a differential frame.
2015-08-14 20:15:44 -04:00
int diff = ( ( int ) image . bits ( ) [ i ] - ( int ) _encoded_reference_frame . bits ( ) [ i ] ) + 128 ;
encoded_frame . bits ( ) [ i ] = ( unsigned char ) std : : max ( 0 , std : : min ( 255 , diff ) ) ;
}
differential_frame = true ;
2015-08-09 18:09:17 -04:00
}
else
{
2015-08-14 16:44:20 -04:00
_encoded_ref_frame_count = 0 ;
_encoded_reference_frame = image ;
2015-08-09 18:09:17 -04:00
encoded_frame = image ;
2015-08-14 22:44:39 -04:00
differential_frame = false ;
2015-08-09 18:09:17 -04:00
}
QByteArray qb ;
QBuffer buffer ( & qb ) ;
buffer . open ( QIODevice : : WriteOnly ) ;
encoded_frame . save ( & buffer , " JPEG " ) ;
2015-08-10 22:13:50 -04:00
voip_chunk . data = malloc ( HEADER_SIZE + qb . size ( ) ) ;
2015-08-14 22:44:39 -04:00
2015-08-10 22:13:50 -04:00
// build header
uint32_t flags = differential_frame ? JPEG_VIDEO_FLAGS_DIFFERENTIAL_FRAME : 0x0 ;
2015-08-14 22:44:39 -04:00
2015-08-14 16:44:20 -04:00
( ( unsigned char * ) voip_chunk . data ) [ 0 ] = VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 1 ] = ( VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO > > 8 ) & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 2 ] = flags & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 3 ] = ( flags > > 8 ) & 0xff ;
2015-08-14 22:44:39 -04:00
2015-08-16 22:59:49 -04:00
memcpy ( & ( ( unsigned char * ) voip_chunk . data ) [ HEADER_SIZE ] , qb . data ( ) , qb . size ( ) ) ;
2015-08-14 22:44:39 -04:00
2015-08-10 22:13:50 -04:00
voip_chunk . size = HEADER_SIZE + qb . size ( ) ;
2015-08-09 18:09:17 -04:00
voip_chunk . type = RsVOIPDataChunk : : RS_VOIP_DATA_TYPE_VIDEO ;
2015-08-14 16:44:20 -04:00
return true ;
2014-07-13 09:57:25 -04:00
}
2015-08-16 22:59:49 -04:00
bool WaveletVideo : : encodeData ( const QImage & image , uint32_t size_hint , RsVOIPDataChunk & voip_chunk )
{
static const int WAVELET_IMG_SIZE = 128 ;
static const float W_THRESHOLD = 0.005 ; // low quality
//static const float W_THRESHOLD = 0.0001; // high quality
//static const float W_THRESHOLD = 0.0005; // medium quality
static const int W2 = WAVELET_IMG_SIZE ;
static const int H2 = WAVELET_IMG_SIZE ;
assert ( image . width ( ) = = W2 ) ;
assert ( image . height ( ) = = H2 ) ;
float * temp = new float [ W2 * H2 ] ;
std : : cerr < < " codec type: wavelets. " < < std : : endl ;
// We should perform some interpolation here ;-)
//
for ( int i = 0 ; i < W2 * H2 ; + + i )
temp [ i ] = ( 0.3 * image . constBits ( ) [ 4 * i + 1 ] + 0.59 * image . constBits ( ) [ 4 * i + 2 ] + 0.11 * image . constBits ( ) [ 4 * i + 3 ] ) / 255.0 ;
std : : cerr < < " resized image to B&W " < < W2 < < " x " < < H2 < < std : : endl ;
2015-08-19 21:50:51 -04:00
DaubechyWavelets < float > : : DWT2D ( temp , W2 , H2 , DaubechyWavelets < float > : : DWT_DAUB12 , DaubechyWavelets < float > : : DWT_FORWARD ) ;
2015-08-16 22:59:49 -04:00
// Now estimate the max energy in the W coefs, and only keep the largest.
float mx = 0.0f ;
for ( int i = 0 ; i < W2 * H2 ; + + i )
if ( mx < fabsf ( temp [ i ] ) )
mx = fabs ( temp [ i ] ) ;
mx * = 1.1 ; // This avoids quantisation problems with wavelet coefs when they get too close to mx.
std : : cerr < < " max wavelet coef : " < < mx < < std : : endl ;
std : : vector < uint16_t > compressed_values ;
compressed_values . reserve ( W2 * H2 ) ;
for ( int i = 0 ; i < W2 * H2 ; + + i )
if ( fabs ( temp [ i ] ) > = W_THRESHOLD * mx ) // This needs to be improved. Wavelets do not all have the same visual impact.
{
// add one value, using 16 bits for coordinates and 16 bits for the value.
compressed_values . push_back ( ( uint16_t ) i ) ;
compressed_values . push_back ( quantize_16b ( temp [ i ] , mx ) ) ;
//float f2 = from_quantized_16b(quantize_16b(temp[i],mx),mx) ;
//if(fabs(f2 - temp[i]) >= 0.01*(fabs(temp[i])+fabs(f2)))
//std::cerr << " before: " << temp[i] << ", quantised=" << quantize_16b(temp[i],mx)<< ", after: " << f2 << std::endl;
}
delete [ ] temp ;
// Serialise all values into a memory buffer. This needs to be taken care of because of endian issues.
int compressed_size = 4 + compressed_values . size ( ) * 2 ;
std : : cerr < < " threshold : " < < W_THRESHOLD < < std : : endl ;
std : : cerr < < " values kept: " < < compressed_values . size ( ) / 2 < < std : : endl ;
std : : cerr < < " compression: " < < compressed_size / float ( W2 * H2 * 3 ) * 100 < < " % " < < std : : endl ;
voip_chunk . data = malloc ( HEADER_SIZE + compressed_size ) ;
// build header
uint32_t flags = 0 ;
( ( unsigned char * ) voip_chunk . data ) [ 0 ] = VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_DDWT_VIDEO & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 1 ] = ( VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_DDWT_VIDEO > > 8 ) & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 2 ] = flags & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 3 ] = ( flags > > 8 ) & 0xff ;
unsigned char * compressed_mem = & ( ( unsigned char * ) voip_chunk . data ) [ HEADER_SIZE ] ;
serialise_ufloat ( compressed_mem , mx ) ;
for ( uint32_t i = 0 ; i < compressed_values . size ( ) ; + + i )
{
compressed_mem [ 4 + 2 * i + 0 ] = compressed_values [ i ] & 0xff ;
compressed_mem [ 4 + 2 * i + 1 ] = compressed_values [ i ] > > 8 ;
}
voip_chunk . type = RsVOIPDataChunk : : RS_VOIP_DATA_TYPE_VIDEO ;
voip_chunk . size = HEADER_SIZE + compressed_size ;
return true ;
}
bool WaveletVideo : : decodeData ( const RsVOIPDataChunk & chunk , QImage & image )
{
static const int WAVELET_IMG_SIZE = 128 ;
static const int W2 = WAVELET_IMG_SIZE ;
static const int H2 = WAVELET_IMG_SIZE ;
float * temp = new float [ W2 * H2 ] ;
const unsigned char * compressed_mem = & static_cast < const unsigned char * > ( chunk . data ) [ HEADER_SIZE ] ;
int compressed_size = chunk . size - HEADER_SIZE ;
memset ( temp , 0 , W2 * H2 * sizeof ( float ) ) ;
float M = deserialise_ufloat ( compressed_mem ) ;
# ifdef VOIP_CODEC_DEBUG
std : : cerr < < " codec type: wavelets. " < < std : : endl ;
std : : cerr < < " max coef: " < < M < < std : : endl ;
# endif
for ( int i = 4 ; i < compressed_size ; i + = 4 )
{
// read all values. first 2 bytes: image coordinates.
// next two bytes: value.
//
uint16_t indx = compressed_mem [ i + 0 ] + ( compressed_mem [ i + 1 ] < < 8 ) ;
uint16_t encv = compressed_mem [ i + 2 ] + ( compressed_mem [ i + 3 ] < < 8 ) ;
float f = from_quantized_16b ( encv , M ) ;
temp [ indx ] = f ;
}
# ifdef VOIP_CODEC_DEBUG
std : : cerr < < " values read: " < < compressed_size / 4 - 1 < < std : : endl ;
# endif
2015-08-19 21:50:51 -04:00
DaubechyWavelets < float > : : DWT2D ( temp , W2 , H2 , DaubechyWavelets < float > : : DWT_DAUB12 , DaubechyWavelets < float > : : DWT_BACKWARD ) ;
2015-08-16 22:59:49 -04:00
# ifdef VOIP_CODEC_DEBUG
std : : cerr < < " resizing image to: " < < w < < " x " < < h < < std : : endl ;
# endif
image = QImage ( W2 , H2 , QImage : : Format_RGB32 ) ;
int indx = 0 ;
for ( int j = 0 ; j < H2 ; + + j )
for ( int i = 0 ; i < W2 ; + + i , + + indx )
{
uint32_t val = std : : min ( 255 , std : : max ( 0 , ( int ) ( 255 * temp [ indx ] ) ) ) ;
QRgb rgb = ( 0xff < < 24 ) + ( val < < 16 ) + ( val < < 8 ) + val ;
image . setPixel ( i , j , rgb ) ;
}
delete [ ] temp ;
return true ;
}
uint16_t WaveletVideo : : quantize_16b ( float x , float M )
{
// Do the quantization into
// x = M * (m * 2^{-p} / 2^10)
//
// where m is coded on 10 bits (0->1023), and p is coded on 6 bits (0->63).
// Packing [mp] into a 16bit uint16_t. M is the maximum coefficient over the quantization
// process.
//
// So this represents numbers from M * 1 * 2^{-73} to M
//
// All calculatoins are performed on x/M*2^10
//
static const float LOG2 = log ( 2.0f ) ;
int m , p ;
if ( fabs ( x ) < 1e-8 * M )
{
m = 0 ;
p = 0 ;
}
else
{
float log2f = log ( fabsf ( x ) / M ) / LOG2 ;
int mexp = ( int ) floor ( MANTISSE_BITS - log2f ) ;
m = ( int ) floor ( pow ( 2.0f , mexp + log2f ) ) ;
p = mexp ;
if ( p > ( 1 < < EXPONENT_BITS ) - 1 )
m = 0 ;
}
return ( uint16_t ) ( p & ( ( 1 < < EXPONENT_BITS ) - 1 ) ) + ( uint16_t ) ( ( m & ( ( 1 < < MANTISSE_BITS ) - 1 ) ) < < EXPONENT_BITS ) + ( ( x < 0.0 ) ? 32768 : 0 ) ;
}
float WaveletVideo : : from_quantized_16b ( uint16_t n , float M )
{
M * = ( n & 32768 ) ? - 1 : 1 ;
n & = 32767 ;
uint32_t p = n & ( ( 1 < < EXPONENT_BITS ) - 1 ) ;
uint32_t m = ( n & ( ( ( 1 < < MANTISSE_BITS ) - 1 ) < < EXPONENT_BITS ) ) > > EXPONENT_BITS ;
if ( p > 10 )
return M * m / 1024.0f / ( float ) ( 1 < < ( p - 10 ) ) ;
else
return M * m / ( float ) ( 1 < < p ) ;
}
void WaveletVideo : : serialise_ufloat ( unsigned char * mem , float f )
{
if ( f < 0.0f )
{
std : : cerr < < " (EE) Cannot serialise invalid negative float value " < < f < < " in " < < __PRETTY_FUNCTION__ < < std : : endl ;
return ;
}
// This serialisation is quite accurate. The max relative error is approx.
// 0.01% and most of the time less than 1e-05% The error is well distributed
// over numbers also.
//
uint32_t n = ( f < 1e-7 ) ? ( ~ ( uint32_t ) 0 ) : ( ( uint32_t ) ( ( 1.0f / ( 1.0f + f ) * ( ~ ( uint32_t ) 0 ) ) ) ) ;
mem [ 0 ] = n & 0xff ; n > > = 8 ;
mem [ 1 ] = n & 0xff ; n > > = 8 ;
mem [ 2 ] = n & 0xff ; n > > = 8 ;
mem [ 3 ] = n & 0xff ;
}
float WaveletVideo : : deserialise_ufloat ( const unsigned char * mem )
{
uint32_t n = mem [ 3 ] ;
n = ( n < < 8 ) + mem [ 2 ] ;
n = ( n < < 8 ) + mem [ 1 ] ;
n = ( n < < 8 ) + mem [ 0 ] ;
return 1.0f / ( n / ( float ) ( ~ ( uint32_t ) 0 ) ) - 1.0f ;
}
2015-08-19 21:50:51 -04:00
FFmpegVideo : : FFmpegVideo ( )
{
2015-08-19 22:17:50 -04:00
// Encoding
encoding_codec = NULL ;
encoding_frame_buffer = NULL ;
encoding_context = NULL ;
2015-08-19 21:50:51 -04:00
2015-08-20 21:46:28 -04:00
//AVCodecID codec_id = AV_CODEC_ID_H264 ;
AVCodecID codec_id = AV_CODEC_ID_MPEG1VIDEO ;
2015-08-19 21:50:51 -04:00
uint8_t endcode [ ] = { 0 , 0 , 1 , 0xb7 } ;
/* find the mpeg1 video encoder */
2015-08-19 22:17:50 -04:00
encoding_codec = avcodec_find_encoder ( codec_id ) ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
if ( ! encoding_codec ) throw ( " AV codec not found for codec id " ) ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
encoding_context = avcodec_alloc_context3 ( encoding_codec ) ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
if ( ! encoding_context ) throw std : : runtime_error ( " AV: Could not allocate video codec encoding context " ) ;
2015-08-19 21:50:51 -04:00
/* put sample parameters */
2015-08-19 22:17:50 -04:00
encoding_context - > bit_rate = 400000 ;
2015-08-19 21:50:51 -04:00
/* resolution must be a multiple of two */
2015-08-20 22:59:51 -04:00
encoding_context - > width = 176 ;
encoding_context - > height = 144 ;
2015-08-19 21:50:51 -04:00
/* frames per second */
2015-08-19 22:17:50 -04:00
encoding_context - > time_base = ( AVRational ) { 1 , 25 } ;
2015-08-19 21:50:51 -04:00
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder , if frame - > pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
2015-08-19 22:17:50 -04:00
encoding_context - > gop_size = 10 ;
encoding_context - > max_b_frames = 1 ;
2015-08-19 21:50:51 -04:00
//context->pix_fmt = AV_PIX_FMT_RGB24;
2015-08-19 22:17:50 -04:00
encoding_context - > pix_fmt = AV_PIX_FMT_YUV420P ;
2015-08-19 21:50:51 -04:00
if ( codec_id = = AV_CODEC_ID_H264 )
2015-08-19 22:17:50 -04:00
av_opt_set ( encoding_context - > priv_data , " preset " , " slow " , 0 ) ;
2015-08-19 21:50:51 -04:00
/* open it */
2015-08-19 22:17:50 -04:00
if ( avcodec_open2 ( encoding_context , encoding_codec , NULL ) < 0 )
2015-08-19 21:50:51 -04:00
throw std : : runtime_error ( " AV: Could not open codec context. Something's wrong. " ) ;
2015-08-20 21:46:28 -04:00
encoding_frame_buffer = avcodec_alloc_frame ( ) ; //(AVFrame*)malloc(sizeof(AVFrame)) ;
2015-08-19 21:50:51 -04:00
2015-08-23 21:08:27 -04:00
if ( ! encoding_frame_buffer )
throw std : : runtime_error ( " AV: could not allocate frame buffer. " ) ;
2015-08-19 22:17:50 -04:00
encoding_frame_buffer - > format = encoding_context - > pix_fmt ;
encoding_frame_buffer - > width = encoding_context - > width ;
encoding_frame_buffer - > height = encoding_context - > height ;
2015-08-19 21:50:51 -04:00
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc ( ) is to be used */
2015-08-23 21:08:27 -04:00
int ret = av_image_alloc ( encoding_frame_buffer - > data , encoding_frame_buffer - > linesize ,
encoding_context - > width , encoding_context - > height , encoding_context - > pix_fmt , 32 ) ;
2015-08-19 21:50:51 -04:00
if ( ret < 0 )
throw std : : runtime_error ( " AV: Could not allocate raw picture buffer " ) ;
2015-08-19 22:17:50 -04:00
encoding_frame_count = 0 ;
// Decoding
decoding_codec = avcodec_find_decoder ( codec_id ) ;
if ( ! decoding_codec )
throw ( " AV codec not found for codec id " ) ;
decoding_context = avcodec_alloc_context3 ( decoding_codec ) ;
2015-08-23 10:27:50 -04:00
if ( ! decoding_context )
throw std : : runtime_error ( " AV: Could not allocate video codec decoding context " ) ;
2015-08-20 22:59:51 -04:00
decoding_context - > width = encoding_context - > width ;
decoding_context - > height = encoding_context - > height ;
decoding_context - > pix_fmt = AV_PIX_FMT_YUV420P ;
2015-08-19 22:17:50 -04:00
2015-08-23 10:27:50 -04:00
if ( decoding_codec - > capabilities & CODEC_CAP_TRUNCATED )
decoding_context - > flags | = CODEC_FLAG_TRUNCATED ; // we do not send complete frames
2015-08-20 21:46:28 -04:00
if ( avcodec_open2 ( decoding_context , decoding_codec , NULL ) < 0 )
throw ( " AV codec open action failed! " ) ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
decoding_frame_buffer = avcodec_alloc_frame ( ) ; //(AVFrame*)malloc(sizeof(AVFrame)) ;
2015-08-23 21:08:27 -04:00
2015-08-23 10:27:50 -04:00
//ret = av_image_alloc(decoding_frame_buffer->data, decoding_frame_buffer->linesize, decoding_context->width, decoding_context->height, decoding_context->pix_fmt, 32);
//if (ret < 0)
//throw std::runtime_error("AV: Could not allocate raw picture buffer");
2015-08-20 22:59:51 -04:00
// debug
# ifdef DEBUG_MPEG_VIDEO
std : : cerr < < " Dumping captured data to file tmpvideo.mpg " < < std : : endl ;
encoding_debug_file = fopen ( " tmpvideo.mpg " , " w " ) ;
# endif
2015-08-19 21:50:51 -04:00
}
FFmpegVideo : : ~ FFmpegVideo ( )
{
2015-08-19 22:17:50 -04:00
avcodec_close ( encoding_context ) ;
avcodec_close ( decoding_context ) ;
av_free ( encoding_context ) ;
av_free ( decoding_context ) ;
av_freep ( & encoding_frame_buffer - > data [ 0 ] ) ;
av_freep ( & decoding_frame_buffer - > data [ 0 ] ) ;
free ( encoding_frame_buffer ) ;
free ( decoding_frame_buffer ) ;
2015-08-19 21:50:51 -04:00
}
bool FFmpegVideo : : encodeData ( const QImage & image , uint32_t size_hint , RsVOIPDataChunk & voip_chunk )
{
2015-08-23 21:08:27 -04:00
std : : cerr < < " Encoding frame of size " < < image . width ( ) < < " x " < < image . height ( ) < < " , resized to " < < encoding_frame_buffer - > width < < " x " < < encoding_frame_buffer - > height < < std : : endl ;
2015-08-19 22:17:50 -04:00
QImage input ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
if ( image . width ( ) ! = encoding_frame_buffer - > width | | image . height ( ) ! = encoding_frame_buffer - > height )
input = image . scaled ( QSize ( encoding_frame_buffer - > width , encoding_frame_buffer - > height ) , Qt : : IgnoreAspectRatio , Qt : : SmoothTransformation ) ;
else
input = image ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
/* prepare a dummy image */
/* Y */
2015-08-23 21:08:27 -04:00
for ( int y = 0 ; y < encoding_context - > height / 2 ; y + + )
for ( int x = 0 ; x < encoding_context - > width / 2 ; x + + )
2015-08-19 22:17:50 -04:00
{
2015-08-23 21:08:27 -04:00
QRgb pix00 = input . pixel ( QPoint ( 2 * x + 0 , 2 * y + 0 ) ) ;
QRgb pix01 = input . pixel ( QPoint ( 2 * x + 0 , 2 * y + 1 ) ) ;
QRgb pix10 = input . pixel ( QPoint ( 2 * x + 1 , 2 * y + 0 ) ) ;
QRgb pix11 = input . pixel ( QPoint ( 2 * x + 1 , 2 * y + 1 ) ) ;
int R00 = ( pix00 > > 16 ) & 0xff ; int G00 = ( pix00 > > 8 ) & 0xff ; int B00 = ( pix00 > > 0 ) & 0xff ;
int R01 = ( pix01 > > 16 ) & 0xff ; int G01 = ( pix01 > > 8 ) & 0xff ; int B01 = ( pix01 > > 0 ) & 0xff ;
int R10 = ( pix10 > > 16 ) & 0xff ; int G10 = ( pix10 > > 8 ) & 0xff ; int B10 = ( pix10 > > 0 ) & 0xff ;
int R11 = ( pix11 > > 16 ) & 0xff ; int G11 = ( pix11 > > 8 ) & 0xff ; int B11 = ( pix11 > > 0 ) & 0xff ;
int Y00 = ( 0.257 * R00 ) + ( 0.504 * G00 ) + ( 0.098 * B00 ) + 16 ;
int Y01 = ( 0.257 * R01 ) + ( 0.504 * G01 ) + ( 0.098 * B01 ) + 16 ;
int Y10 = ( 0.257 * R10 ) + ( 0.504 * G10 ) + ( 0.098 * B10 ) + 16 ;
int Y11 = ( 0.257 * R11 ) + ( 0.504 * G11 ) + ( 0.098 * B11 ) + 16 ;
float R = 0.25 * ( R00 + R01 + R10 + R11 ) ;
float G = 0.25 * ( G00 + G01 + G10 + G11 ) ;
float B = 0.25 * ( B00 + B01 + B10 + B11 ) ;
2015-08-20 22:59:51 -04:00
int U = ( 0.439 * R ) - ( 0.368 * G ) - ( 0.071 * B ) + 128 ;
int V = - ( 0.148 * R ) - ( 0.291 * G ) + ( 0.439 * B ) + 128 ;
2015-08-19 21:50:51 -04:00
2015-08-23 21:08:27 -04:00
encoding_frame_buffer - > data [ 0 ] [ ( 2 * y + 0 ) * encoding_frame_buffer - > linesize [ 0 ] + 2 * x + 0 ] = std : : min ( 255 , std : : max ( 0 , Y00 ) ) ; // Y
encoding_frame_buffer - > data [ 0 ] [ ( 2 * y + 0 ) * encoding_frame_buffer - > linesize [ 0 ] + 2 * x + 1 ] = std : : min ( 255 , std : : max ( 0 , Y01 ) ) ; // Y
encoding_frame_buffer - > data [ 0 ] [ ( 2 * y + 1 ) * encoding_frame_buffer - > linesize [ 0 ] + 2 * x + 0 ] = std : : min ( 255 , std : : max ( 0 , Y10 ) ) ; // Y
encoding_frame_buffer - > data [ 0 ] [ ( 2 * y + 1 ) * encoding_frame_buffer - > linesize [ 0 ] + 2 * x + 1 ] = std : : min ( 255 , std : : max ( 0 , Y11 ) ) ; // Y
2015-08-20 22:59:51 -04:00
encoding_frame_buffer - > data [ 1 ] [ y * encoding_frame_buffer - > linesize [ 1 ] + x ] = std : : min ( 255 , std : : max ( 0 , U ) ) ; // Cr
encoding_frame_buffer - > data [ 2 ] [ y * encoding_frame_buffer - > linesize [ 2 ] + x ] = std : : min ( 255 , std : : max ( 0 , V ) ) ; // Cb
2015-08-19 22:17:50 -04:00
}
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
encoding_frame_buffer - > pts = encoding_frame_count + + ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
/* encode the image */
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
int got_output = 0 ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
AVFrame * frame = encoding_frame_buffer ;
2015-08-19 21:50:51 -04:00
2015-08-23 10:27:50 -04:00
AVPacket pkt ;
av_init_packet ( & pkt ) ;
pkt . data = NULL ; // packet data will be allocated by the encoder
pkt . size = 0 ;
2015-08-19 22:17:50 -04:00
// do
// {
int ret = avcodec_encode_video2 ( encoding_context , & pkt , frame , & got_output ) ;
2015-08-19 21:50:51 -04:00
2015-08-19 22:17:50 -04:00
if ( ret < 0 )
{
std : : cerr < < " Error encoding frame! " < < std : : endl ;
return false ;
}
// frame = NULL ; // next attempts: do not encode anything. Do this to just flush the buffer
//
// } while(got_output) ;
if ( got_output )
2015-08-23 21:08:27 -04:00
{
voip_chunk . data = malloc ( pkt . size + HEADER_SIZE ) ;
uint32_t flags = 0 ;
2015-08-19 22:17:50 -04:00
2015-08-23 21:08:27 -04:00
( ( unsigned char * ) voip_chunk . data ) [ 0 ] = VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 1 ] = ( VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO > > 8 ) & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 2 ] = flags & 0xff ;
( ( unsigned char * ) voip_chunk . data ) [ 3 ] = ( flags > > 8 ) & 0xff ;
2015-08-20 21:46:28 -04:00
2015-08-23 21:08:27 -04:00
memcpy ( & ( ( unsigned char * ) voip_chunk . data ) [ HEADER_SIZE ] , pkt . data , pkt . size ) ;
2015-08-20 21:46:28 -04:00
2015-08-23 21:08:27 -04:00
voip_chunk . size = pkt . size + HEADER_SIZE ;
voip_chunk . type = RsVOIPDataChunk : : RS_VOIP_DATA_TYPE_VIDEO ;
std : : cerr < < " Output : " < < pkt . size < < " bytes. " < < std : : endl ;
2015-08-20 22:59:51 -04:00
# ifdef DEBUG_MPEG_VIDEO
2015-08-23 21:08:27 -04:00
fwrite ( pkt . data , 1 , pkt . size , encoding_debug_file ) ;
fflush ( encoding_debug_file ) ;
2015-08-20 22:59:51 -04:00
# endif
2015-08-23 21:08:27 -04:00
av_free_packet ( & pkt ) ;
return true ;
}
2015-08-19 22:17:50 -04:00
else
{
voip_chunk . data = NULL ;
voip_chunk . size = 0 ;
voip_chunk . type = RsVOIPDataChunk : : RS_VOIP_DATA_TYPE_VIDEO ;
std : : cerr < < " No output produced. " < < std : : endl ;
2015-08-23 21:08:27 -04:00
return false ;
2015-08-19 22:17:50 -04:00
}
2015-08-19 21:50:51 -04:00
}
bool FFmpegVideo : : decodeData ( const RsVOIPDataChunk & chunk , QImage & image )
{
2015-08-23 10:27:50 -04:00
std : : cerr < < " Decoding data of size " < < chunk . size < < std : : endl ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
AVPacket pkt ;
av_init_packet ( & pkt ) ;
pkt . data = ( unsigned char * ) memalign ( 16 , chunk . size - HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE ) ;
memset ( pkt . data , 0 , chunk . size - HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE ) ;
memcpy ( pkt . data , & static_cast < unsigned char * > ( chunk . data ) [ HEADER_SIZE ] , chunk . size - HEADER_SIZE ) ;
pkt . size = chunk . size - HEADER_SIZE ;
pkt . pts = AV_NOPTS_VALUE ;
pkt . dts = AV_NOPTS_VALUE ;
int got_frame = 0 ;
int len = avcodec_decode_video2 ( decoding_context , decoding_frame_buffer , & got_frame , & pkt ) ;
2015-08-20 21:46:28 -04:00
2015-08-23 10:27:50 -04:00
if ( len < 0 )
{
std : : cerr < < " Error decodign frame! " < < std : : endl ;
return false ;
}
2015-08-23 21:08:27 -04:00
std : : cerr < < " Used " < < len < < " bytes out of " < < pkt . size < < " . got_frame = " < < got_frame < < std : : endl ;
2015-08-23 10:27:50 -04:00
if ( ! got_frame )
return false ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
assert ( pkt . size > 0 ) ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
av_free_packet ( & pkt ) ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
image = QImage ( QSize ( decoding_frame_buffer - > width , decoding_frame_buffer - > height ) , QImage : : Format_ARGB32 ) ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
std : : cerr < < " Decoded frame. Size= " < < image . width ( ) < < " x " < < image . height ( ) < < std : : endl ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
for ( int y = 0 ; y < decoding_frame_buffer - > height ; y + + )
for ( int x = 0 ; x < decoding_frame_buffer - > width ; x + + )
{
int Y = decoding_frame_buffer - > data [ 0 ] [ y * decoding_frame_buffer - > linesize [ 0 ] + x ] ;
int U = decoding_frame_buffer - > data [ 1 ] [ y * decoding_frame_buffer - > linesize [ 1 ] + x ] ;
int V = decoding_frame_buffer - > data [ 2 ] [ y * decoding_frame_buffer - > linesize [ 2 ] + x ] ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
int R = std : : min ( 255 , std : : max ( 0 , ( int ) ( 1.164 * ( Y - 16 ) + 1.596 * ( V - 128 ) ) ) ) ;
int G = std : : min ( 255 , std : : max ( 0 , ( int ) ( 1.164 * ( Y - 16 ) - 0.813 * ( V - 128 ) - 0.391 * ( U - 128 ) ) ) ) ;
int B = std : : min ( 255 , std : : max ( 0 , ( int ) ( 1.164 * ( Y - 16 ) + 2.018 * ( U - 128 ) ) ) ) ;
2015-08-20 22:59:51 -04:00
2015-08-23 10:27:50 -04:00
image . setPixel ( QPoint ( x , y ) , QRgb ( 0xff000000 + ( R < < 16 ) + ( G < < 8 ) + B ) ) ;
}
2015-08-19 22:17:50 -04:00
2015-08-23 10:27:50 -04:00
return true ;
2015-08-19 21:50:51 -04:00
}