From e01b865616b1e7d5dbf623bf2124803ca85e0d53 Mon Sep 17 00:00:00 2001 From: "hlundin@google.com" Date: Mon, 13 Jun 2011 07:02:25 +0000 Subject: [PATCH] Implement Copy method for VP8 decoder Use get/set reference frames to realize a decoder cloning. Must also inject the latest keyframe. Note: this CL does not work with the Bali release of libvpx. Must apply the bug fix in commit fbea3728. Review URL: http://webrtc-codereview.appspot.com/32004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@67 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../codecs/vp8/main/interface/vp8.h | 15 ++ .../codecs/vp8/main/source/vp8.cc | 184 +++++++++++++++++- 2 files changed, 193 insertions(+), 6 deletions(-) diff --git a/modules/video_coding/codecs/vp8/main/interface/vp8.h b/modules/video_coding/codecs/vp8/main/interface/vp8.h index b2a916d82..073547f84 100644 --- a/modules/video_coding/codecs/vp8/main/interface/vp8.h +++ b/modules/video_coding/codecs/vp8/main/interface/vp8.h @@ -24,6 +24,7 @@ typedef struct vpx_codec_ctx vpx_codec_ctx_t; typedef struct vpx_codec_ctx vpx_dec_ctx_t; typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t; typedef struct vpx_image vpx_image_t; +typedef struct vpx_ref_frame vpx_ref_frame_t; namespace webrtc { @@ -214,12 +215,26 @@ public: virtual WebRtc_Word32 Reset(); virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/) { return -1; } +// Create a copy of the codec and its internal state. +// +// Return value : A copy of the instance if OK, NULL otherwise. + virtual VideoDecoder* Copy(); + private: +// Copy reference image from this _decoder to the _decoder in copyTo. Set which +// frame type to copy in _refFrame->frame_type before the call to this function. + int CopyReference(VP8Decoder* copyTo); + RawImage _decodedImage; DecodedImageCallback* _decodeCompleteCallback; bool _inited; bool _feedbackModeOn; vpx_dec_ctx_t* _decoder; + VideoCodec* _inst; + WebRtc_Word32 _numCores; + EncodedImage _lastKeyFrame; + int _imageFormat; + vpx_ref_frame_t* _refFrame; };// end of VP8Decoder class diff --git a/modules/video_coding/codecs/vp8/main/source/vp8.cc b/modules/video_coding/codecs/vp8/main/source/vp8.cc index 7eebce8fa..d7d7c1d91 100644 --- a/modules/video_coding/codecs/vp8/main/source/vp8.cc +++ b/modules/video_coding/codecs/vp8/main/source/vp8.cc @@ -250,10 +250,6 @@ VP8Encoder::InitEncode(const VideoCodec* inst, } _encodedImage._size = (3 * inst->width * inst->height) >> 1; _encodedImage._buffer = new WebRtc_UWord8[_encodedImage._size]; - if (_encodedImage._buffer == NULL) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } vpx_img_alloc(_raw, IMG_FMT_I420, inst->width, inst->height, 1); // populate encoder configuration with default values @@ -590,9 +586,15 @@ VP8Encoder::RegisterEncodeCompleteCallback(EncodedImageCallback* callback) } VP8Decoder::VP8Decoder(): + _decodeCompleteCallback(NULL), _inited(false), _feedbackModeOn(false), - _decoder(NULL) + _decoder(NULL), + _inst(NULL), + _numCores(1), + _lastKeyFrame(), + _imageFormat(VPX_IMG_FMT_NONE), + _refFrame(NULL) { } @@ -615,7 +617,7 @@ VP8Decoder::Reset() WebRtc_Word32 VP8Decoder::InitDecode(const VideoCodec* inst, - WebRtc_Word32 numberOfCores) + WebRtc_Word32 numberOfCores) { vp8_postproc_cfg_t ppcfg; WebRtc_Word32 retVal = Release(); @@ -648,6 +650,17 @@ VP8Decoder::InitDecode(const VideoCodec* inst, //ppcfg.NoiseLevel = 1; //Noise intensity. Valid range: [0,7] vpx_codec_control(_decoder, VP8_SET_POSTPROC, &ppcfg); + // Save the VideoCodec instance for later; mainly for duplicating the decoder. + if (inst) + { + if (!_inst) + { + _inst = new VideoCodec; + } + *_inst = *inst; + } + _numCores = numberOfCores; + _inited = true; return WEBRTC_VIDEO_CODEC_OK; } @@ -721,6 +734,33 @@ VP8Decoder::Decode(const EncodedImage& inputImage, return WEBRTC_VIDEO_CODEC_ERROR; } + // Store encoded frame if key frame. (Used in Copy method.) + if (inputImage._frameType == kKeyFrame) + { + // Reduce size due to PictureID that we won't copy. + const int bytesToCopy = inputImage._length - numberOfBytes; + if (_lastKeyFrame._size < bytesToCopy) + { + delete [] _lastKeyFrame._buffer; + _lastKeyFrame._buffer = NULL; + _lastKeyFrame._size = 0; + } + + WebRtc_UWord8* tempBuffer = _lastKeyFrame._buffer; // Save buffer ptr. + _lastKeyFrame = inputImage; // Shallow copy. + _lastKeyFrame._buffer = tempBuffer; // Restore buffer ptr. + _lastKeyFrame._length = bytesToCopy; + if (!_lastKeyFrame._buffer) + { + // Allocate memory. + _lastKeyFrame._size = bytesToCopy; + _lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size]; + } + // Copy encoded frame. + memcpy(_lastKeyFrame._buffer, inputImage._buffer + numberOfBytes, + _lastKeyFrame._length); + } + int lastRefUpdates = 0; #ifdef DEV_PIC_LOSS if (vpx_codec_control(_decoder, VP8D_GET_LAST_REF_UPDATES, &lastRefUpdates)) @@ -776,6 +816,9 @@ VP8Decoder::Decode(const EncodedImage& inputImage, _decodedImage._timeStamp = inputImage._timeStamp; _decodeCompleteCallback->Decoded(_decodedImage); + // Remember image format for later + _imageFormat = img->fmt; + // we need to communicate that we should send a RPSI with a specific picture ID // TODO(pw): how do we know it's a golden or alt reference frame? libvpx will @@ -815,6 +858,11 @@ VP8Decoder::Release() delete [] _decodedImage._buffer; _decodedImage._buffer = NULL; } + if (_lastKeyFrame._buffer != NULL) + { + delete [] _lastKeyFrame._buffer; + _lastKeyFrame._buffer = NULL; + } if (_decoder != NULL) { if(vpx_codec_destroy(_decoder)) @@ -824,9 +872,133 @@ VP8Decoder::Release() delete _decoder; _decoder = NULL; } + if (_inst != NULL) + { + delete _inst; + _inst = NULL; + } + if (_refFrame != NULL) + { + vpx_img_free(&_refFrame->img); + delete _refFrame; + _refFrame = NULL; + } _inited = false; return WEBRTC_VIDEO_CODEC_OK; } +VideoDecoder* +VP8Decoder::Copy() +{ + // Sanity checks. + if (!_inited) + { + // Not initialized. + assert(false); + return NULL; + } + if (_decodedImage._buffer == NULL) + { + // Nothing has been decoded before; cannot clone. + assert(false); + return NULL; + } + if (_lastKeyFrame._buffer == NULL) + { + // Cannot clone if we have no key frame to start with. + assert(false); + return NULL; + } + + // Create a new VideoDecoder object + VP8Decoder *copyTo = new VP8Decoder; + + // Initialize the new decoder + if (copyTo->InitDecode(_inst, _numCores) != WEBRTC_VIDEO_CODEC_OK) + { + delete copyTo; + return NULL; + } + + // Inject last key frame into new decoder. + if (vpx_codec_decode(copyTo->_decoder, _lastKeyFrame._buffer, + _lastKeyFrame._length, NULL, VPX_DL_REALTIME)) + { + delete copyTo; + return NULL; + } + + // Allocate memory for reference image copy + assert(_decodedImage._width > 0); + assert(_decodedImage._height > 0); + assert(_imageFormat > VPX_IMG_FMT_NONE); + // Check if frame format has changed. + if (_refFrame && + (_decodedImage._width != _refFrame->img.d_w || + _decodedImage._height != _refFrame->img.d_h || + _imageFormat != _refFrame->img.fmt)) + { + vpx_img_free(&_refFrame->img); + delete _refFrame; + _refFrame = NULL; + } + + + if (!_refFrame) + { + _refFrame = new vpx_ref_frame_t; + + if(!vpx_img_alloc(&_refFrame->img, + static_cast(_imageFormat), + _decodedImage._width, _decodedImage._height, 1)) + { + assert(false); + delete copyTo; + return NULL; + } + } + + const vpx_ref_frame_type_t typeVec[] = { VP8_LAST_FRAME, VP8_GOLD_FRAME, + VP8_ALTR_FRAME }; + for (int ix = 0; ix < sizeof(typeVec) / sizeof(vpx_ref_frame_type_t); ++ix) + { + _refFrame->frame_type = typeVec[ix]; + if (CopyReference(copyTo) < 0) + { + delete copyTo; + return NULL; + } + } + + // Copy all member variables (that are not set in initialization). + copyTo->_feedbackModeOn = _feedbackModeOn; + copyTo->_imageFormat = _imageFormat; + copyTo->_lastKeyFrame = _lastKeyFrame; // Shallow copy. + // Allocate memory. (Discard copied _buffer pointer.) + copyTo->_lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size]; + memcpy(copyTo->_lastKeyFrame._buffer, _lastKeyFrame._buffer, + _lastKeyFrame._length); + + return static_cast(copyTo); } + +int VP8Decoder::CopyReference(VP8Decoder* copyTo) +{ + // The type of frame to copy should be set in _refFrame->frame_type + // before the call to this function. + if (vpx_codec_control(_decoder, VP8_COPY_REFERENCE, _refFrame) + != VPX_CODEC_OK) + { + return -1; + } + if (vpx_codec_control(copyTo->_decoder, VP8_SET_REFERENCE, _refFrame) + != VPX_CODEC_OK) + { + return -1; + } + return 0; +} + + +} // namespace webrtc