| /* |
| * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "rps_test.h" |
| |
| #include <assert.h> |
| #include <string.h> // memcmp |
| #include <time.h> |
| |
| #include "vp8.h" |
| |
| VP8RpsTest::VP8RpsTest(float bitRate) |
| : VP8NormalAsyncTest(bitRate), |
| decoder2_(new webrtc::VP8Decoder), |
| sli_(false) { |
| } |
| |
| VP8RpsTest::VP8RpsTest() |
| : VP8NormalAsyncTest("VP8 Reference Picture Selection Test", |
| "VP8 Reference Picture Selection Test", 1), |
| decoder2_(new webrtc::VP8Decoder), |
| sli_(false) { |
| } |
| |
| VP8RpsTest::~VP8RpsTest() { |
| if (decoder2_) { |
| decoder2_->Release(); |
| delete decoder2_; |
| } |
| decoded_frame2_.Free(); |
| } |
| |
| void VP8RpsTest::Perform() { |
| _inname = "test/testFiles/foreman_cif.yuv"; |
| CodecSettings(352, 288, 30, _bitRate); |
| Setup(); |
| _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); |
| _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); |
| decoded_frame2_.VerifyAndAllocate(_lengthSourceFrame); |
| |
| // Enable RPS functionality |
| _inst.codecSpecific.VP8.pictureLossIndicationOn = true; |
| _inst.codecSpecific.VP8.feedbackModeOn = true; |
| |
| if(_encoder->InitEncode(&_inst, 4, 1460) < 0) |
| exit(EXIT_FAILURE); |
| |
| _decoder->InitDecode(&_inst,1); |
| decoder2_->InitDecode(&_inst,1); |
| |
| FrameQueue frameQueue; |
| VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this); |
| RpsDecodeCompleteCallback decCallback(&_decodedVideoBuffer); |
| RpsDecodeCompleteCallback decCallback2(&decoded_frame2_); |
| _encoder->RegisterEncodeCompleteCallback(&encCallback); |
| _decoder->RegisterDecodeCompleteCallback(&decCallback); |
| decoder2_->RegisterDecodeCompleteCallback(&decCallback2); |
| |
| if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) |
| exit(EXIT_FAILURE); |
| |
| _totalEncodeTime = _totalDecodeTime = 0; |
| _totalEncodePipeTime = _totalDecodePipeTime = 0; |
| bool complete = false; |
| _framecnt = 0; |
| _encFrameCnt = 0; |
| _decFrameCnt = 0; |
| _sumEncBytes = 0; |
| _lengthEncFrame = 0; |
| double starttime = clock()/(double)CLOCKS_PER_SEC; |
| while (!complete) { |
| CodecSpecific_InitBitrate(); |
| complete = EncodeRps(&decCallback2); |
| if (!frameQueue.Empty() || complete) { |
| while (!frameQueue.Empty()) { |
| _frameToDecode = |
| static_cast<FrameQueueTuple *>(frameQueue.PopFrame()); |
| int lost = DoPacketLoss(); |
| if (lost == 2) { |
| // Lost the whole frame, continue |
| _missingFrames = true; |
| delete _frameToDecode; |
| _frameToDecode = NULL; |
| continue; |
| } |
| int ret = Decode(lost); |
| delete _frameToDecode; |
| _frameToDecode = NULL; |
| if (ret < 0) { |
| fprintf(stderr,"\n\nError in decoder: %d\n\n", ret); |
| exit(EXIT_FAILURE); |
| } |
| else if (ret == 0) { |
| _framecnt++; |
| } |
| else { |
| fprintf(stderr, |
| "\n\nPositive return value from decode!\n\n"); |
| } |
| } |
| } |
| } |
| double endtime = clock()/(double)CLOCKS_PER_SEC; |
| double totalExecutionTime = endtime - starttime; |
| printf("Total execution time: %.1f s\n", totalExecutionTime); |
| _sumEncBytes = encCallback.EncodedBytes(); |
| double actualBitRate = ActualBitRate(_encFrameCnt) / 1000.0; |
| double avgEncTime = _totalEncodeTime / _encFrameCnt; |
| double avgDecTime = _totalDecodeTime / _decFrameCnt; |
| printf("Actual bitrate: %f kbps\n", actualBitRate); |
| printf("Average encode time: %.1f ms\n", 1000 * avgEncTime); |
| printf("Average decode time: %.1f ms\n", 1000 * avgDecTime); |
| printf("Average encode pipeline time: %.1f ms\n", |
| 1000 * _totalEncodePipeTime / _encFrameCnt); |
| printf("Average decode pipeline time: %.1f ms\n", |
| 1000 * _totalDecodePipeTime / _decFrameCnt); |
| printf("Number of encoded frames: %u\n", _encFrameCnt); |
| printf("Number of decoded frames: %u\n", _decFrameCnt); |
| (*_log) << "Actual bitrate: " << actualBitRate << " kbps\tTarget: " << |
| _bitRate << " kbps" << std::endl; |
| (*_log) << "Average encode time: " << avgEncTime << " s" << std::endl; |
| (*_log) << "Average decode time: " << avgDecTime << " s" << std::endl; |
| _encoder->Release(); |
| _decoder->Release(); |
| Teardown(); |
| } |
| |
| bool VP8RpsTest::EncodeRps(RpsDecodeCompleteCallback* decodeCallback) { |
| _lengthEncFrame = 0; |
| size_t bytes_read = fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile); |
| if (bytes_read < _lengthSourceFrame) |
| return true; |
| _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); |
| _inputVideoBuffer.SetTimeStamp((unsigned int) |
| (_encFrameCnt * 9e4 / _inst.maxFramerate)); |
| _inputVideoBuffer.SetWidth(_inst.width); |
| _inputVideoBuffer.SetHeight(_inst.height); |
| webrtc::RawImage rawImage; |
| VideoBufferToRawImage(_inputVideoBuffer, rawImage); |
| if (feof(_sourceFile) != 0) { |
| return true; |
| } |
| _encodeCompleteTime = 0; |
| _encodeTimes[rawImage._timeStamp] = tGetTime(); |
| webrtc::VideoFrameType frameType = webrtc::kDeltaFrame; |
| |
| webrtc::CodecSpecificInfo* codecSpecificInfo = CreateEncoderSpecificInfo(); |
| codecSpecificInfo->codecSpecific.VP8.pictureIdRPSI = |
| decodeCallback->LastDecodedRefPictureId( |
| &codecSpecificInfo->codecSpecific.VP8.hasReceivedRPSI); |
| if (sli_) { |
| codecSpecificInfo->codecSpecific.VP8.pictureIdSLI = |
| decodeCallback->LastDecodedPictureId(); |
| codecSpecificInfo->codecSpecific.VP8.hasReceivedSLI = true; |
| sli_ = false; |
| } |
| printf("Encoding: %u\n", _framecnt); |
| int ret = _encoder->Encode(rawImage, codecSpecificInfo, &frameType); |
| if (ret < 0) |
| printf("Failed to encode: %u\n", _framecnt); |
| |
| if (codecSpecificInfo != NULL) { |
| delete codecSpecificInfo; |
| codecSpecificInfo = NULL; |
| } |
| if (_encodeCompleteTime > 0) { |
| _totalEncodeTime += _encodeCompleteTime - |
| _encodeTimes[rawImage._timeStamp]; |
| } |
| else { |
| _totalEncodeTime += tGetTime() - _encodeTimes[rawImage._timeStamp]; |
| } |
| return false; |
| } |
| |
| //#define FRAME_LOSS 1 |
| |
| int VP8RpsTest::Decode(int lossValue) { |
| _sumEncBytes += _frameToDecode->_frame->GetLength(); |
| webrtc::EncodedImage encodedImage; |
| VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage); |
| encodedImage._completeFrame = !lossValue; |
| _decodeCompleteTime = 0; |
| _decodeTimes[encodedImage._timeStamp] = clock()/(double)CLOCKS_PER_SEC; |
| int ret = _decoder->Decode(encodedImage, _missingFrames, NULL, |
| _frameToDecode->_codecSpecificInfo); |
| // Drop every 10th frame for the second decoder |
| #if FRAME_LOSS |
| if (_framecnt == 0 || _framecnt % 10 != 0) { |
| printf("Decoding: %u\n", _framecnt); |
| if (_framecnt > 1 && (_framecnt - 1) % 10 == 0) |
| _missingFrames = true; |
| #else |
| if (true) { |
| if (_framecnt > 0 && _framecnt % 10 == 0) { |
| encodedImage._length = std::rand() % encodedImage._length; |
| printf("Decoding with loss: %u\n", _framecnt); |
| } |
| else |
| printf("Decoding: %u\n", _framecnt); |
| #endif |
| int ret2 = decoder2_->Decode(encodedImage, _missingFrames, NULL, |
| _frameToDecode->_codecSpecificInfo, |
| 0 /* dummy */); |
| |
| // check return values |
| if (ret < 0 || ret2 < 0) { |
| return -1; |
| } else if (ret2 == WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI || |
| ret2 == WEBRTC_VIDEO_CODEC_REQUEST_SLI) { |
| sli_ = true; |
| } |
| |
| // compare decoded images |
| #if FRAME_LOSS |
| if (!_missingFrames) { |
| if (!CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), |
| _decodedVideoBuffer.GetLength(), |
| decoded_frame2_.GetBuffer(), _decodedVideoBuffer.GetLength())) { |
| fprintf(stderr,"\n\nRPS decoder different from master: %u\n\n", |
| _framecnt); |
| return -1; |
| } |
| } |
| #else |
| if (_framecnt > 0 && _framecnt % 10 != 0) { |
| if (!CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), |
| _decodedVideoBuffer.GetLength(), |
| decoded_frame2_.GetBuffer(), _decodedVideoBuffer.GetLength())) { |
| fprintf(stderr,"\n\nRPS decoder different from master: %u\n\n", |
| _framecnt); |
| return -1; |
| } |
| } |
| #endif |
| } |
| #if FRAME_LOSS |
| else |
| printf("Dropping %u\n", _framecnt); |
| #endif |
| _missingFrames = false; |
| return 0; |
| } |
| |
| |
| bool |
| VP8RpsTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes, |
| const void* ptrB, unsigned int bLengthBytes) { |
| if (aLengthBytes != bLengthBytes) |
| return false; |
| return memcmp(ptrA, ptrB, aLengthBytes) == 0; |
| } |
| |
| RpsDecodeCompleteCallback::RpsDecodeCompleteCallback(TestVideoBuffer* buffer) |
| : decoded_frame_(buffer), |
| decode_complete_(false), |
| last_decoded_picture_id_(0), |
| last_decoded_ref_picture_id_(0), |
| updated_ref_picture_id_(false) { |
| } |
| |
| WebRtc_Word32 RpsDecodeCompleteCallback::Decoded(webrtc::RawImage& image) { |
| decoded_frame_->VerifyAndAllocate(image._length); |
| decoded_frame_->CopyBuffer(image._length, image._buffer); |
| decoded_frame_->SetWidth(image._width); |
| decoded_frame_->SetHeight(image._height); |
| decoded_frame_->SetTimeStamp(image._timeStamp); |
| decode_complete_ = true; |
| return 0; |
| } |
| |
| bool RpsDecodeCompleteCallback::DecodeComplete() { |
| if (decode_complete_) |
| { |
| decode_complete_ = false; |
| return true; |
| } |
| return false; |
| } |
| |
| WebRtc_Word32 RpsDecodeCompleteCallback::ReceivedDecodedReferenceFrame( |
| const WebRtc_UWord64 picture_id) { |
| last_decoded_ref_picture_id_ = picture_id & 0x7FFF; |
| updated_ref_picture_id_ = true; |
| return 0; |
| } |
| |
| WebRtc_Word32 RpsDecodeCompleteCallback::ReceivedDecodedFrame( |
| const WebRtc_UWord64 picture_id) { |
| last_decoded_picture_id_ = picture_id & 0x3F; |
| return 0; |
| } |
| |
| WebRtc_UWord64 RpsDecodeCompleteCallback::LastDecodedPictureId() const { |
| return last_decoded_picture_id_; |
| } |
| |
| WebRtc_UWord64 RpsDecodeCompleteCallback::LastDecodedRefPictureId( |
| bool *updated) { |
| if (updated) |
| *updated = updated_ref_picture_id_; |
| updated_ref_picture_id_ = false; |
| return last_decoded_ref_picture_id_; |
| } |