/******************************************************************************* * plugins/VOIP/gui/VideoProcessor.cpp * * * * Copyright (C) 2012 by Retroshare Team * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * * * *******************************************************************************/ #include #include #ifdef __MACH__ #include #else #include #endif #include #include #include #include "util/rsmemory.h" #include "VideoProcessor.h" #include "QVideoDevice.h" #include #include extern "C" { #include #include //#include #include #include //#include //#include } //#define DEBUG_MPEG_VIDEO 1 #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 #ifndef MINGW #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)) //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 #endif // MINGW VideoProcessor::VideoProcessor() :_encoded_frame_size(640,480) { //_lastTimeToShowFrame = time(NULL); _decoded_output_device = NULL ; //_encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO; _encoding_current_codec = VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO; _estimated_bandwidth_in = 0 ; _estimated_bandwidth_out = 0 ; _target_bandwidth_out = 30*1024 ; // 30 KB/s _total_encoded_size_in = 0 ; _total_encoded_size_out = 0 ; _last_bw_estimate_in_TS = time(NULL) ; _last_bw_estimate_out_TS = time(NULL) ; } VideoProcessor::~VideoProcessor() { // clear encoding queue while(!_encoded_out_queue.empty()) { _encoded_out_queue.tail().clear(); _encoded_out_queue.pop() ; } } bool VideoProcessor::processImage(const QImage& img) { VideoCodec *codec ; switch(_encoding_current_codec) { case VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO: codec = &_jpeg_video_codec ; break ; case VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO: codec = &_mpeg_video_codec ; break ; default: codec = NULL ; } // std::cerr << "reducing to " << _frame_size.width() << " x " << _frame_size.height() << std::endl; if(codec) { codec->encodeData(img.scaled(_encoded_frame_size,Qt::IgnoreAspectRatio,Qt::SmoothTransformation),_target_bandwidth_out, _encoded_out_queue); time_t now = time(NULL) ; if(now > _last_bw_estimate_out_TS) { _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 } return true ; } else { std::cerr << "No codec for codec ID = " << _encoding_current_codec << ". Please call VideoProcessor::setCurrentCodec()" << std::endl; return false ; } } bool VideoProcessor::nextEncodedPacket(RsVOIPDataChunk& chunk) { return _encoded_out_queue.pop(chunk) && ((_total_encoded_size_out += chunk.size), true); } void VideoProcessor::setInternalFrameSize(QSize s) { _encoded_frame_size = s ; } void VideoProcessor::receiveEncodedData(const RsVOIPDataChunk& chunk) { static const int HEADER_SIZE = 4 ; // read frame type. Use first 4 bytes to give info about content. // // 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) ; //uint16_t flags = ((unsigned char *)chunk.data)[2] + (((unsigned char *)chunk.data)[3] << 8) ; VideoCodec *codec ; switch(codid) { case VIDEO_PROCESSOR_CODEC_ID_JPEG_VIDEO: codec = &_jpeg_video_codec ; break ; case VIDEO_PROCESSOR_CODEC_ID_MPEG_VIDEO: codec = &_mpeg_video_codec ; break ; default: codec = NULL ; } if(codec == NULL) { std::cerr << "Unknown decoding codec: " << codid << std::endl; return ; } { _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))) ; _total_encoded_size_in = 0 ; _last_bw_estimate_in_TS = now ; #ifdef DEBUG_VIDEO_OUTPUT_DEVICE std::cerr << "new bw estimate (in): " << _estimated_bandwidth_in << std::endl; #endif } } QImage img ; if(!codec->decodeData(chunk,img)) { std::cerr << "No image decoded. Probably in the middle of something..." << std::endl; return ; } if(_decoded_output_device) // if (time(NULL) > _lastTimeToShowFrame) // { _decoded_output_device->showFrame(img) ; // _lastTimeToShowFrame = time(NULL) ;//+ 1000/25; #warning \plugins\VOIP\gui\VideoProcessor.cpp:210 Phenom: TODO: Get CPU usage to pass image. // } } void VideoProcessor::setMaximumBandwidth(uint32_t bytes_per_sec) { std::cerr << "Video Encoder: maximum frame rate is set to " << bytes_per_sec << " Bps" << std::endl; _target_bandwidth_out = bytes_per_sec ; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// JPEGVideo::JPEGVideo() : _encoded_ref_frame_max_distance(10),_encoded_ref_frame_count(10) { } 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) ; // un-compress image data QByteArray qb((char*)&((uint8_t*)chunk.data)[HEADER_SIZE],(int)chunk.size - HEADER_SIZE) ; if(!image.loadFromData(qb,"JPEG")) { std::cerr << "image.loadFromData(): returned an error.: " << std::endl; return false ; } if(flags & JPEG_VIDEO_FLAGS_DIFFERENTIAL_FRAME) { if(_decoded_reference_frame.size() != image.size()) { std::cerr << "Bad reference frame!" << std::endl; return false ; } QImage res = _decoded_reference_frame ; for(int i=0;i& dst) { // check if we make a diff image, or if we use the full frame. if(dst.full()) return false; RsVOIPDataChunk& voip_chunk = dst.head(); QImage encoded_frame ; bool differential_frame ; if (_encoded_ref_frame_count++ < _encoded_ref_frame_max_distance && image.size() == _encoded_reference_frame.size() && image.byteCount() == _encoded_reference_frame.byteCount()) { // compute difference with reference frame. encoded_frame = image ; for(int i=0;i> 8) & 0xff ; ((unsigned char *)voip_chunk.data)[2] = flags & 0xff ; ((unsigned char *)voip_chunk.data)[3] = (flags >> 8) & 0xff ; memcpy(&((unsigned char*)voip_chunk.data)[HEADER_SIZE],qb.data(),qb.size()) ; voip_chunk.size = HEADER_SIZE + qb.size() ; voip_chunk.type = RsVOIPDataChunk::RS_VOIP_DATA_TYPE_VIDEO ; dst.push(); return true ; } FFmpegVideo::FFmpegVideo() { #if LIBAVCODEC_VERSION_MAJOR < 58 avcodec_register_all(); #endif // Encoding encoding_codec = NULL ; encoding_frame_buffer = NULL ; encoding_context = NULL ; //AVCodecID codec_id = AV_CODEC_ID_H264 ; //AVCodecID codec_id = AV_CODEC_ID_MPEG2VIDEO; #if LIBAVCODEC_VERSION_MAJOR < 54 CodecID codec_id = CODEC_ID_MPEG4; #else AVCodecID codec_id = AV_CODEC_ID_MPEG4; #endif /* find the video encoder */ encoding_codec = avcodec_find_encoder(codec_id); 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 ") ; encoding_context = avcodec_alloc_context3(encoding_codec); 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"); /* put sample parameters */ encoding_context->bit_rate = 10*1024 ; // default bitrate is 30KB/s encoding_context->bit_rate_tolerance = encoding_context->bit_rate ; #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 encoding_context->rc_min_rate = 0; encoding_context->rc_max_rate = 0; encoding_context->rc_buffer_size = 0; #endif #if LIBAVCODEC_VERSION_MAJOR < 58 if (encoding_codec->capabilities & AV_CODEC_CAP_TRUNCATED) encoding_context->flags |= AV_CODEC_FLAG_TRUNCATED; #endif encoding_context->flags |= AV_CODEC_FLAG_PSNR;//Peak signal-to-noise ratio encoding_context->flags |= AV_CODEC_CAP_PARAM_CHANGE; 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; encoding_context->qmin = 1; encoding_context->qmax = 51; encoding_context->max_qdiff = 4; //encoding_context->me_method = ME_HEX; //encoding_context->max_b_frames = 4; //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; /* resolution must be a multiple of two */ encoding_context->width = 640;//176; encoding_context->height = 480;//144; /* frames per second */ encoding_context->time_base = av_make_q(1, 25); /* 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 */ encoding_context->gop_size = 100; //encoding_context->max_b_frames = 1; #if LIBAVCODEC_VERSION_MAJOR < 54 encoding_context->pix_fmt = PIX_FMT_YUV420P; //context->pix_fmt = PIX_FMT_RGB24; if (codec_id == CODEC_ID_H264) #else encoding_context->pix_fmt = AV_PIX_FMT_YUV420P; //context->pix_fmt = AV_PIX_FMT_RGB24; if (codec_id == AV_CODEC_ID_H264) #endif { av_opt_set(encoding_context->priv_data, "preset", "slow", 0); } /* open it */ if (avcodec_open2(encoding_context, encoding_codec, NULL) < 0) { std::cerr << "AV: Could not open codec context. Something's wrong." << std::endl; throw std::runtime_error( "AV: Could not open codec context. Something's wrong."); } #if (LIBAVCODEC_VERSION_MAJOR < 57) | (LIBAVCODEC_VERSION_MAJOR == 57 && LIBAVCODEC_VERSION_MINOR <3 ) encoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ; #else encoding_frame_buffer = av_frame_alloc() ; #endif if(!encoding_frame_buffer) std::cerr << "AV: could not allocate frame buffer." << std::endl; if(!encoding_frame_buffer) throw std::runtime_error("AV: could not allocate frame buffer.") ; encoding_frame_buffer->format = encoding_context->pix_fmt; encoding_frame_buffer->width = encoding_context->width; encoding_frame_buffer->height = encoding_context->height; /* 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 */ int ret = av_image_alloc(encoding_frame_buffer->data, encoding_frame_buffer->linesize, encoding_context->width, encoding_context->height, encoding_context->pix_fmt, 32); 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"); encoding_frame_count = 0 ; // Decoding decoding_codec = avcodec_find_decoder(codec_id); if (!decoding_codec) std::cerr << "AV codec not found for codec id " << std::endl; if (!decoding_codec) throw("AV codec not found for codec id ") ; decoding_context = avcodec_alloc_context3(decoding_codec); if(!decoding_context) std::cerr << "AV: Could not allocate video codec decoding context" << std::endl; if(!decoding_context) throw std::runtime_error("AV: Could not allocate video codec decoding context"); decoding_context->width = encoding_context->width; decoding_context->height = encoding_context->height; #if LIBAVCODEC_VERSION_MAJOR < 54 decoding_context->pix_fmt = PIX_FMT_YUV420P; #else decoding_context->pix_fmt = AV_PIX_FMT_YUV420P; #endif #if LIBAVCODEC_VERSION_MAJOR < 58 if(decoding_codec->capabilities & AV_CODEC_CAP_TRUNCATED) decoding_context->flags |= AV_CODEC_FLAG_TRUNCATED; // we do not send complete frames #endif //we can receive truncated frames decoding_context->flags2 |= AV_CODEC_FLAG2_CHUNKS; AVDictionary* dictionary = NULL; if(avcodec_open2(decoding_context, decoding_codec, &dictionary) < 0) { std::cerr << "AV codec open action failed! " << std::endl; throw("AV codec open action failed! ") ; } //decoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ; decoding_frame_buffer = av_frame_alloc() ; av_init_packet(&decoding_buffer); decoding_buffer.data = NULL ; decoding_buffer.size = 0 ; //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"); // debug #ifdef DEBUG_MPEG_VIDEO std::cerr << "Dumping captured data to file tmpvideo.mpg" << std::endl; encoding_debug_file = fopen("tmpvideo.mpg","w") ; #endif } FFmpegVideo::~FFmpegVideo() { avcodec_free_context(&encoding_context); avcodec_free_context(&decoding_context); av_frame_free(&encoding_frame_buffer); av_frame_free(&decoding_frame_buffer); } #define MAX_FFMPEG_ENCODING_BITRATE 81920 static void imageToFrame(AVFrame *frame, const QImage& image) { QImage input; if(image.width() == frame->width && image.height() == frame->height) input = image; else input = image.scaled(QSize(frame->width, frame->height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); for (int y = 0; y < frame->height - 1; y += 2) for (int x = 0; x < frame->width - 1; x += 2) { QRgb pix00 = input.pixel(QPoint(x+0,y+0)); QRgb pix10 = input.pixel(QPoint(x+1,y+0)); QRgb pix01 = input.pixel(QPoint(x+0,y+1)); QRgb pix11 = input.pixel(QPoint(x+1,y+1)); int R00 = pix00>>16 & 0xff, G00 = pix00>>8 & 0xff, B00 = pix00>>0 & 0xff; int R10 = pix10>>16 & 0xff, G10 = pix10>>8 & 0xff, B10 = pix10>>0 & 0xff; int R01 = pix01>>16 & 0xff, G01 = pix01>>8 & 0xff, B01 = pix01>>0 & 0xff; int R11 = pix11>>16 & 0xff, G11 = pix11>>8 & 0xff, B11 = pix11>>0 & 0xff; float R = 0.25*(R00+R01+R10+R11); float G = 0.25*(G00+G01+G10+G11); float B = 0.25*(B00+B01+B10+B11); int Y00 = (0.257 * R00) + (0.504 * G00) + (0.098 * B00) + 16.5; int Y01 = (0.257 * R01) + (0.504 * G01) + (0.098 * B01) + 16.5; int Y10 = (0.257 * R10) + (0.504 * G10) + (0.098 * B10) + 16.5; int Y11 = (0.257 * R11) + (0.504 * G11) + (0.098 * B11) + 16.5; int U = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128.5 ; int V = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128.5 ; frame->data[0][(y+0) * frame->linesize[0] + x+0] = Y00; // Y frame->data[0][(y+0) * frame->linesize[0] + x+1] = Y01; // Y frame->data[0][(y+1) * frame->linesize[0] + x+0] = Y10; // Y frame->data[0][(y+1) * frame->linesize[0] + x+1] = Y11; // Y frame->data[1][y/2 * frame->linesize[1] + x/2] = U; // Cr frame->data[2][y/2 * frame->linesize[2] + x/2] = V; // Cb } } static void frameToImage(QImage& image, const AVFrame *frame) { image = QImage(QSize(frame->width,frame->height),QImage::Format_ARGB32); #ifdef DEBUG_MPEG_VIDEO std::cerr << "Decoded frame. Size=" << image.width() << "x" << image.height() << std::endl; #endif for (int y = 0; y < frame->height; y++) for (int x = 0; x < frame->width; x++) { int Y = frame->data[0][y * frame->linesize[0] + x]; int U = frame->data[1][(y/2) * frame->linesize[1] + x/2]; int V = frame->data[2][(y/2) * frame->linesize[2] + x/2]; int B = (int)(1.164*Y + 1.596*V - 222.421); int G = (int)(1.164*Y - 0.391*U - 0.813*V + 136.076); int R = (int)(1.164*Y + 2.018*U - 277.336); B = std::max(0,std::min(255,B)); G = std::max(0,std::min(255,G)); R = std::max(0,std::min(255,R)); image.setPixel(QPoint(x,y), QRgb(0xff000000 | (R << 16) | (G << 8) | B)); } } bool FFmpegVideo::encodeData(const QImage& image, uint32_t target_encoding_bitrate, NetQueue& dst) { #if LIBAVCODEC_VERSION_MAJOR < 58 if(dst.full()) return false; #endif #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 << " : "; #endif 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 ; } //encoding_context->bit_rate = target_encoding_bitrate; encoding_context->rc_max_rate = target_encoding_bitrate; //encoding_context->bit_rate_tolerance = target_encoding_bitrate; imageToFrame(encoding_frame_buffer, image); encoding_frame_buffer->pts = encoding_frame_count++; /* encode the image */ AVPacket pkt ; av_init_packet(&pkt); #if LIBAVCODEC_VERSION_MAJOR < 54 pkt.size = avpicture_get_size(encoding_context->pix_fmt, encoding_context->width, encoding_context->height); pkt.data = (uint8_t*)av_malloc(pkt.size); // do // { int got_output = 0; int ret = avcodec_encode_video(encoding_context, pkt.data, pkt.size, encoding_frame_buffer) ; if (ret > 0) { got_output = ret; } #elif LIBAVCODEC_VERSION_MAJOR < 58 pkt.data = NULL; // packet data will be allocated by the encoder pkt.size = 0; // do // { int got_output = 0; int ret = avcodec_encode_video2(encoding_context, &pkt, encoding_frame_buffer, &got_output) ; #else int ret = avcodec_send_frame(encoding_context, encoding_frame_buffer); if(ret == AVERROR(EAGAIN)) ret = 0; #endif if (ret < 0) { std::cerr << "Error encoding frame!" << std::endl; goto fail; } // frame = NULL ; // next attempts: do not encode anything. Do this to just flush the buffer // // } while(got_output) ; #if LIBAVCODEC_VERSION_MAJOR >= 58 while(!dst.full() && !(ret = avcodec_receive_packet(encoding_context, &pkt))) #else if(!got_output) { std::cerr << "No output produced." << std::endl; } else #endif { RsVOIPDataChunk& voip_chunk = dst.head(); voip_chunk.data = rs_malloc(pkt.size + HEADER_SIZE) ; if(!voip_chunk.data) { std::cerr << "error: failed to allocate RsVOIPDataChunk data" << std::endl; goto fail; } uint32_t flags = 0; ((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 ; memcpy(&((unsigned char*)voip_chunk.data)[HEADER_SIZE],pkt.data,pkt.size) ; voip_chunk.size = pkt.size + HEADER_SIZE; voip_chunk.type = RsVOIPDataChunk::RS_VOIP_DATA_TYPE_VIDEO ; dst.push(); #ifdef DEBUG_MPEG_VIDEO std::cerr << "Output : " << pkt.size << " bytes." << std::endl; fwrite(pkt.data,1,pkt.size,encoding_debug_file) ; fflush(encoding_debug_file) ; #endif } #if LIBAVCODEC_VERSION_MAJOR >= 58 if(ret == AVERROR(EAGAIN)) ret = 0; else if(ret < 0) { std::cerr << "error: failed to get packet from encoder: " << ret << std::endl; } #endif fail: #if LIBAVCODEC_VERSION_MAJOR < 58 av_free_packet(&pkt); #else av_packet_unref(&pkt); #endif return ret == 0; } bool FFmpegVideo::decodeData(const RsVOIPDataChunk& chunk, QImage& image) { #ifdef DEBUG_MPEG_VIDEO std::cerr << "Decoding data of size " << chunk.size << std::endl; std::cerr << "Allocating new buffer of size " << chunk.size - HEADER_SIZE << std::endl; #endif uint32_t s = chunk.size - HEADER_SIZE ; unsigned char *tmp = (unsigned char *)av_malloc(s + AV_INPUT_BUFFER_PADDING_SIZE); if (tmp == NULL) { std::cerr << "FFmpegVideo::decodeData() Unable to allocate new buffer of size " << s << std::endl; return false; } /* copy chunk data without header to new buffer */ memcpy(tmp, &((unsigned char*)chunk.data)[HEADER_SIZE], s); /* 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) ; av_packet_from_data(&decoding_buffer, tmp, s); #if LIBAVCODEC_VERSION_MAJOR >= 58 int ret = avcodec_send_packet(decoding_context, &decoding_buffer); av_packet_unref(&decoding_buffer); if(ret) { // EAGAIN is an error here because the decoder's buffer should be empty std::cerr << "error: failed to send packet to decoder: " << ret << std::endl; } ret = avcodec_receive_frame(decoding_context, decoding_frame_buffer); if(ret == AVERROR(EAGAIN)) { std::cerr << "warning: " << "decoder consumed a packet but didn't generate a frame" << std::endl; return false; } else if(ret) { std::cerr << "error: failed to fetch frame from decoder: " << ret << std::endl; return false ; } frameToImage(image, decoding_frame_buffer); #ifdef DEBUG_MPEG_VIDEO std::cerr << "debug(MPEG_VIDEO): decoded frame: size=" << image.width() << "x" << image.height() << std::endl; #endif // Clear out the internal buffer and drop the frames to avoid latency. // Video decoders typically only create one frame per packet, so this // shouldn't happen under normal circumstances. // Ideally, we would render the most recent frame and discard earlier ones, // but that would require copying of frames that is not worth it. while(!avcodec_receive_frame(decoding_context, decoding_frame_buffer)) { std::cerr << "warning: dropping decoded frame"; } #else int got_frame = 1 ; while (decoding_buffer.size > 0 || (!decoding_buffer.data && got_frame)) { int len = avcodec_decode_video2(decoding_context,decoding_frame_buffer,&got_frame,&decoding_buffer) ; if (len < 0) { std::cerr << "Error decoding frame! Return=" << len << std::endl; return false ; } decoding_buffer.data += len; decoding_buffer.size -= len; if(got_frame) { frameToImage(image, decoding_frame_buffer); #ifdef DEBUG_MPEG_VIDEO std::cerr << "Decoded frame. Size=" << image.width() << "x" << image.height() << std::endl; #endif } } /* flush the decoder */ decoding_buffer.data = NULL; decoding_buffer.size = 0; //avcodec_decode_video2(decoding_context,decoding_frame_buffer,&got_frame,&decoding_buffer) ; #endif return true ; }