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-19 21:50:51 -04:00
# include <math.h>
2015-09-03 10:33:50 -04:00
# include <time.h>
2015-08-19 21:50:51 -04:00
extern " C " {
# include <libavcodec/avcodec.h>
# include <libavutil/opt.h>
2015-09-04 08:51:05 -04:00
//#include <libavutil/channel_layout.h>
2015-09-05 16:21:49 -04:00
# include <libavutil/mem.h>
2015-08-19 21:50:51 -04:00
# include <libavutil/imgutils.h>
2015-09-04 08:51:05 -04:00
//#include <libavutil/mathematics.h>
//#include <libavutil/samplefmt.h>
2015-08-19 21:50:51 -04:00
}
2015-08-24 21:42:01 -04:00
//#define DEBUG_MPEG_VIDEO 1
2015-08-19 21:50:51 -04:00
2015-10-03 22:08:24 -04:00
# ifndef AV_INPUT_BUFFER_PADDING_SIZE
# ifndef FF_INPUT_BUFFER_PADDING_SIZE
# define AV_INPUT_BUFFER_PADDING_SIZE 32
# else
# define AV_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE
# endif
# endif
2015-10-13 13:17:25 -04:00
# if (LIBAVUTIL_VERSION_MAJOR == 54) && (LIBAVUTIL_VERSION_MINOR == 3) && (LIBAVUTIL_VERSION_MICRO == 0)
//Ubuntu Vivid use other version of rational.h than GIT with LIBAVUTIL_VERSION_MICRO == 0
# define VIVID_RATIONAL_H_VERSION 1
# endif
# if (VIVID_RATIONAL_H_VERSION) || (LIBAVUTIL_VERSION_MAJOR < 52) || ((LIBAVUTIL_VERSION_MAJOR == 52) && (LIBAVUTIL_VERSION_MINOR < 63))
2015-10-03 22:08:24 -04:00
//since https://github.com/FFmpeg/FFmpeg/commit/3532dd52c51f3d4b95f31d1b195e64a04a8aea5d
static inline AVRational av_make_q ( int num , int den )
{
AVRational r = { num , den } ;
return r ;
}
# endif
# if (LIBAVUTIL_VERSION_MAJOR < 55) || ((LIBAVUTIL_VERSION_MAJOR == 55) && (LIBAVUTIL_VERSION_MINOR < 52))
//since https://github.com/FFmpeg/FFmpeg/commit/fd056029f45a9f6d213d9fce8165632042511d4f
void avcodec_free_context ( AVCodecContext * * pavctx )
{
AVCodecContext * avctx = * pavctx ;
if ( ! avctx )
return ;
avcodec_close ( avctx ) ;
av_freep ( & avctx - > extradata ) ;
av_freep ( & avctx - > subtitle_header ) ;
av_freep ( pavctx ) ;
}
# endif
# if (LIBAVUTIL_VERSION_MAJOR < 57) || ((LIBAVUTIL_VERSION_MAJOR == 57) && (LIBAVUTIL_VERSION_MINOR < 52))
//Since https://github.com/FFmpeg/FFmpeg/commit/7ecc2d403ce5c7b6ea3b1f368dccefd105209c7e
static void get_frame_defaults ( AVFrame * frame )
{
if ( frame - > extended_data ! = frame - > data )
av_freep ( & frame - > extended_data ) ;
return ;
frame - > extended_data = NULL ;
get_frame_defaults ( frame ) ;
return ;
}
AVFrame * av_frame_alloc ( void )
{
AVFrame * frame = ( AVFrame * ) av_mallocz ( sizeof ( * frame ) ) ;
if ( ! frame )
return NULL ;
get_frame_defaults ( frame ) ;
return frame ;
}
void av_frame_free ( AVFrame * * frame )
{
if ( ! frame | | ! * frame )
return ;
av_freep ( frame ) ;
}
# endif
2015-08-14 16:44:20 -04:00
VideoProcessor : : VideoProcessor ( )
2015-08-31 22:41:38 -04:00
: _encoded_frame_size ( 640 , 480 ) , vpMtx ( " VideoProcessor " )
2014-07-20 16:50:36 -04:00
{
2015-08-24 21:42:01 -04:00
_decoded_output_device = NULL ;
2015-10-03 22:08:24 -04:00
2015-08-19 21:50:51 -04:00
//_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO;
_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO ;
2015-10-03 22:08:24 -04:00
2015-08-24 21:15:33 -04:00
_estimated_bandwidth_in = 0 ;
_estimated_bandwidth_out = 0 ;
2015-08-29 21:48:58 -04:00
_target_bandwidth_out = 30 * 1024 ; // 30 KB/s
2015-10-03 22:08:24 -04:00
2015-08-24 21:15:33 -04:00
_total_encoded_size_in = 0 ;
_total_encoded_size_out = 0 ;
2015-10-03 22:08:24 -04:00
2015-08-24 21:15:33 -04:00
_last_bw_estimate_in_TS = time ( NULL ) ;
_last_bw_estimate_out_TS = time ( NULL ) ;
2014-07-20 16:50:36 -04:00
}
2014-07-13 09:57:25 -04:00
2015-08-24 21:42:01 -04:00
VideoProcessor : : ~ VideoProcessor ( )
{
// clear encoding queue
2015-10-03 22:08:24 -04:00
2015-08-24 21:42:01 -04:00
RS_STACK_MUTEX ( vpMtx ) ;
2015-10-03 22:08:24 -04:00
2015-08-24 21:42:01 -04:00
while ( ! _encoded_out_queue . empty ( ) )
{
_encoded_out_queue . back ( ) . clear ( ) ;
_encoded_out_queue . pop_back ( ) ;
}
}
2015-08-29 21:48:58 -04:00
bool VideoProcessor : : processImage ( const QImage & img )
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 ;
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;
if ( codec )
{
RsVOIPDataChunk chunk ;
2015-08-29 21:48:58 -04:00
if ( codec - > encodeData ( img . scaled ( _encoded_frame_size , Qt : : IgnoreAspectRatio , Qt : : SmoothTransformation ) , _target_bandwidth_out , chunk ) & & chunk . size > 0 )
2015-08-24 21:15:33 -04:00
{
2015-08-24 21:42:01 -04:00
RS_STACK_MUTEX ( vpMtx ) ;
2015-08-24 21:15:33 -04:00
_encoded_out_queue . push_back ( chunk ) ;
_total_encoded_size_out + = chunk . size ;
}
time_t now = time ( NULL ) ;
if ( now > _last_bw_estimate_out_TS )
{
2015-08-24 21:42:01 -04:00
RS_STACK_MUTEX ( vpMtx ) ;
2015-10-03 22:08:24 -04:00
2015-08-24 21:15:33 -04:00
_estimated_bandwidth_out = uint32_t ( 0.75 * _estimated_bandwidth_out + 0.25 * ( _total_encoded_size_out / ( float ) ( now - _last_bw_estimate_out_TS ) ) ) ;
_total_encoded_size_out = 0 ;
_last_bw_estimate_out_TS = now ;
# ifdef DEBUG_VIDEO_OUTPUT_DEVICE
std : : cerr < < " new bw estimate: " < < _estimated_bw < < std : : endl ;
# endif
}
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-10-03 22:08:24 -04:00
RS_STACK_MUTEX ( vpMtx ) ;
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-10-03 22:08:24 -04:00
//
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 ;
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-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-10-03 22:08:24 -04:00
2015-08-24 21:15:33 -04:00
{
2015-08-24 21:42:01 -04:00
RS_STACK_MUTEX ( vpMtx ) ;
_total_encoded_size_in + = chunk . size ;
time_t now = time ( NULL ) ;
if ( now > _last_bw_estimate_in_TS )
{
_estimated_bandwidth_in = uint32_t ( 0.75 * _estimated_bandwidth_in + 0.25 * ( _total_encoded_size_in / ( float ) ( now - _last_bw_estimate_in_TS ) ) ) ;
2015-08-24 21:15:33 -04:00
2015-08-24 21:42:01 -04:00
_total_encoded_size_in = 0 ;
_last_bw_estimate_in_TS = now ;
2015-08-24 21:15:33 -04:00
# ifdef DEBUG_VIDEO_OUTPUT_DEVICE
2015-08-24 21:42:01 -04:00
std : : cerr < < " new bw estimate (in): " < < _estimated_bandwidth_in < < std : : endl ;
2015-08-24 21:15:33 -04:00
# endif
2015-10-03 22:08:24 -04:00
}
2015-08-24 21:42:01 -04:00
}
2015-10-03 22:08:24 -04:00
QImage img ;
if ( ! codec - > decodeData ( chunk , img ) )
2015-08-23 10:27:50 -04:00
{
std : : cerr < < " No image decoded. Probably in the middle of something... " < < std : : endl ;
return ;
}
if ( _decoded_output_device )
2015-10-03 22:08:24 -04:00
_decoded_output_device - > showFrame ( img ) ;
2015-08-14 16:44:20 -04:00
}
2015-08-24 21:15:33 -04:00
void VideoProcessor : : setMaximumBandwidth ( uint32_t bytes_per_sec )
2015-08-14 16:44:20 -04:00
{
std : : cerr < < " Video Encoder: maximum frame rate is set to " < < bytes_per_sec < < " Bps " < < std : : endl ;
2015-08-24 21:15:33 -04:00
_target_bandwidth_out = bytes_per_sec ;
2015-08-14 16:44:20 -04:00
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
JPEGVideo : : JPEGVideo ( )
2015-10-03 22:08:24 -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 ) ;
2015-10-03 22:08:24 -04:00
2015-08-14 16:44:20 -04:00
assert ( codec = = VideoProcessor : : VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO ) ;
2015-10-03 22:08:24 -04:00
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-10-03 22:08:24 -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-10-03 22:08:24 -04:00
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-10-03 22:08:24 -04:00
if ( _encoded_ref_frame_count + + < _encoded_ref_frame_max_distance
& & image . size ( ) = = _encoded_reference_frame . size ( )
& & image . byteCount ( ) = = _encoded_reference_frame . byteCount ( ) )
{
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 ;
2015-10-03 22:08:24 -04:00
_encoded_reference_frame = image . copy ( ) ;
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-19 21:50:51 -04:00
FFmpegVideo : : FFmpegVideo ( )
{
2015-10-03 22:08:24 -04:00
avcodec_register_all ( ) ;
// Encoding
2015-08-19 22:17:50 -04:00
encoding_codec = NULL ;
encoding_frame_buffer = NULL ;
encoding_context = NULL ;
2015-10-03 22:08:24 -04:00
//AVCodecID codec_id = AV_CODEC_ID_H264 ;
2015-08-31 22:41:38 -04:00
//AVCodecID codec_id = AV_CODEC_ID_MPEG2VIDEO;
2015-09-05 09:44:10 -04:00
# if LIBAVCODEC_VERSION_MAJOR < 54
2015-09-04 17:59:13 -04:00
CodecID codec_id = CODEC_ID_MPEG4 ;
# else
2015-08-31 22:41:38 -04:00
AVCodecID codec_id = AV_CODEC_ID_MPEG4 ;
2015-09-04 17:59:13 -04:00
# endif
2015-08-19 21:50:51 -04:00
2015-10-03 22:08:24 -04:00
/* find the video encoder */
2015-08-19 22:17:50 -04:00
encoding_codec = avcodec_find_encoder ( codec_id ) ;
2015-10-03 22:08:24 -04:00
if ( ! encoding_codec ) std : : cerr < < " AV codec not found for codec id " < < std : : endl ;
if ( ! encoding_codec ) throw std : : runtime_error ( " 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-10-03 22:08:24 -04:00
if ( ! encoding_context ) std : : cerr < < " AV: Could not allocate video codec encoding context " < < std : : endl ;
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-31 22:41:38 -04:00
encoding_context - > bit_rate = 10 * 1024 ; // default bitrate is 30KB/s
2015-08-29 21:48:58 -04:00
encoding_context - > bit_rate_tolerance = encoding_context - > bit_rate ;
2015-10-03 22:08:24 -04:00
2015-08-31 22:41:38 -04:00
# ifdef USE_VARIABLE_BITRATE
encoding_context - > rc_min_rate = 0 ;
encoding_context - > rc_max_rate = 10 * 1024 ; //encoding_context->bit_rate;
encoding_context - > rc_buffer_size = 10 * 1024 * 1024 ;
encoding_context - > rc_initial_buffer_occupancy = ( int ) ( 0.9 * encoding_context - > rc_buffer_size ) ;
encoding_context - > rc_max_available_vbv_use = 1.0 ;
encoding_context - > rc_min_vbv_overflow_use = 0.0 ;
# else
2015-08-29 21:48:58 -04:00
encoding_context - > rc_min_rate = 0 ;
2015-08-31 22:41:38 -04:00
encoding_context - > rc_max_rate = 0 ;
encoding_context - > rc_buffer_size = 0 ;
# endif
2015-10-03 22:08:24 -04:00
if ( encoding_codec - > capabilities & CODEC_CAP_TRUNCATED )
encoding_context - > flags | = CODEC_FLAG_TRUNCATED ;
encoding_context - > flags | = CODEC_FLAG_PSNR ; //Peak signal-to-noise ratio
2015-08-31 22:41:38 -04:00
encoding_context - > flags | = CODEC_CAP_PARAM_CHANGE ;
2015-08-29 21:48:58 -04:00
encoding_context - > i_quant_factor = 0.769f ;
encoding_context - > b_quant_factor = 1.4f ;
encoding_context - > time_base . num = 1 ;
encoding_context - > time_base . den = 15 ; //framesPerSecond;
2015-08-31 22:41:38 -04:00
encoding_context - > qmin = 1 ;
2015-08-29 21:48:58 -04:00
encoding_context - > qmax = 51 ;
encoding_context - > max_qdiff = 4 ;
2015-10-03 22:08:24 -04:00
2015-08-31 22:41:38 -04:00
//encoding_context->me_method = ME_HEX;
2015-10-03 22:08:24 -04:00
//encoding_context->max_b_frames = 4;
2015-08-31 22:41:38 -04:00
//encoding_context->flags |= CODEC_FLAG_LOW_DELAY; // MPEG2 only
//encoding_context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_P4X4 | X264_PART_B8X8;
//encoding_context->crf = 0.0f;
//encoding_context->cqp = 26;
2015-10-03 22:08:24 -04:00
2015-08-19 21:50:51 -04:00
/* resolution must be a multiple of two */
2015-08-31 22:41:38 -04:00
encoding_context - > width = 640 ; //176;
encoding_context - > height = 480 ; //144;
2015-08-19 21:50:51 -04:00
/* frames per second */
2015-10-03 22:08:24 -04:00
encoding_context - > time_base = av_make_q ( 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-31 22:41:38 -04:00
encoding_context - > gop_size = 100 ;
//encoding_context->max_b_frames = 1;
2015-09-05 09:44:10 -04:00
# if LIBAVCODEC_VERSION_MAJOR < 54
2015-09-04 17:59:13 -04:00
encoding_context - > pix_fmt = PIX_FMT_YUV420P ; //context->pix_fmt = PIX_FMT_RGB24;
if ( codec_id = = CODEC_ID_H264 ) {
# else
2015-08-29 21:48:58 -04:00
encoding_context - > pix_fmt = AV_PIX_FMT_YUV420P ; //context->pix_fmt = AV_PIX_FMT_RGB24;
2015-09-04 17:59:13 -04:00
if ( codec_id = = AV_CODEC_ID_H264 ) {
# endif
2015-08-19 22:17:50 -04:00
av_opt_set ( encoding_context - > priv_data , " preset " , " slow " , 0 ) ;
2015-09-04 17:59:13 -04:00
}
2015-08-19 21:50:51 -04:00
/* open it */
2015-10-03 22:08:24 -04:00
if ( avcodec_open2 ( encoding_context , encoding_codec , NULL ) < 0 )
{
std : : cerr < < " AV: Could not open codec context. Something's wrong. " < < std : : endl ;
2015-08-19 21:50:51 -04:00
throw std : : runtime_error ( " AV: Could not open codec context. Something's wrong. " ) ;
2015-10-03 22:08:24 -04:00
}
2015-08-19 21:50:51 -04:00
2015-10-03 22:08:24 -04:00
# if (LIBAVCODEC_VERSION_MAJOR < 57) | (LIBAVCODEC_VERSION_MAJOR == 57 && LIBAVCODEC_VERSION_MINOR <3 )
2015-08-20 21:46:28 -04:00
encoding_frame_buffer = avcodec_alloc_frame ( ) ; //(AVFrame*)malloc(sizeof(AVFrame)) ;
2015-10-03 22:08:24 -04:00
# else
encoding_frame_buffer = av_frame_alloc ( ) ;
# endif
if ( ! encoding_frame_buffer ) std : : cerr < < " AV: could not allocate frame buffer. " < < std : : endl ;
2015-08-23 21:08:27 -04:00
if ( ! encoding_frame_buffer )
throw std : : runtime_error ( " AV: could not allocate frame buffer. " ) ;
2015-10-03 22:08:24 -04:00
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-10-03 22:08:24 -04:00
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-10-03 22:08:24 -04:00
if ( ret < 0 ) std : : cerr < < " AV: Could not allocate raw picture buffer " < < std : : endl ;
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 ;
2015-10-03 22:08:24 -04:00
2015-08-19 22:17:50 -04:00
// Decoding
decoding_codec = avcodec_find_decoder ( codec_id ) ;
2015-10-03 22:08:24 -04:00
if ( ! decoding_codec ) std : : cerr < < " AV codec not found for codec id " < < std : : endl ;
if ( ! decoding_codec )
2015-08-19 22:17:50 -04:00
throw ( " AV codec not found for codec id " ) ;
2015-10-03 22:08:24 -04:00
2015-08-19 22:17:50 -04:00
decoding_context = avcodec_alloc_context3 ( decoding_codec ) ;
2015-10-03 22:08:24 -04:00
if ( ! decoding_context ) std : : cerr < < " AV: Could not allocate video codec decoding context " < < std : : endl ;
if ( ! decoding_context )
2015-08-23 10:27:50 -04:00
throw std : : runtime_error ( " AV: Could not allocate video codec decoding context " ) ;
2015-10-03 22:08:24 -04:00
2015-08-20 22:59:51 -04:00
decoding_context - > width = encoding_context - > width ;
decoding_context - > height = encoding_context - > height ;
2015-09-05 09:44:10 -04:00
# if LIBAVCODEC_VERSION_MAJOR < 54
2015-09-04 17:59:13 -04:00
decoding_context - > pix_fmt = PIX_FMT_YUV420P ;
# else
2015-08-20 22:59:51 -04:00
decoding_context - > pix_fmt = AV_PIX_FMT_YUV420P ;
2015-09-04 17:59:13 -04:00
# endif
2015-10-03 22:08:24 -04:00
2015-08-23 10:27:50 -04:00
if ( decoding_codec - > capabilities & CODEC_CAP_TRUNCATED )
2015-10-03 22:08:24 -04:00
decoding_context - > flags | = CODEC_FLAG_TRUNCATED ; // we do not send complete frames
//we can receive truncated frames
decoding_context - > flags2 | = CODEC_FLAG2_CHUNKS ;
AVDictionary * dictionary = NULL ;
if ( avcodec_open2 ( decoding_context , decoding_codec , & dictionary ) < 0 )
{
std : : cerr < < " AV codec open action failed! " < < std : : endl ;
2015-08-20 21:46:28 -04:00
throw ( " AV codec open action failed! " ) ;
2015-10-03 22:08:24 -04:00
}
//decoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ;
decoding_frame_buffer = av_frame_alloc ( ) ;
2015-08-23 22:39:16 -04:00
av_init_packet ( & decoding_buffer ) ;
decoding_buffer . data = NULL ;
decoding_buffer . size = 0 ;
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);
2015-10-03 22:08:24 -04:00
//if (ret < 0)
//throw std::runtime_error("AV: Could not allocate raw picture buffer");
2015-08-20 22:59:51 -04:00
// debug
2015-10-03 22:08:24 -04:00
# ifdef DEBUG_MPEG_VIDEO
2015-08-20 22:59:51 -04:00
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
}
2015-10-03 22:08:24 -04:00
2015-08-19 21:50:51 -04:00
FFmpegVideo : : ~ FFmpegVideo ( )
{
2015-10-03 22:08:24 -04:00
avcodec_free_context ( & encoding_context ) ;
avcodec_free_context ( & decoding_context ) ;
av_frame_free ( & encoding_frame_buffer ) ;
av_frame_free ( & decoding_frame_buffer ) ;
2015-08-19 21:50:51 -04:00
}
2015-08-29 21:48:58 -04:00
# define MAX_FFMPEG_ENCODING_BITRATE 81920
2015-08-19 21:50:51 -04:00
2015-08-29 21:48:58 -04:00
bool FFmpegVideo : : encodeData ( const QImage & image , uint32_t target_encoding_bitrate , RsVOIPDataChunk & voip_chunk )
2015-08-19 21:50:51 -04:00
{
2015-10-03 22:08:24 -04:00
# ifdef DEBUG_MPEG_VIDEO
std : : cerr < < " Encoding frame of size " < < image . width ( ) < < " x " < < image . height ( ) < < " , resized to " < < encoding_frame_buffer - > width < < " x " < < encoding_frame_buffer - > height < < " : " ;
2015-08-23 22:58:36 -04:00
# endif
2015-08-19 22:17:50 -04:00
QImage input ;
2015-10-03 22:08:24 -04:00
if ( target_encoding_bitrate > MAX_FFMPEG_ENCODING_BITRATE )
{
std : : cerr < < " Max encodign bitrate eexceeded. Capping to " < < MAX_FFMPEG_ENCODING_BITRATE < < std : : endl ;
target_encoding_bitrate = MAX_FFMPEG_ENCODING_BITRATE ;
}
2015-08-31 22:41:38 -04:00
//encoding_context->bit_rate = target_encoding_bitrate;
encoding_context - > rc_max_rate = target_encoding_bitrate ;
//encoding_context->bit_rate_tolerance = target_encoding_bitrate;
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-10-03 22:08:24 -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 ;
2015-10-03 22:08:24 -04:00
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-10-03 22:08:24 -04:00
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-23 10:27:50 -04:00
AVPacket pkt ;
av_init_packet ( & pkt ) ;
2015-09-05 16:21:49 -04:00
# if LIBAVCODEC_VERSION_MAJOR < 54
2015-09-04 17:42:26 -04:00
pkt . size = avpicture_get_size ( encoding_context - > pix_fmt , encoding_context - > width , encoding_context - > height ) ;
pkt . data = ( uint8_t * ) av_malloc ( pkt . size ) ;
// do
// {
2015-10-03 22:08:24 -04:00
int ret = avcodec_encode_video ( encoding_context , pkt . data , pkt . size , encoding_frame_buffer ) ;
2015-09-04 17:42:26 -04:00
if ( ret > 0 ) {
got_output = ret ;
}
# else
2015-08-23 10:27:50 -04:00
pkt . data = NULL ; // packet data will be allocated by the encoder
pkt . size = 0 ;
2015-08-19 22:17:50 -04:00
// do
// {
2015-10-03 22:08:24 -04:00
int ret = avcodec_encode_video2 ( encoding_context , & pkt , encoding_frame_buffer , & got_output ) ;
2015-09-04 17:42:26 -04:00
# endif
2015-08-19 21:50:51 -04:00
2015-10-03 22:08:24 -04:00
if ( ret < 0 )
2015-08-19 22:17:50 -04:00
{
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 ;
2015-10-03 22:08:24 -04:00
# ifdef DEBUG_MPEG_VIDEO
2015-08-23 22:58:36 -04:00
std : : cerr < < " Output : " < < pkt . size < < " bytes. " < < std : : endl ;
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 ) ;
2015-10-03 22:08:24 -04:00
return true ;
2015-08-23 21:08:27 -04:00
}
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-10-03 22:08:24 -04:00
return false ;
2015-08-19 22:17:50 -04:00
}
2015-08-19 21:50:51 -04:00
}
2015-08-29 21:48:58 -04:00
2015-08-19 21:50:51 -04:00
bool FFmpegVideo : : decodeData ( const RsVOIPDataChunk & chunk , QImage & image )
{
2015-08-23 22:58:36 -04:00
# ifdef DEBUG_MPEG_VIDEO
2015-08-23 22:39:16 -04:00
std : : cerr < < " Decoding data of size " < < chunk . size < < std : : endl ;
2015-10-03 22:08:24 -04:00
std : : cerr < < " Allocating new buffer of size " < < chunk . size - HEADER_SIZE < < std : : endl ;
2015-08-23 22:58:36 -04:00
# endif
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
uint32_t s = chunk . size - HEADER_SIZE ;
# if defined(__MINGW32__)
unsigned char * tmp = ( unsigned char * ) _aligned_malloc ( s + AV_INPUT_BUFFER_PADDING_SIZE , 16 ) ;
# else
unsigned char * tmp = ( unsigned char * ) memalign ( 16 , s + AV_INPUT_BUFFER_PADDING_SIZE ) ;
# endif //MINGW
if ( tmp = = NULL ) {
std : : cerr < < " FFmpegVideo::decodeData() Unable to allocate new buffer of size " < < s < < std : : endl ;
return false ;
2015-08-23 22:39:16 -04:00
}
2015-10-03 22:08:24 -04:00
/* copy chunk data without header to new buffer */
memcpy ( tmp , & ( ( unsigned char * ) chunk . data ) [ HEADER_SIZE ] , s ) ;
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
/* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
memset ( & tmp [ s ] , 0 , AV_INPUT_BUFFER_PADDING_SIZE ) ;
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
decoding_buffer . size = s ;
decoding_buffer . data = tmp ;
int got_frame = 1 ;
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
while ( decoding_buffer . size > 0 | | ( ! decoding_buffer . data & & got_frame ) ) {
int len = avcodec_decode_video2 ( decoding_context , decoding_frame_buffer , & got_frame , & decoding_buffer ) ;
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
if ( len < 0 )
{
std : : cerr < < " Error decoding frame! Return= " < < len < < std : : endl ;
return false ;
}
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
decoding_buffer . data + = len ;
decoding_buffer . size - = len ;
2015-08-20 22:59:51 -04:00
2015-10-03 22:08:24 -04:00
if ( got_frame )
{
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 22:58:36 -04:00
# ifdef DEBUG_MPEG_VIDEO
2015-10-03 22:08:24 -04:00
std : : cerr < < " Decoded frame. Size= " < < image . width ( ) < < " x " < < image . height ( ) < < std : : endl ;
2015-08-23 22:58:36 -04:00
# endif
2015-08-19 22:17:50 -04:00
2015-10-03 22:08:24 -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 / 2 ) * decoding_frame_buffer - > linesize [ 1 ] + x / 2 ] ;
int V = decoding_frame_buffer - > data [ 2 ] [ ( y / 2 ) * decoding_frame_buffer - > linesize [ 2 ] + x / 2 ] ;
2015-08-23 22:39:16 -04:00
2015-10-03 22:08:24 -04:00
int B = 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 R = std : : min ( 255 , std : : max ( 0 , ( int ) ( 1.164 * ( Y - 16 ) + 2.018 * ( U - 128 ) ) ) ) ;
2015-08-23 22:39:16 -04:00
2015-10-03 22:08:24 -04:00
image . setPixel ( QPoint ( x , y ) , QRgb ( 0xff000000 + ( R < < 16 ) + ( G < < 8 ) + B ) ) ;
}
}
2015-08-23 22:39:16 -04:00
}
2015-10-03 22:08:24 -04:00
/* flush the decoder */
decoding_buffer . data = NULL ;
decoding_buffer . size = 0 ;
//avcodec_decode_video2(decoding_context,decoding_frame_buffer,&got_frame,&decoding_buffer) ;
2015-08-23 22:39:16 -04:00
return true ;
2015-08-19 21:50:51 -04:00
}