git-svn-id: http://webrtc.googlecode.com/svn/trunk@156 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/video_coding/codecs/OWNERS b/src/modules/video_coding/codecs/OWNERS
new file mode 100644
index 0000000..30eee35
--- /dev/null
+++ b/src/modules/video_coding/codecs/OWNERS
@@ -0,0 +1,4 @@
+holmer@google.com
+mikhal@google.com
+marpan@google.com
+hlundin@google.com
diff --git a/src/modules/video_coding/codecs/i420/main/interface/i420.h b/src/modules/video_coding/codecs/i420/main/interface/i420.h
new file mode 100644
index 0000000..75c34d9
--- /dev/null
+++ b/src/modules/video_coding/codecs/i420/main/interface/i420.h
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_
+
+#include "video_codec_interface.h"
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+class I420Encoder : public VideoEncoder
+{
+public:
+
+ I420Encoder();
+
+ virtual ~I420Encoder();
+
+// Initialize the encoder with the information from the VideoCodec
+//
+// Input:
+// - codecSettings : Codec settings
+// - numberOfCores : Number of cores available for the encoder
+// - maxPayloadSize : The maximum size each payload is allowed
+// to have. Usually MTU - overhead.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Error
+ virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores*/, WebRtc_UWord32 /*maxPayloadSize*/);
+
+// "Encode" an I420 image (as a part of a video stream). The encoded image
+// will be returned to the user via the encode complete callback.
+//
+// Input:
+// - inputImage : Image to be encoded
+// - codecSpecificInfo : Pointer to codec specific data
+// - frameType : Frame type to be sent (Key /Delta) .
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Error
+ virtual WebRtc_Word32
+ Encode(const RawImage& inputImage,
+ const CodecSpecificInfo* /*codecSpecificInfo*/,
+ VideoFrameType /*frameType*/);
+
+// Register an encode complete callback object.
+//
+// Input:
+// - callback : Callback object which handles encoded images.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback* callback);
+
+// Free encoder memory.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Release();
+
+// Reset encoder state and prepare for a new call.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+// <0 - Error
+ virtual WebRtc_Word32 Reset();
+
+ virtual WebRtc_Word32 SetRates(WebRtc_UWord32 /*newBitRate*/, WebRtc_UWord32 /*frameRate*/) {return WEBRTC_VIDEO_CODEC_OK;}
+
+ virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 /*packetLoss*/){return WEBRTC_VIDEO_CODEC_OK;};
+
+ virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/){return WEBRTC_VIDEO_CODEC_OK;};
+
+// Get version number for the codec.
+//
+// Input:
+// - version : Pointer to allocated char buffer.
+// - length : Length of provided char buffer.
+//
+// Output:
+// - version : Version number string written to char buffer.
+//
+// Return value : >0 - Length of written string.
+// <0 - Error
+ static WebRtc_Word32 VersionStatic(WebRtc_Word8 *version, WebRtc_Word32 length);
+ virtual WebRtc_Word32 Version(WebRtc_Word8 *version, WebRtc_Word32 length) const;
+
+private:
+ bool _inited;
+ EncodedImage _encodedImage;
+ EncodedImageCallback* _encodedCompleteCallback;
+
+}; // end of WebRtcI420DEncoder class
+
+class I420Decoder : public VideoDecoder
+{
+public:
+
+ I420Decoder();
+
+ virtual ~I420Decoder();
+
+// Initialize the decoder.
+// The user must notify the codec of width and height values.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK.
+// <0 - Errors
+ virtual WebRtc_Word32 InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores*/);
+
+ virtual WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/){return WEBRTC_VIDEO_CODEC_OK;};
+
+// Decode encoded image (as a part of a video stream). The decoded image
+// will be returned to the user through the decode complete callback.
+//
+// Input:
+// - inputImage : Encoded image to be decoded
+// - missingFrames : True if one or more frames have been lost
+// since the previous decode call.
+// - codecSpecificInfo : pointer to specific codec data
+// - renderTimeMs : Render time in Ms
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Error
+ virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, bool missingFrames,
+ const void* /*codecSpecificInfo */, WebRtc_Word64 /*renderTimeMs*/);
+
+// Register a decode complete callback object.
+//
+// Input:
+// - callback : Callback object which handles decoded images.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback* callback);
+
+// Free decoder memory.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Error
+ virtual WebRtc_Word32 Release();
+
+// Reset decoder state and prepare for a new call.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK.
+// <0 - Error
+ virtual WebRtc_Word32 Reset();
+
+private:
+
+ RawImage _decodedImage;
+ WebRtc_Word32 _width;
+ WebRtc_Word32 _height;
+ bool _inited;
+ DecodedImageCallback* _decodeCompleteCallback;
+
+
+}; // end of WebRtcI420Decoder class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_
diff --git a/src/modules/video_coding/codecs/i420/main/source/Android.mk b/src/modules/video_coding/codecs/i420/main/source/Android.mk
new file mode 100644
index 0000000..c74f57e
--- /dev/null
+++ b/src/modules/video_coding/codecs/i420/main/source/Android.mk
@@ -0,0 +1,51 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_i420
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+LOCAL_SRC_FILES := i420.cc
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS :=
+MY_CFLAGS_C :=
+MY_DEFS := '-DNO_TCMALLOC' \
+ '-DNO_HEAPCHECKER' \
+ '-DWEBRTC_TARGET_PC' \
+ '-DWEBRTC_LINUX' \
+ '-DWEBRTC_THREAD_RR' \
+ '-DWEBRTC_ANDROID' \
+ '-DANDROID' \
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \
+ $(LOCAL_PATH)/../interface \
+ $(LOCAL_PATH)/../../../interface \
+ $(LOCAL_PATH)/../../../../../../system_wrappers/interface
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS :=
+
+LOCAL_LDFLAGS :=
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_SHARED_LIBRARIES := libcutils \
+ libdl \
+ libstlport
+LOCAL_ADDITIONAL_DEPENDENCIES :=
+
+include external/stlport/libstlport.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/modules/video_coding/codecs/i420/main/source/i420.cc b/src/modules/video_coding/codecs/i420/main/source/i420.cc
new file mode 100644
index 0000000..2f276f0
--- /dev/null
+++ b/src/modules/video_coding/codecs/i420/main/source/i420.cc
@@ -0,0 +1,281 @@
+/*
+ * 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 "i420.h"
+#include <string.h>
+
+namespace webrtc
+{
+
+I420Encoder::I420Encoder():
+_inited(false),
+_encodedImage(),
+_encodedCompleteCallback(NULL)
+{
+ //
+}
+
+I420Encoder::~I420Encoder()
+{
+ _inited = false;
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ _encodedImage._buffer = NULL;
+ }
+}
+
+WebRtc_Word32
+I420Encoder::VersionStatic(WebRtc_Word8* version, WebRtc_Word32 length)
+{
+ const WebRtc_Word8* str= "I420 version 1.1.0\n";
+ WebRtc_Word32 verLen = (WebRtc_Word32)strlen(str);
+ if(verLen > length)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ strncpy(version, str,length);
+ return verLen;
+}
+
+WebRtc_Word32
+I420Encoder::Version(WebRtc_Word8 *version, WebRtc_Word32 length) const
+{
+ return VersionStatic(version, length);
+}
+
+
+
+WebRtc_Word32
+I420Encoder::Release()
+{
+ // should allocate an encoded frame and then release it here, for that we actaully need an init flag
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ _encodedImage._buffer = NULL;
+ }
+ _inited = false;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+I420Encoder::Reset()
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+
+}
+
+WebRtc_Word32
+I420Encoder::InitEncode(const VideoCodec* codecSettings,
+ WebRtc_Word32 /*numberOfCores*/,
+ WebRtc_UWord32 /*maxPayloadSize */)
+{
+ if (codecSettings == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (codecSettings->width < 1 || codecSettings->height < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+
+ // allocating encoded memory
+
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ _encodedImage._buffer = NULL;
+ _encodedImage._size = 0;
+ }
+ const WebRtc_UWord32 newSize = (3 * codecSettings->width *
+ codecSettings->height) >> 1;
+ WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize];
+ if (newBuffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ _encodedImage._size = newSize;
+ _encodedImage._buffer = newBuffer;
+
+ // if no memory allocation, no point to init
+ _inited = true;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+
+WebRtc_Word32
+I420Encoder::Encode(const RawImage& inputImage,
+ const CodecSpecificInfo* /*codecSpecificInfo*/,
+ VideoFrameType /*frameTypes*/)
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (_encodedCompleteCallback == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ _encodedImage._frameType = kKeyFrame; // no coding
+ _encodedImage._timeStamp = inputImage._timeStamp;
+ _encodedImage._encodedHeight = inputImage._height;
+ _encodedImage._encodedWidth = inputImage._width;
+ if (inputImage._length > _encodedImage._size)
+ {
+
+ // allocating encoded memory
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ _encodedImage._buffer = NULL;
+ _encodedImage._size = 0;
+ }
+ const WebRtc_UWord32 newSize = (3 * _encodedImage._encodedWidth * _encodedImage._encodedHeight) >> 1;
+ WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize];
+ if (newBuffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ _encodedImage._size = newSize;
+ _encodedImage._buffer = newBuffer;
+ }
+ memcpy(_encodedImage._buffer, inputImage._buffer, inputImage._length);
+ _encodedImage._length = inputImage._length;
+ _encodedCompleteCallback->Encoded(_encodedImage);
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+WebRtc_Word32
+I420Encoder::RegisterEncodeCompleteCallback(EncodedImageCallback* callback)
+{
+ _encodedCompleteCallback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+I420Decoder::I420Decoder():
+_decodedImage(),
+_width(0),
+_height(0),
+_inited(false),
+_decodeCompleteCallback(NULL)
+{
+ //
+}
+
+I420Decoder::~I420Decoder()
+{
+ Release();
+}
+
+WebRtc_Word32
+I420Decoder::Reset()
+{
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+WebRtc_Word32
+I420Decoder::InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores */)
+{
+ if (codecSettings == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ else if (codecSettings->width < 1 || codecSettings->height < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ _width = codecSettings->width;
+ _height = codecSettings->height;
+ _inited = true;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+I420Decoder::Decode(const EncodedImage& inputImage, bool /*missingFrames*/, const void* /*codecSpecificInfo*/, WebRtc_Word64 /*renderTimeMs*/)
+{
+ if (inputImage._buffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (_decodeCompleteCallback == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (inputImage._length <= 0)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ //Allocate memory for decoded image
+
+ if (_decodedImage._buffer != NULL)
+ {
+ delete [] _decodedImage._buffer;
+ _decodedImage._buffer = NULL;
+ _decodedImage._size = 0;
+ }
+ if (_decodedImage._buffer == NULL)
+ {
+ const WebRtc_UWord32 newSize = (3*_width*_height) >> 1;
+ WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize];
+ if (newBuffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ _decodedImage._size = newSize;
+ _decodedImage._buffer = newBuffer;
+ }
+
+ // Set decoded image parameters
+ _decodedImage._height = _height;
+ _decodedImage._width = _width;
+ _decodedImage._timeStamp = inputImage._timeStamp;
+ memcpy(_decodedImage._buffer, inputImage._buffer, inputImage._length);
+ _decodedImage._length = inputImage._length;
+ //_decodedImage._buffer = inputImage._buffer;
+
+ _decodeCompleteCallback->Decoded(_decodedImage);
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+I420Decoder::RegisterDecodeCompleteCallback(DecodedImageCallback* callback)
+{
+ _decodeCompleteCallback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+I420Decoder::Release()
+{
+ if (_decodedImage._buffer != NULL)
+ {
+ delete [] _decodedImage._buffer;
+ _decodedImage._buffer = NULL;
+ }
+ _inited = false;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+}
diff --git a/src/modules/video_coding/codecs/i420/main/source/i420.gyp b/src/modules/video_coding/codecs/i420/main/source/i420.gyp
new file mode 100644
index 0000000..f3afcb6
--- /dev/null
+++ b/src/modules/video_coding/codecs/i420/main/source/i420.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ '../../../../../../common_settings.gypi', # Common settings
+ ],
+ 'targets': [
+ {
+ 'target_name': 'webrtc_i420',
+ 'type': '<(library)',
+ 'dependencies': [
+ '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
+ ],
+ 'include_dirs': [
+ '../interface',
+ '../../../interface',
+ '../../../../../../common_video/interface',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../interface',
+ '../../../../../../common_video/interface',
+ ],
+ },
+ 'sources': [
+ '../interface/i420.h',
+ 'i420.cc',
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/modules/video_coding/codecs/interface/video_codec_interface.h b/src/modules/video_coding/codecs/interface/video_codec_interface.h
new file mode 100644
index 0000000..6aea149
--- /dev/null
+++ b/src/modules/video_coding/codecs/interface/video_codec_interface.h
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#ifndef VIDEO_CODEC_INTERFACE_H
+#define VIDEO_CODEC_INTERFACE_H
+
+#include "common_types.h"
+#include "typedefs.h"
+#include "video_image.h"
+#include "video_error_codes.h"
+
+namespace webrtc
+{
+
+class RTPFragmentationHeader; // forward declaration
+
+struct CodecSpecificInfoVP8
+{
+ bool hasReceivedSLI;
+ WebRtc_UWord8 pictureIdSLI;
+ bool hasReceivedRPSI;
+ WebRtc_UWord64 pictureIdRPSI;
+};
+
+union CodecSpecificInfoUnion
+{
+ CodecSpecificInfoVP8 VP8;
+};
+
+struct CodecSpecificInfo
+{
+ VideoCodecType codecType;
+ CodecSpecificInfoUnion codecSpecific;
+};
+
+class EncodedImageCallback
+{
+public:
+ virtual ~EncodedImageCallback() {};
+
+ // Callback function which is called when an image has been encoded.
+ //
+ // Input:
+ // - encodedImage : The encoded image
+ //
+ // Return value : > 0, signals to the caller that one or more future frames
+ // should be dropped to keep bit rate or frame rate.
+ // = 0, if OK.
+ // < 0, on error.
+ virtual WebRtc_Word32
+ Encoded(EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo = NULL,
+ const RTPFragmentationHeader* fragmentation = NULL) = 0;
+};
+
+class VideoEncoder
+{
+public:
+ virtual ~VideoEncoder() {};
+
+ // Get the encoder version.
+ //
+ // Input:
+ // - length : Length of the version buffer.
+ //
+ // Output:
+ // - version : Buffer where the version string will be written.
+ //
+ // Return value : Number of bytes written to the version buffer.
+ // < 0 on failure.
+ virtual WebRtc_Word32 Version(WebRtc_Word8 *version, WebRtc_Word32 length) const = 0;
+
+ // Initialize the encoder with the information from the VideoCodec.
+ //
+ // Input:
+ // - codecSettings : Codec settings
+ // - numberOfCores : Number of cores available for the encoder
+ // - maxPayloadSize : The maximum size each payload is allowed
+ // to have. Usually MTU - overhead.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings, WebRtc_Word32 numberOfCores, WebRtc_UWord32 maxPayloadSize) = 0;
+
+ // Encode an I420 image (as a part of a video stream). The encoded image
+ // will be returned to the user through the encode complete callback.
+ //
+ // Input:
+ // - inputImage : Image to be encoded
+ // - codecSpecificInfo : Pointer to codec specific data
+ // - frameType : The frame type to encode
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32
+ Encode(const RawImage& inputImage,
+ const CodecSpecificInfo* codecSpecificInfo = NULL,
+ VideoFrameType frameType = kDeltaFrame) = 0;
+
+ // Register an encode complete callback object.
+ //
+ // Input:
+ // - callback : Callback object which handles encoded images.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback* callback) = 0;
+
+ // Free encoder memory.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Release() = 0;
+
+ // Reset encoder state and prepare for a new call.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Reset() = 0;
+
+ // Inform the encoder about the packet loss and round trip time on the network
+ // used to decide the best pattern and signaling.
+ //
+ // - packetLoss : Fraction lost
+ // (loss rate in percent = 100 * packetLoss / 255)
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 packetLoss) = 0;
+
+ // Inform the encoder about the new target bit rate.
+ //
+ // - newBitRate : New target bit rate
+ // - frameRate : The target frame rate
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate) = 0;
+
+ // Use this function to enable or disable periodic key frames. Can be useful for codecs
+ // which have other ways of stopping error propagation.
+ //
+ // - enable : Enable or disable periodic key frames
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 SetPeriodicKeyFrames(bool enable) { return WEBRTC_VIDEO_CODEC_ERROR; }
+
+ // Codec configuration data to send out-of-band, i.e. in SIP call setup
+ //
+ // - buffer : Buffer pointer to where the configuration data
+ // should be stored
+ // - size : The size of the buffer in bytes
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; }
+};
+
+class DecodedImageCallback
+{
+public:
+ virtual ~DecodedImageCallback() {};
+
+ // Callback function which is called when an image has been decoded.
+ //
+ // Input:
+ // - decodedImage : The decoded image
+ //
+ // Return value : 0 if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Decoded(RawImage& decodedImage) = 0;
+
+ virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) {return -1;}
+
+ virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId) {return -1;}
+};
+
+class VideoDecoder
+{
+public:
+ virtual ~VideoDecoder() {};
+
+ // Initialize the decoder with the information from the VideoCodec.
+ //
+ // Input:
+ // - inst : Codec settings
+ // - numberOfCores : Number of cores available for the decoder
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 numberOfCores) = 0;
+
+ // Decode encoded image (as a part of a video stream). The decoded image
+ // will be returned to the user through the decode complete callback.
+ //
+ // Input:
+ // - inputImage : Encoded image to be decoded
+ // - missingFrames : True if one or more frames have been lost
+ // since the previous decode call.
+ // - codecSpecificInfo : Pointer to codec specific data
+ // - renderTimeMs : System time to render in milliseconds. Only
+ // used by decoders with internal rendering.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, bool missingFrames, const void* codecSpecificInfo = NULL, WebRtc_Word64 renderTimeMs = -1) = 0;
+
+ // Register an decode complete callback object.
+ //
+ // Input:
+ // - callback : Callback object which handles decoded images.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback* callback) = 0;
+
+ // Free decoder memory.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Release() = 0;
+
+ // Reset decoder state and prepare for a new call.
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Reset() = 0;
+
+ // Codec configuration data sent out-of-band, i.e. in SIP call setup
+ //
+ // Input/Output:
+ // - buffer : Buffer pointer to the configuration data
+ // - size : The size of the configuration data in
+ // bytes
+ //
+ // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; }
+
+ // Create a copy of the codec and its internal state.
+ //
+ // Return value : A copy of the instance if OK, NULL otherwise.
+ virtual VideoDecoder* Copy() { return NULL; }
+};
+
+} // namespace webrtc
+
+#endif // VIDEO_CODEC_INTERFACE_H
diff --git a/src/modules/video_coding/codecs/interface/video_error_codes.h b/src/modules/video_coding/codecs/interface/video_error_codes.h
new file mode 100644
index 0000000..257da1b
--- /dev/null
+++ b/src/modules/video_coding/codecs/interface/video_error_codes.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef VIDEO_ERROR_CODES_H
+#define VIDEO_ERROR_CODES_H
+
+// NOTE: in sync with video_coding_module_defines.h
+
+// Define return values
+
+#define WEBRTC_VIDEO_CODEC_REQUEST_SLI 2
+#define WEBRTC_VIDEO_CODEC_OK 0
+#define WEBRTC_VIDEO_CODEC_ERROR -1
+#define WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED -2
+#define WEBRTC_VIDEO_CODEC_MEMORY -3
+#define WEBRTC_VIDEO_CODEC_ERR_PARAMETER -4
+#define WEBRTC_VIDEO_CODEC_ERR_SIZE -5
+#define WEBRTC_VIDEO_CODEC_TIMEOUT -6
+#define WEBRTC_VIDEO_CODEC_UNINITIALIZED -7
+#define WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI -12
+
+#endif // VIDEO_ERROR_CODES_H
diff --git a/src/modules/video_coding/codecs/test_framework/benchmark.cc b/src/modules/video_coding/codecs/test_framework/benchmark.cc
new file mode 100644
index 0000000..22b1315
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/benchmark.cc
@@ -0,0 +1,304 @@
+/*
+ * 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 "benchmark.h"
+#include "video_source.h"
+#include "vplib.h"
+#include <vector>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <cassert>
+#if defined(_WIN32)
+ #include <windows.h>
+#endif
+#include "event_wrapper.h"
+#include "video_codec_interface.h"
+
+#define SSIM_CALC 0 // by default, don't compute SSIM
+
+using namespace webrtc;
+
+Benchmark::Benchmark()
+:
+_resultsFileName("../../../../testFiles/benchmark.txt"),
+_codecName("Default"),
+NormalAsyncTest("Benchmark", "Codec benchmark over a range of test cases", 6)
+{
+}
+
+Benchmark::Benchmark(std::string name, std::string description)
+:
+_resultsFileName("../../../../testFiles/benchmark.txt"),
+_codecName("Default"),
+NormalAsyncTest(name, description, 6)
+{
+}
+
+Benchmark::Benchmark(std::string name, std::string description, std::string resultsFileName, std::string codecName)
+:
+_resultsFileName(resultsFileName),
+_codecName(codecName),
+NormalAsyncTest(name, description, 6)
+{
+}
+
+void
+Benchmark::Perform()
+{
+ std::vector<const VideoSource*> sources;
+ std::vector<const VideoSource*>::iterator it;
+
+ // Configuration --------------------------
+ sources.push_back(new const VideoSource("test/testFiles/foreman_cif.yuv", kCIF));
+ sources.push_back(new const VideoSource("test/testFiles/akiyo_cif.yuv", kCIF));
+
+ const VideoSize size[] = {kQCIF, kCIF};
+ const int frameRate[] = {10, 15, 30};
+ // Specifies the framerates for which to perform a speed test.
+ const bool speedTestMask[] = {false, false, false};
+ const int bitRate[] = {50, 100, 200, 300, 400, 500, 600, 1000};
+ // Determines the number of iterations to perform to arrive at the speed result.
+ enum { kSpeedTestIterations = 10 };
+ // ----------------------------------------
+
+ const int nFrameRates = sizeof(frameRate)/sizeof(*frameRate);
+ assert(sizeof(speedTestMask)/sizeof(*speedTestMask) == nFrameRates);
+ const int nBitrates = sizeof(bitRate)/sizeof(*bitRate);
+ int testIterations = 10;
+
+ double psnr[nBitrates];
+ double ssim[nBitrates];
+ double fps[nBitrates];
+ double totalEncodeTime[nBitrates];
+ double totalDecodeTime[nBitrates];
+
+ _results.open(_resultsFileName.c_str(), std::fstream::out);
+ _results << GetMagicStr() << std::endl;
+ _results << _codecName << std::endl;
+
+ for (it = sources.begin() ; it < sources.end(); it++)
+ {
+ for (int i = 0; i < sizeof(size)/sizeof(*size); i++)
+ {
+ for (int j = 0; j < nFrameRates; j++)
+ {
+ std::stringstream ss;
+ std::string strFrameRate;
+ std::string outFileName;
+ ss << frameRate[j];
+ ss >> strFrameRate;
+ outFileName = (*it)->GetFilePath() + "/" + (*it)->GetName() + "_" +
+ VideoSource::GetSizeString(size[i]) + "_" + strFrameRate + ".yuv";
+
+ _target = new const VideoSource(outFileName, size[i], frameRate[j]);
+ (*it)->Convert(*_target);
+ if (VideoSource::FileExists(outFileName.c_str()))
+ {
+ _inname = outFileName;
+ }
+ else
+ {
+ _inname = (*it)->GetFileName();
+ }
+
+ std::cout << (*it)->GetName() << ", " << VideoSource::GetSizeString(size[i])
+ << ", " << frameRate[j] << " fps" << std::endl << "Bitrate [kbps]:";
+ _results << (*it)->GetName() << "," << VideoSource::GetSizeString(size[i])
+ << "," << frameRate[j] << " fps" << std::endl << "Bitrate [kbps]";
+
+ if (speedTestMask[j])
+ {
+ testIterations = kSpeedTestIterations;
+ }
+ else
+ {
+ testIterations = 1;
+ }
+
+ for (int k = 0; k < nBitrates; k++)
+ {
+ _bitRate = (bitRate[k]);
+ double avgFps = 0.0;
+ totalEncodeTime[k] = 0;
+ totalDecodeTime[k] = 0;
+
+ for (int l = 0; l < testIterations; l++)
+ {
+ PerformNormalTest();
+ _appendNext = false;
+
+ avgFps += _framecnt / (_totalEncodeTime + _totalDecodeTime);
+ totalEncodeTime[k] += _totalEncodeTime;
+ totalDecodeTime[k] += _totalDecodeTime;
+
+ }
+ avgFps /= testIterations;
+ totalEncodeTime[k] /= testIterations;
+ totalDecodeTime[k] /= testIterations;
+
+ double actualBitRate = ActualBitRate(_framecnt) / 1000.0;
+ std::cout << " " << actualBitRate;
+ _results << "," << actualBitRate;
+ PSNRfromFiles(_inname.c_str(), _outname.c_str(), _inst.width,
+ _inst.height, &psnr[k]);
+ if (SSIM_CALC)
+ {
+ SSIMfromFiles(_inname.c_str(), _outname.c_str(), _inst.width,
+ _inst.height, &ssim[k]);
+
+ }
+ fps[k] = avgFps;
+ }
+ std::cout << std::endl << "Y-PSNR [dB]:";
+ _results << std::endl << "Y-PSNR [dB]";
+ for (int k = 0; k < nBitrates; k++)
+ {
+ std::cout << " " << psnr[k];
+ _results << "," << psnr[k];
+
+ }
+ if (SSIM_CALC)
+ {
+ std::cout << std::endl << "SSIM: ";
+ _results << std::endl << "SSIM ";
+ for (int k = 0; k < nBitrates; k++)
+ {
+ std::cout << " " << ssim[k];
+ _results << "," << ssim[k];
+ }
+
+ }
+
+ std::cout << std::endl << "Encode Time[ms]:";
+ _results << std::endl << "Encode Time[ms]";
+ for (int k = 0; k < nBitrates; k++)
+ {
+ std::cout << " " << totalEncodeTime[k];
+ _results << "," << totalEncodeTime[k];
+
+ }
+
+ std::cout << std::endl << "Decode Time[ms]:";
+ _results << std::endl << "Decode Time[ms]";
+ for (int k = 0; k < nBitrates; k++)
+ {
+ std::cout << " " << totalDecodeTime[k];
+ _results << "," << totalDecodeTime[k];
+
+ }
+
+ if (speedTestMask[j])
+ {
+ std::cout << std::endl << "Speed [fps]:";
+ _results << std::endl << "Speed [fps]";
+ for (int k = 0; k < nBitrates; k++)
+ {
+ std::cout << " " << static_cast<int>(fps[k] + 0.5);
+ _results << "," << static_cast<int>(fps[k] + 0.5);
+ }
+ }
+ std::cout << std::endl << std::endl;
+ _results << std::endl << std::endl;
+
+ delete _target;
+ }
+ }
+ delete *it;
+ }
+ _results.close();
+}
+
+void
+Benchmark::PerformNormalTest()
+{
+ _encoder = GetNewEncoder();
+ _decoder = GetNewDecoder();
+ CodecSettings(_target->GetWidth(), _target->GetHeight(), _target->GetFrameRate(), _bitRate);
+ Setup();
+ EventWrapper* waitEvent = EventWrapper::Create();
+
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _encoder->InitEncode(&_inst, 4, 1440);
+ CodecSpecific_InitBitrate();
+ _decoder->InitDecode(&_inst,1);
+
+ FrameQueue frameQueue;
+ VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this);
+ VideoDecodeCompleteCallback decCallback(_decodedFile, *this);
+ _encoder->RegisterEncodeCompleteCallback(&encCallback);
+ _decoder->RegisterDecodeCompleteCallback(&decCallback);
+
+ SetCodecSpecificParameters();
+
+ _totalEncodeTime = _totalDecodeTime = 0;
+ _totalEncodePipeTime = _totalDecodePipeTime = 0;
+ bool complete = false;
+ _framecnt = 0;
+ _encFrameCnt = 0;
+ _sumEncBytes = 0;
+ _lengthEncFrame = 0;
+ while (!complete)
+ {
+ complete = Encode();
+ if (!frameQueue.Empty() || complete)
+ {
+ while (!frameQueue.Empty())
+ {
+ _frameToDecode = static_cast<FrameQueueTuple *>(frameQueue.PopFrame());
+ DoPacketLoss();
+ int ret = Decode();
+ 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");
+ }
+ }
+ }
+ waitEvent->Wait(5);
+ }
+
+ _inputVideoBuffer.Free();
+ //_encodedVideoBuffer.Reset(); ?
+ _encodedVideoBuffer.Free();
+ _decodedVideoBuffer.Free();
+
+ _encoder->Release();
+ _decoder->Release();
+ delete waitEvent;
+ delete _encoder;
+ delete _decoder;
+ Teardown();
+}
+
+void
+Benchmark::CodecSpecific_InitBitrate()
+{
+ if (_bitRate == 0)
+ {
+ _encoder->SetRates(600, _inst.maxFramerate);
+ }
+ else
+ {
+ _encoder->SetRates(_bitRate, _inst.maxFramerate);
+ }
+}
+
diff --git a/src/modules/video_coding/codecs/test_framework/benchmark.h b/src/modules/video_coding/codecs/test_framework/benchmark.h
new file mode 100644
index 0000000..57806e5
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/benchmark.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_
+
+#include "normal_async_test.h"
+
+class VideoSource;
+
+class Benchmark : public NormalAsyncTest
+{
+public:
+ Benchmark();
+ virtual void Perform();
+
+protected:
+ Benchmark(std::string name, std::string description);
+ Benchmark(std::string name, std::string description, std::string resultsFileName, std::string codecName);
+ virtual webrtc::VideoEncoder* GetNewEncoder() = 0;
+ virtual webrtc::VideoDecoder* GetNewDecoder() = 0;
+ virtual void PerformNormalTest();
+ virtual void CodecSpecific_InitBitrate();
+ static const char* GetMagicStr() { return "#!benchmark1.0"; }
+
+ const VideoSource* _target;
+ std::string _resultsFileName;
+ std::ofstream _results;
+ std::string _codecName;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_
+
diff --git a/src/modules/video_coding/codecs/test_framework/exportfig.m b/src/modules/video_coding/codecs/test_framework/exportfig.m
new file mode 100644
index 0000000..d0d5ed9
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/exportfig.m
@@ -0,0 +1,500 @@
+function exportfig(varargin)
+%EXPORTFIG Export a figure to Encapsulated Postscript.
+% EXPORTFIG(H, FILENAME) writes the figure H to FILENAME. H is
+% a figure handle and FILENAME is a string that specifies the
+% name of the output file.
+%
+% EXPORTFIG(...,PARAM1,VAL1,PARAM2,VAL2,...) specifies
+% parameters that control various characteristics of the output
+% file.
+%
+% Format Paramter:
+% 'Format' one of the strings 'eps','eps2','jpeg','png','preview'
+% specifies the output format. Defaults to 'eps'.
+% The output format 'preview' does not generate an output
+% file but instead creates a new figure window with a
+% preview of the exported figure. In this case the
+% FILENAME parameter is ignored.
+%
+% 'Preview' one of the strings 'none', 'tiff'
+% specifies a preview for EPS files. Defaults to 'none'.
+%
+% Size Parameters:
+% 'Width' a positive scalar
+% specifies the width in the figure's PaperUnits
+% 'Height' a positive scalar
+% specifies the height in the figure's PaperUnits
+%
+% Specifying only one dimension sets the other dimension
+% so that the exported aspect ratio is the same as the
+% figure's current aspect ratio.
+% If neither dimension is specified the size defaults to
+% the width and height from the figure's PaperPosition.
+%
+% Rendering Parameters:
+% 'Color' one of the strings 'bw', 'gray', 'cmyk'
+% 'bw' specifies that lines and text are exported in
+% black and all other objects in grayscale
+% 'gray' specifies that all objects are exported in grayscale
+% 'cmyk' specifies that all objects are exported in color
+% using the CMYK color space
+% 'Renderer' one of the strings 'painters', 'zbuffer', 'opengl'
+% specifies the renderer to use
+% 'Resolution' a positive scalar
+% specifies the resolution in dots-per-inch.
+%
+% The default color setting is 'bw'.
+%
+% Font Parameters:
+% 'FontMode' one of the strings 'scaled', 'fixed'
+% 'FontSize' a positive scalar
+% in 'scaled' mode multiplies with the font size of each
+% text object to obtain the exported font size
+% in 'fixed' mode specifies the font size of all text
+% objects in points
+% 'FontEncoding' one of the strings 'latin1', 'adobe'
+% specifies the character encoding of the font
+%
+% If FontMode is 'scaled' but FontSize is not specified then a
+% scaling factor is computed from the ratio of the size of the
+% exported figure to the size of the actual figure. The minimum
+% font size allowed after scaling is 5 points.
+% If FontMode is 'fixed' but FontSize is not specified then the
+% exported font sizes of all text objects is 7 points.
+%
+% The default 'FontMode' setting is 'scaled'.
+%
+% Line Width Parameters:
+% 'LineMode' one of the strings 'scaled', 'fixed'
+% 'LineWidth' a positive scalar
+% the semantics of LineMode and LineWidth are exactly the
+% same as FontMode and FontSize, except that they apply
+% to line widths instead of font sizes. The minumum line
+% width allowed after scaling is 0.5 points.
+% If LineMode is 'fixed' but LineWidth is not specified
+% then the exported line width of all line objects is 1
+% point.
+%
+% Examples:
+% exportfig(gcf,'fig1.eps','height',3);
+% Exports the current figure to the file named 'fig1.eps' with
+% a height of 3 inches (assuming the figure's PaperUnits is
+% inches) and an aspect ratio the same as the figure's aspect
+% ratio on screen.
+%
+% exportfig(gcf, 'fig2.eps', 'FontMode', 'fixed',...
+% 'FontSize', 10, 'color', 'cmyk' );
+% Exports the current figure to 'fig2.eps' in color with all
+% text in 10 point fonts. The size of the exported figure is
+% the figure's PaperPostion width and height.
+
+
+if (nargin < 2)
+ error('Too few input arguments');
+end
+
+% exportfig(H, filename, ...)
+H = varargin{1};
+if ~ishandle(H) | ~strcmp(get(H,'type'), 'figure')
+ error('First argument must be a handle to a figure.');
+end
+filename = varargin{2};
+if ~ischar(filename)
+ error('Second argument must be a string.');
+end
+paramPairs = varargin(3:end);
+
+% Do some validity checking on param-value pairs
+if (rem(length(paramPairs),2) ~= 0)
+ error(['Invalid input syntax. Optional parameters and values' ...
+ ' must be in pairs.']);
+end
+
+format = 'eps';
+preview = 'none';
+width = -1;
+height = -1;
+color = 'bw';
+fontsize = -1;
+fontmode='scaled';
+linewidth = -1;
+linemode=[];
+fontencoding = 'latin1';
+renderer = [];
+resolution = [];
+
+% Process param-value pairs
+args = {};
+for k = 1:2:length(paramPairs)
+ param = lower(paramPairs{k});
+ if (~ischar(param))
+ error('Optional parameter names must be strings');
+ end
+ value = paramPairs{k+1};
+
+ switch (param)
+ case 'format'
+ format = value;
+ if (~strcmp(format,{'eps','eps2','jpeg','png','preview'}))
+ error(['Format must be ''eps'', ''eps2'', ''jpeg'', ''png'' or' ...
+ ' ''preview''.']);
+ end
+ case 'preview'
+ preview = value;
+ if (~strcmp(preview,{'none','tiff'}))
+ error('Preview must be ''none'' or ''tiff''.');
+ end
+ case 'width'
+ width = LocalToNum(value);
+ if(~LocalIsPositiveScalar(width))
+ error('Width must be a numeric scalar > 0');
+ end
+ case 'height'
+ height = LocalToNum(value);
+ if(~LocalIsPositiveScalar(height))
+ error('Height must be a numeric scalar > 0');
+ end
+ case 'color'
+ color = lower(value);
+ if (~strcmp(color,{'bw','gray','cmyk'}))
+ error('Color must be ''bw'', ''gray'' or ''cmyk''.');
+ end
+ case 'fontmode'
+ fontmode = lower(value);
+ if (~strcmp(fontmode,{'scaled','fixed'}))
+ error('FontMode must be ''scaled'' or ''fixed''.');
+ end
+ case 'fontsize'
+ fontsize = LocalToNum(value);
+ if(~LocalIsPositiveScalar(fontsize))
+ error('FontSize must be a numeric scalar > 0');
+ end
+ case 'fontencoding'
+ fontencoding = lower(value);
+ if (~strcmp(fontencoding,{'latin1','adobe'}))
+ error('FontEncoding must be ''latin1'' or ''adobe''.');
+ end
+ case 'linemode'
+ linemode = lower(value);
+ if (~strcmp(linemode,{'scaled','fixed'}))
+ error('LineMode must be ''scaled'' or ''fixed''.');
+ end
+ case 'linewidth'
+ linewidth = LocalToNum(value);
+ if(~LocalIsPositiveScalar(linewidth))
+ error('LineWidth must be a numeric scalar > 0');
+ end
+ case 'renderer'
+ renderer = lower(value);
+ if (~strcmp(renderer,{'painters','zbuffer','opengl'}))
+ error('Renderer must be ''painters'', ''zbuffer'' or ''opengl''.');
+ end
+ case 'resolution'
+ resolution = LocalToNum(value);
+ if ~(isnumeric(value) & (prod(size(value)) == 1) & (value >= 0));
+ error('Resolution must be a numeric scalar >= 0');
+ end
+ otherwise
+ error(['Unrecognized option ' param '.']);
+ end
+end
+
+allLines = findall(H, 'type', 'line');
+allText = findall(H, 'type', 'text');
+allAxes = findall(H, 'type', 'axes');
+allImages = findall(H, 'type', 'image');
+allLights = findall(H, 'type', 'light');
+allPatch = findall(H, 'type', 'patch');
+allSurf = findall(H, 'type', 'surface');
+allRect = findall(H, 'type', 'rectangle');
+allFont = [allText; allAxes];
+allColor = [allLines; allText; allAxes; allLights];
+allMarker = [allLines; allPatch; allSurf];
+allEdge = [allPatch; allSurf];
+allCData = [allImages; allPatch; allSurf];
+
+old.objs = {};
+old.prop = {};
+old.values = {};
+
+% Process format and preview parameter
+showPreview = strcmp(format,'preview');
+if showPreview
+ format = 'png';
+ filename = [tempName '.png'];
+end
+if strncmp(format,'eps',3) & ~strcmp(preview,'none')
+ args = {args{:}, ['-' preview]};
+end
+
+hadError = 0;
+try
+ % Process size parameters
+ paperPos = get(H, 'PaperPosition');
+ old = LocalPushOldData(old, H, 'PaperPosition', paperPos);
+ figureUnits = get(H, 'Units');
+ set(H, 'Units', get(H,'PaperUnits'));
+ figurePos = get(H, 'Position');
+ aspectRatio = figurePos(3)/figurePos(4);
+ set(H, 'Units', figureUnits);
+ if (width == -1) & (height == -1)
+ width = paperPos(3);
+ height = paperPos(4);
+ elseif (width == -1)
+ width = height * aspectRatio;
+ elseif (height == -1)
+ height = width / aspectRatio;
+ end
+ set(H, 'PaperPosition', [0 0 width height]);
+ paperPosMode = get(H, 'PaperPositionMode');
+ old = LocalPushOldData(old, H, 'PaperPositionMode', paperPosMode);
+ set(H, 'PaperPositionMode', 'manual');
+
+ % Process rendering parameters
+ switch (color)
+ case {'bw', 'gray'}
+ if ~strcmp(color,'bw') & strncmp(format,'eps',3)
+ format = [format 'c'];
+ end
+ args = {args{:}, ['-d' format]};
+
+ %compute and set gray colormap
+ oldcmap = get(H,'Colormap');
+ newgrays = 0.30*oldcmap(:,1) + 0.59*oldcmap(:,2) + 0.11*oldcmap(:,3);
+ newcmap = [newgrays newgrays newgrays];
+ old = LocalPushOldData(old, H, 'Colormap', oldcmap);
+ set(H, 'Colormap', newcmap);
+
+ %compute and set ColorSpec and CData properties
+ old = LocalUpdateColors(allColor, 'color', old);
+ old = LocalUpdateColors(allAxes, 'xcolor', old);
+ old = LocalUpdateColors(allAxes, 'ycolor', old);
+ old = LocalUpdateColors(allAxes, 'zcolor', old);
+ old = LocalUpdateColors(allMarker, 'MarkerEdgeColor', old);
+ old = LocalUpdateColors(allMarker, 'MarkerFaceColor', old);
+ old = LocalUpdateColors(allEdge, 'EdgeColor', old);
+ old = LocalUpdateColors(allEdge, 'FaceColor', old);
+ old = LocalUpdateColors(allCData, 'CData', old);
+
+ case 'cmyk'
+ if strncmp(format,'eps',3)
+ format = [format 'c'];
+ args = {args{:}, ['-d' format], '-cmyk'};
+ else
+ args = {args{:}, ['-d' format]};
+ end
+ otherwise
+ error('Invalid Color parameter');
+ end
+ if (~isempty(renderer))
+ args = {args{:}, ['-' renderer]};
+ end
+ if (~isempty(resolution)) | ~strncmp(format,'eps',3)
+ if isempty(resolution)
+ resolution = 0;
+ end
+ args = {args{:}, ['-r' int2str(resolution)]};
+ end
+
+ % Process font parameters
+ if (~isempty(fontmode))
+ oldfonts = LocalGetAsCell(allFont,'FontSize');
+ switch (fontmode)
+ case 'fixed'
+ oldfontunits = LocalGetAsCell(allFont,'FontUnits');
+ old = LocalPushOldData(old, allFont, {'FontUnits'}, oldfontunits);
+ set(allFont,'FontUnits','points');
+ if (fontsize == -1)
+ set(allFont,'FontSize',7);
+ else
+ set(allFont,'FontSize',fontsize);
+ end
+ case 'scaled'
+ if (fontsize == -1)
+ wscale = width/figurePos(3);
+ hscale = height/figurePos(4);
+ scale = min(wscale, hscale);
+ else
+ scale = fontsize;
+ end
+ newfonts = LocalScale(oldfonts,scale,5);
+ set(allFont,{'FontSize'},newfonts);
+ otherwise
+ error('Invalid FontMode parameter');
+ end
+ % make sure we push the size after the units
+ old = LocalPushOldData(old, allFont, {'FontSize'}, oldfonts);
+ end
+ if strcmp(fontencoding,'adobe') & strncmp(format,'eps',3)
+ args = {args{:}, '-adobecset'};
+ end
+
+ % Process linewidth parameters
+ if (~isempty(linemode))
+ oldlines = LocalGetAsCell(allMarker,'LineWidth');
+ old = LocalPushOldData(old, allMarker, {'LineWidth'}, oldlines);
+ switch (linemode)
+ case 'fixed'
+ if (linewidth == -1)
+ set(allMarker,'LineWidth',1);
+ else
+ set(allMarker,'LineWidth',linewidth);
+ end
+ case 'scaled'
+ if (linewidth == -1)
+ wscale = width/figurePos(3);
+ hscale = height/figurePos(4);
+ scale = min(wscale, hscale);
+ else
+ scale = linewidth;
+ end
+ newlines = LocalScale(oldlines, scale, 0.5);
+ set(allMarker,{'LineWidth'},newlines);
+ otherwise
+ error('Invalid LineMode parameter');
+ end
+ end
+
+ % Export
+ print(H, filename, args{:});
+
+catch
+ hadError = 1;
+end
+
+% Restore figure settings
+for n=1:length(old.objs)
+ set(old.objs{n}, old.prop{n}, old.values{n});
+end
+
+if hadError
+ error(deblank(lasterr));
+end
+
+% Show preview if requested
+if showPreview
+ X = imread(filename,'png');
+ delete(filename);
+ f = figure( 'Name', 'Preview', ...
+ 'Menubar', 'none', ...
+ 'NumberTitle', 'off', ...
+ 'Visible', 'off');
+ image(X);
+ axis image;
+ ax = findobj(f, 'type', 'axes');
+ set(ax, 'Units', get(H,'PaperUnits'), ...
+ 'Position', [0 0 width height], ...
+ 'Visible', 'off');
+ set(ax, 'Units', 'pixels');
+ axesPos = get(ax,'Position');
+ figPos = get(f,'Position');
+ rootSize = get(0,'ScreenSize');
+ figPos(3:4) = axesPos(3:4);
+ if figPos(1) + figPos(3) > rootSize(3)
+ figPos(1) = rootSize(3) - figPos(3) - 50;
+ end
+ if figPos(2) + figPos(4) > rootSize(4)
+ figPos(2) = rootSize(4) - figPos(4) - 50;
+ end
+ set(f, 'Position',figPos, ...
+ 'Visible', 'on');
+end
+
+%
+% Local Functions
+%
+
+function outData = LocalPushOldData(inData, objs, prop, values)
+outData.objs = {inData.objs{:}, objs};
+outData.prop = {inData.prop{:}, prop};
+outData.values = {inData.values{:}, values};
+
+function cellArray = LocalGetAsCell(fig,prop);
+cellArray = get(fig,prop);
+if (~isempty(cellArray)) & (~iscell(cellArray))
+ cellArray = {cellArray};
+end
+
+function newArray = LocalScale(inArray, scale, minValue)
+n = length(inArray);
+newArray = cell(n,1);
+for k=1:n
+ newArray{k} = max(minValue,scale*inArray{k}(1));
+end
+
+function newArray = LocalMapToGray(inArray);
+n = length(inArray);
+newArray = cell(n,1);
+for k=1:n
+ color = inArray{k};
+ if (~isempty(color))
+ if ischar(color)
+ switch color(1)
+ case 'y'
+ color = [1 1 0];
+ case 'm'
+ color = [1 0 1];
+ case 'c'
+ color = [0 1 1];
+ case 'r'
+ color = [1 0 0];
+ case 'g'
+ color = [0 1 0];
+ case 'b'
+ color = [0 0 1];
+ case 'w'
+ color = [1 1 1];
+ case 'k'
+ color = [0 0 0];
+ otherwise
+ newArray{k} = color;
+ end
+ end
+ if ~ischar(color)
+ color = 0.30*color(1) + 0.59*color(2) + 0.11*color(3);
+ end
+ end
+ if isempty(color) | ischar(color)
+ newArray{k} = color;
+ else
+ newArray{k} = [color color color];
+ end
+end
+
+function newArray = LocalMapCData(inArray);
+n = length(inArray);
+newArray = cell(n,1);
+for k=1:n
+ color = inArray{k};
+ if (ndims(color) == 3) & isa(color,'double')
+ gray = 0.30*color(:,:,1) + 0.59*color(:,:,2) + 0.11*color(:,:,3);
+ color(:,:,1) = gray;
+ color(:,:,2) = gray;
+ color(:,:,3) = gray;
+ end
+ newArray{k} = color;
+end
+
+function outData = LocalUpdateColors(inArray, prop, inData)
+value = LocalGetAsCell(inArray,prop);
+outData.objs = {inData.objs{:}, inArray};
+outData.prop = {inData.prop{:}, {prop}};
+outData.values = {inData.values{:}, value};
+if (~isempty(value))
+ if strcmp(prop,'CData')
+ value = LocalMapCData(value);
+ else
+ value = LocalMapToGray(value);
+ end
+ set(inArray,{prop},value);
+end
+
+function bool = LocalIsPositiveScalar(value)
+bool = isnumeric(value) & ...
+ prod(size(value)) == 1 & ...
+ value > 0;
+
+function value = LocalToNum(value)
+if ischar(value)
+ value = str2num(value);
+end
diff --git a/src/modules/video_coding/codecs/test_framework/normal_async_test.cc b/src/modules/video_coding/codecs/test_framework/normal_async_test.cc
new file mode 100644
index 0000000..9141cb0
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/normal_async_test.cc
@@ -0,0 +1,563 @@
+/*
+ * 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 "normal_async_test.h"
+#include "typedefs.h"
+#include <sstream>
+#include <assert.h>
+#include <queue>
+#include <string.h>
+#include "tick_util.h"
+
+using namespace webrtc;
+
+NormalAsyncTest::NormalAsyncTest()
+:
+_requestKeyFrame(false),
+_testNo(1),
+_appendNext(false),
+_decFrameCnt(0),
+_encFrameCnt(0),
+_missingFrames(false),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_rttFrames(0),
+_hasReceivedSLI(false),
+_hasReceivedPLI(false),
+_waitForKey(false),
+NormalTest("Async Normal Test 1", "A test of normal execution of the codec",
+ _testNo)
+{
+}
+
+NormalAsyncTest::NormalAsyncTest(WebRtc_UWord32 bitRate)
+:
+_requestKeyFrame(false),
+_testNo(1),
+_appendNext(false),
+_decFrameCnt(0),
+_encFrameCnt(0),
+_missingFrames(false),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_rttFrames(0),
+_hasReceivedSLI(false),
+_hasReceivedPLI(false),
+_waitForKey(false),
+NormalTest("Async Normal Test 1", "A test of normal execution of the codec",
+ bitRate, _testNo)
+{
+}
+
+NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
+ unsigned int testNo)
+:
+_requestKeyFrame(false),
+_testNo(testNo),
+_lengthEncFrame(0),
+_appendNext(false),
+_decFrameCnt(0),
+_encFrameCnt(0),
+_missingFrames(false),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_rttFrames(0),
+_hasReceivedSLI(false),
+_hasReceivedPLI(false),
+_waitForKey(false),
+NormalTest(name, description, _testNo)
+{
+}
+
+NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
+ WebRtc_UWord32 bitRate, unsigned int testNo)
+:
+_requestKeyFrame(false),
+_testNo(testNo),
+_lengthEncFrame(0),
+_appendNext(false),
+_decFrameCnt(0),
+_encFrameCnt(0),
+_missingFrames(false),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_rttFrames(0),
+_hasReceivedSLI(false),
+_hasReceivedPLI(false),
+_waitForKey(false),
+NormalTest(name, description, bitRate, _testNo)
+{
+}
+
+NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
+ WebRtc_UWord32 bitRate, unsigned int testNo,
+ unsigned int rttFrames)
+:
+_requestKeyFrame(false),
+_testNo(testNo),
+_lengthEncFrame(0),
+_appendNext(false),
+_decFrameCnt(0),
+_encFrameCnt(0),
+_missingFrames(false),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_rttFrames(rttFrames),
+_hasReceivedSLI(false),
+_hasReceivedPLI(false),
+_waitForKey(false),
+NormalTest(name, description, bitRate, _testNo)
+{
+}
+
+void
+NormalAsyncTest::Setup()
+{
+ Test::Setup();
+ std::stringstream ss;
+ std::string strTestNo;
+ ss << _testNo;
+ ss >> strTestNo;
+
+ // Check if settings exist. Otherwise use defaults.
+ if (_outname == "")
+ {
+ _outname = "../../out_normaltest" + strTestNo + ".yuv";
+ }
+
+ if (_encodedName == "")
+ {
+ _encodedName = "../../encoded_normaltest" + strTestNo + ".yuv";
+ }
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+
+ char mode[3] = "wb";
+ if (_appendNext)
+ {
+ strncpy(mode, "ab", 3);
+ }
+
+ if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL)
+ {
+ printf("Cannot write file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ _appendNext = true;
+}
+
+void
+NormalAsyncTest::Teardown()
+{
+ Test::Teardown();
+ fclose(_sourceFile);
+ fclose(_decodedFile);
+}
+
+FrameQueueTuple::~FrameQueueTuple()
+{
+ if (_codecSpecificInfo != NULL)
+ {
+ // TODO(holmer): implement virtual function for deleting this and
+ // remove warnings
+ delete _codecSpecificInfo;
+ }
+ if (_frame != NULL)
+ {
+ delete _frame;
+ }
+}
+
+void FrameQueue::PushFrame(TestVideoEncodedBuffer *frame,
+ void* codecSpecificInfo)
+{
+ WriteLockScoped cs(_queueRWLock);
+ _frameBufferQueue.push(new FrameQueueTuple(frame, codecSpecificInfo));
+}
+
+FrameQueueTuple* FrameQueue::PopFrame()
+{
+ WriteLockScoped cs(_queueRWLock);
+ if (_frameBufferQueue.empty())
+ {
+ return NULL;
+ }
+ FrameQueueTuple* tuple = _frameBufferQueue.front();
+ _frameBufferQueue.pop();
+ return tuple;
+}
+
+bool FrameQueue::Empty()
+{
+ ReadLockScoped cs(_queueRWLock);
+ return _frameBufferQueue.empty();
+}
+
+WebRtc_UWord32 VideoEncodeCompleteCallback::EncodedBytes()
+{
+ return _encodedBytes;
+}
+
+WebRtc_Word32
+VideoEncodeCompleteCallback::Encoded(EncodedImage& encodedImage,
+ const void* codecSpecificInfo,
+ const webrtc::RTPFragmentationHeader*
+ fragmentation)
+{
+ _test.Encoded(encodedImage);
+ TestVideoEncodedBuffer *newBuffer = new TestVideoEncodedBuffer();
+ //newBuffer->VerifyAndAllocate(encodedImage._length);
+ newBuffer->VerifyAndAllocate(encodedImage._size);
+ _encodedBytes += encodedImage._length;
+ // If _frameQueue would have been a fixed sized buffer we could have asked
+ // it for an empty frame and then just do:
+ // emptyFrame->SwapBuffers(encodedBuffer);
+ // This is how it should be done in Video Engine to save in on memcpys
+ void* codecSpecificInfoCopy =
+ _test.CopyCodecSpecificInfo(codecSpecificInfo);
+ _test.CopyEncodedImage(*newBuffer, encodedImage, codecSpecificInfoCopy);
+ if (_encodedFile != NULL)
+ {
+ fwrite(newBuffer->GetBuffer(), 1, newBuffer->GetLength(), _encodedFile);
+ }
+ _frameQueue->PushFrame(newBuffer, codecSpecificInfoCopy);
+ return 0;
+}
+
+WebRtc_UWord32 VideoDecodeCompleteCallback::DecodedBytes()
+{
+ return _decodedBytes;
+}
+
+WebRtc_Word32
+VideoDecodeCompleteCallback::Decoded(RawImage& image)
+{
+ _test.Decoded(image);
+ _decodedBytes += image._length;
+ if (_decodedFile != NULL)
+ {
+ fwrite(image._buffer, 1, image._length, _decodedFile);
+ }
+ return 0;
+}
+
+WebRtc_Word32
+VideoDecodeCompleteCallback::ReceivedDecodedReferenceFrame(
+ const WebRtc_UWord64 pictureId)
+{
+ return _test.ReceivedDecodedReferenceFrame(pictureId);
+}
+
+WebRtc_Word32
+VideoDecodeCompleteCallback::ReceivedDecodedFrame(
+ const WebRtc_UWord64 pictureId)
+{
+ return _test.ReceivedDecodedFrame(pictureId);
+}
+
+void
+NormalAsyncTest::Encoded(const EncodedImage& encodedImage)
+{
+ _encodeCompleteTime = tGetTime();
+ _encFrameCnt++;
+ _totalEncodePipeTime += _encodeCompleteTime -
+ _encodeTimes[encodedImage._timeStamp];
+}
+
+void
+NormalAsyncTest::Decoded(const RawImage& decodedImage)
+{
+ _decodeCompleteTime = tGetTime();
+ _decFrameCnt++;
+ _totalDecodePipeTime += _decodeCompleteTime -
+ _decodeTimes[decodedImage._timeStamp];
+ _decodedWidth = decodedImage._width;
+ _decodedHeight = decodedImage._height;
+}
+
+void
+NormalAsyncTest::Perform()
+{
+ _inname = "test/testFiles/foreman_cif.yuv";
+ CodecSettings(352, 288, 30, _bitRate);
+ Setup();
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ if(_encoder->InitEncode(&_inst, 1, 1440) < 0)
+ {
+ exit(EXIT_FAILURE);
+ }
+ _decoder->InitDecode(&_inst, 1);
+ FrameQueue frameQueue;
+ VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this);
+ VideoDecodeCompleteCallback decCallback(_decodedFile, *this);
+ _encoder->RegisterEncodeCompleteCallback(&encCallback);
+ _decoder->RegisterDecodeCompleteCallback(&decCallback);
+ 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 = tGetTime();
+ while (!complete)
+ {
+ CodecSpecific_InitBitrate();
+ complete = Encode();
+ 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 = tGetTime();
+ 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
+NormalAsyncTest::Encode()
+{
+ _lengthEncFrame = 0;
+ fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile);
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
+ _inputVideoBuffer.SetTimeStamp((unsigned int)
+ (_encFrameCnt * 9e4 / _inst.maxFramerate));
+ _inputVideoBuffer.SetWidth(_inst.width);
+ _inputVideoBuffer.SetHeight(_inst.height);
+ RawImage rawImage;
+ VideoBufferToRawImage(_inputVideoBuffer, rawImage);
+ if (feof(_sourceFile) != 0)
+ {
+ return true;
+ }
+ _encodeCompleteTime = 0;
+ _encodeTimes[rawImage._timeStamp] = tGetTime();
+ VideoFrameType frameType = kDeltaFrame;
+
+ // check SLI queue
+ _hasReceivedSLI = false;
+ while (!_signalSLI.empty() && _signalSLI.front().delay == 0)
+ {
+ // SLI message has arrived at sender side
+ _hasReceivedSLI = true;
+ _pictureIdSLI = _signalSLI.front().id;
+ _signalSLI.pop_front();
+ }
+ // decrement SLI queue times
+ for (std::list<fbSignal>::iterator it = _signalSLI.begin();
+ it !=_signalSLI.end(); it++)
+ {
+ (*it).delay--;
+ }
+
+ // check PLI queue
+ _hasReceivedPLI = false;
+ while (!_signalPLI.empty() && _signalPLI.front().delay == 0)
+ {
+ // PLI message has arrived at sender side
+ _hasReceivedPLI = true;
+ _signalPLI.pop_front();
+ }
+ // decrement PLI queue times
+ for (std::list<fbSignal>::iterator it = _signalPLI.begin();
+ it != _signalPLI.end(); it++)
+ {
+ (*it).delay--;
+ }
+
+ if (_hasReceivedPLI)
+ {
+ // respond to PLI by encoding a key frame
+ frameType = kKeyFrame;
+ _hasReceivedPLI = false;
+ _hasReceivedSLI = false; // don't trigger both at once
+ }
+
+ void* codecSpecificInfo = CreateEncoderSpecificInfo();
+ int ret = _encoder->Encode(rawImage, codecSpecificInfo, frameType);
+ if (codecSpecificInfo != NULL)
+ {
+ // TODO(holmer): implement virtual function for deleting this and
+ // remove warnings
+ delete codecSpecificInfo;
+ codecSpecificInfo = NULL;
+ }
+ if (_encodeCompleteTime > 0)
+ {
+ _totalEncodeTime += _encodeCompleteTime -
+ _encodeTimes[rawImage._timeStamp];
+ }
+ else
+ {
+ _totalEncodeTime += tGetTime() - _encodeTimes[rawImage._timeStamp];
+ }
+ assert(ret >= 0);
+ return false;
+}
+
+int
+NormalAsyncTest::Decode(int lossValue)
+{
+ _sumEncBytes += _frameToDecode->_frame->GetLength();
+ double starttime = 0;
+ EncodedImage encodedImage;
+ VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage);
+ encodedImage._completeFrame = !lossValue;
+ _decodeCompleteTime = 0;
+ _decodeTimes[encodedImage._timeStamp] = tGetTime();
+ int ret = WEBRTC_VIDEO_CODEC_OK;
+ if (!_waitForKey || encodedImage._frameType == kKeyFrame)
+ {
+ _waitForKey = false;
+ ret = _decoder->Decode(encodedImage, _missingFrames,
+ _frameToDecode->_codecSpecificInfo);
+
+ if (ret >= 0)
+ {
+ _missingFrames = false;
+ }
+ }
+
+ // check for SLI
+ if (ret == WEBRTC_VIDEO_CODEC_REQUEST_SLI)
+ {
+ // add an SLI feedback to the feedback "queue"
+ // to be delivered to encoder with _rttFrames delay
+ _signalSLI.push_back(fbSignal(_rttFrames,
+ static_cast<WebRtc_UWord8>((_lastDecPictureId) & 0x3f))); // 6 lsb
+
+ ret = WEBRTC_VIDEO_CODEC_OK;
+ }
+ else if (ret == WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI)
+ {
+ // add an SLI feedback to the feedback "queue"
+ // to be delivered to encoder with _rttFrames delay
+ _signalSLI.push_back(fbSignal(_rttFrames,
+ static_cast<WebRtc_UWord8>((_lastDecPictureId + 1) & 0x3f)));//6 lsb
+
+ ret = WEBRTC_VIDEO_CODEC_OK;
+ }
+ else if (ret == WEBRTC_VIDEO_CODEC_ERROR)
+ {
+ // wait for new key frame
+ // add an PLI feedback to the feedback "queue"
+ // to be delivered to encoder with _rttFrames delay
+ _signalPLI.push_back(fbSignal(_rttFrames, 0 /* picId not used*/));
+ _waitForKey = true;
+
+ ret = WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ if (_decodeCompleteTime > 0)
+ {
+ _totalDecodeTime += _decodeCompleteTime -
+ _decodeTimes[encodedImage._timeStamp];
+ }
+ else
+ {
+ _totalDecodeTime += tGetTime() - _decodeTimes[encodedImage._timeStamp];
+ }
+ return ret;
+}
+
+void NormalAsyncTest::CodecSpecific_InitBitrate()
+{
+ if (_bitRate == 0)
+ {
+ _encoder->SetRates(600, _inst.maxFramerate);
+ }
+ else
+ {
+ _encoder->SetRates(_bitRate, _inst.maxFramerate);
+ }
+}
+
+void NormalAsyncTest::CopyEncodedImage(TestVideoEncodedBuffer& dest,
+ EncodedImage& src,
+ void* /*codecSpecificInfo*/) const
+{
+ dest.CopyBuffer(src._length, src._buffer);
+ dest.SetFrameType(src._frameType);
+ dest.SetCaptureWidth((WebRtc_UWord16)src._encodedWidth);
+ dest.SetCaptureHeight((WebRtc_UWord16)src._encodedHeight);
+ dest.SetTimeStamp(src._timeStamp);
+}
+double
+NormalAsyncTest::tGetTime()
+{// return time in sec
+ return ((double) (TickTime::MillisecondTimestamp())/1000);
+ }
diff --git a/src/modules/video_coding/codecs/test_framework/normal_async_test.h b/src/modules/video_coding/codecs/test_framework/normal_async_test.h
new file mode 100644
index 0000000..65506c3
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/normal_async_test.h
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_
+
+#include "common_types.h"
+
+#include "normal_test.h"
+#include "rw_lock_wrapper.h"
+#include <list>
+#include <map>
+#include <queue>
+
+class FrameQueueTuple
+{
+public:
+ FrameQueueTuple(TestVideoEncodedBuffer *frame,
+ const void* codecSpecificInfo = NULL)
+ :
+ _frame(frame),
+ _codecSpecificInfo(codecSpecificInfo)
+ {};
+ ~FrameQueueTuple();
+ TestVideoEncodedBuffer* _frame;
+ const void* _codecSpecificInfo;
+};
+
+class FrameQueue
+{
+public:
+ FrameQueue()
+ :
+ _queueRWLock(*webrtc::RWLockWrapper::CreateRWLock()),
+ _prevTS(-1)
+ {
+ }
+
+ ~FrameQueue()
+ {
+ delete &_queueRWLock;
+ }
+
+ void PushFrame(TestVideoEncodedBuffer *frame,
+ void* codecSpecificInfo = NULL);
+ FrameQueueTuple* PopFrame();
+ bool Empty();
+
+private:
+ webrtc::RWLockWrapper& _queueRWLock;
+ std::queue<FrameQueueTuple *> _frameBufferQueue;
+ WebRtc_Word64 _prevTS;
+};
+
+// feedback signal to encoder
+struct fbSignal
+{
+ fbSignal(int d, WebRtc_UWord8 pid) : delay(d), id(pid) {};
+ int delay;
+ WebRtc_UWord8 id;
+};
+
+class NormalAsyncTest : public NormalTest
+{
+public:
+ NormalAsyncTest();
+ NormalAsyncTest(WebRtc_UWord32 bitRate);
+ NormalAsyncTest(std::string name, std::string description,
+ unsigned int testNo);
+ NormalAsyncTest(std::string name, std::string description,
+ WebRtc_UWord32 bitRate, unsigned int testNo);
+ NormalAsyncTest(std::string name, std::string description,
+ WebRtc_UWord32 bitRate, unsigned int testNo,
+ unsigned int rttFrames);
+ virtual ~NormalAsyncTest() {};
+ virtual void Perform();
+ virtual void Encoded(const webrtc::EncodedImage& encodedImage);
+ virtual void Decoded(const webrtc::RawImage& decodedImage);
+ virtual void*
+ CopyCodecSpecificInfo(const void* /*codecSpecificInfo */) const
+ { return NULL; };
+ virtual void CopyEncodedImage(TestVideoEncodedBuffer& dest,
+ webrtc::EncodedImage& src,
+ void* /*codecSpecificInfo*/) const;
+ virtual void* CreateEncoderSpecificInfo() const { return NULL; };
+ virtual WebRtc_Word32
+ ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) { return 0;};
+ virtual WebRtc_Word32
+ ReceivedDecodedFrame(const WebRtc_UWord64 pictureId) { return 0;};
+
+protected:
+ virtual void Setup();
+ virtual void Teardown();
+ virtual bool Encode();
+ virtual int Decode(int lossValue = 0);
+ virtual void CodecSpecific_InitBitrate();
+ virtual int SetCodecSpecificParameters() {return 0;};
+ double tGetTime();// return time in sec
+
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ WebRtc_UWord32 _decodedWidth;
+ WebRtc_UWord32 _decodedHeight;
+ double _totalEncodeTime;
+ double _totalDecodeTime;
+ double _decodeCompleteTime;
+ double _encodeCompleteTime;
+ double _totalEncodePipeTime;
+ double _totalDecodePipeTime;
+ int _framecnt;
+ int _encFrameCnt;
+ int _decFrameCnt;
+ bool _requestKeyFrame;
+ unsigned int _testNo;
+ unsigned int _lengthEncFrame;
+ FrameQueueTuple* _frameToDecode;
+ bool _appendNext;
+ std::map<WebRtc_UWord32, double> _encodeTimes;
+ std::map<WebRtc_UWord32, double> _decodeTimes;
+ bool _missingFrames;
+ std::list<fbSignal> _signalSLI;
+ int _rttFrames;
+ mutable bool _hasReceivedSLI;
+ WebRtc_UWord8 _pictureIdSLI;
+ WebRtc_UWord64 _lastDecPictureId;
+ std::list<fbSignal> _signalPLI;
+ bool _hasReceivedPLI;
+ bool _waitForKey;
+};
+
+class VideoEncodeCompleteCallback : public webrtc::EncodedImageCallback
+{
+public:
+ VideoEncodeCompleteCallback(FILE* encodedFile, FrameQueue *frameQueue,
+ NormalAsyncTest& test)
+ :
+ _encodedFile(encodedFile),
+ _frameQueue(frameQueue),
+ _test(test),
+ _encodedBytes(0)
+ {}
+
+ WebRtc_Word32
+ Encoded(webrtc::EncodedImage& encodedImage,
+ const void* codecSpecificInfo = NULL,
+ const webrtc::RTPFragmentationHeader* fragmentation = NULL);
+ WebRtc_UWord32 EncodedBytes();
+private:
+ FILE* _encodedFile;
+ FrameQueue* _frameQueue;
+ NormalAsyncTest& _test;
+ WebRtc_UWord32 _encodedBytes;
+};
+
+class VideoDecodeCompleteCallback : public webrtc::DecodedImageCallback
+{
+public:
+ VideoDecodeCompleteCallback(FILE* decodedFile, NormalAsyncTest& test)
+ :
+ _decodedFile(decodedFile),
+ _test(test),
+ _decodedBytes(0)
+ {}
+
+ virtual WebRtc_Word32 Decoded(webrtc::RawImage& decodedImage);
+ virtual WebRtc_Word32
+ ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId);
+ virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId);
+
+ WebRtc_UWord32 DecodedBytes();
+private:
+ FILE* _decodedFile;
+ NormalAsyncTest& _test;
+ WebRtc_UWord32 _decodedBytes;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_
diff --git a/src/modules/video_coding/codecs/test_framework/normal_test.cc b/src/modules/video_coding/codecs/test_framework/normal_test.cc
new file mode 100644
index 0000000..917eb0c
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/normal_test.cc
@@ -0,0 +1,246 @@
+/*
+ * 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 "normal_test.h"
+#include <time.h>
+#include <sstream>
+#include <string.h>
+
+NormalTest::NormalTest()
+:
+_testNo(1),
+_lengthEncFrame(0),
+_appendNext(false),
+Test("Normal Test 1", "A test of normal execution of the codec")
+{
+}
+
+NormalTest::NormalTest(std::string name, std::string description, unsigned int testNo)
+:
+_requestKeyFrame(false),
+_testNo(testNo),
+_lengthEncFrame(0),
+_appendNext(false),
+Test(name, description)
+{
+}
+
+NormalTest::NormalTest(std::string name, std::string description, WebRtc_UWord32 bitRate, unsigned int testNo)
+:
+_requestKeyFrame(false),
+_testNo(testNo),
+_lengthEncFrame(0),
+_appendNext(false),
+Test(name, description, bitRate)
+{
+}
+
+void
+NormalTest::Setup()
+{
+ Test::Setup();
+ std::stringstream ss;
+ std::string strTestNo;
+ ss << _testNo;
+ ss >> strTestNo;
+
+ // Check if settings exist. Otherwise use defaults.
+ if (_outname == "")
+ {
+ _outname = "../../out_normaltest" + strTestNo + ".yuv";
+ }
+
+ if (_encodedName == "")
+ {
+ _encodedName = "../../encoded_normaltest" + strTestNo + ".yuv";
+ }
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+
+ char mode[3] = "wb";
+ if (_appendNext)
+ {
+ strncpy(mode, "ab", 3);
+ }
+
+ if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL)
+ {
+ printf("Cannot write file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ _appendNext = true;
+}
+
+void
+NormalTest::Teardown()
+{
+ Test::Teardown();
+ fclose(_sourceFile);
+ fclose(_decodedFile);
+}
+
+void
+NormalTest::Perform()
+{
+ _inname = "../../../../testFiles/foreman.yuv";
+ CodecSettings(352, 288, 30, _bitRate);
+ Setup();
+
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+
+ _encoder->InitEncode(&_inst, 1, 1460);
+ CodecSpecific_InitBitrate();
+ _decoder->InitDecode(&_inst,1);
+
+ _totalEncodeTime = _totalDecodeTime = 0;
+ _framecnt = 0;
+ _sumEncBytes = 0;
+ _lengthEncFrame = 0;
+ int decodeLength = 0;
+ while (!Encode())
+ {
+ DoPacketLoss();
+ _encodedVideoBuffer.UpdateLength(_encodedVideoBuffer.GetLength());
+ fwrite(_encodedVideoBuffer.GetBuffer(), 1, _encodedVideoBuffer.GetLength(), _encodedFile);
+ decodeLength = Decode();
+ if (decodeLength < 0)
+ {
+ fprintf(stderr,"\n\nError in decoder: %d\n\n", decodeLength);
+ exit(EXIT_FAILURE);
+ }
+ fwrite(_decodedVideoBuffer.GetBuffer(), 1, decodeLength, _decodedFile);
+ CodecSpecific_InitBitrate();
+ _framecnt++;
+ }
+
+ // Ensure we empty the decoding queue.
+ while (decodeLength > 0)
+ {
+ decodeLength = Decode();
+ if (decodeLength < 0)
+ {
+ fprintf(stderr,"\n\nError in decoder: %d\n\n", decodeLength);
+ exit(EXIT_FAILURE);
+ }
+ fwrite(_decodedVideoBuffer.GetBuffer(), 1, decodeLength, _decodedFile);
+ }
+
+ double actualBitRate = ActualBitRate(_framecnt) / 1000.0;
+ double avgEncTime = _totalEncodeTime / _framecnt;
+ double avgDecTime = _totalDecodeTime / _framecnt;
+ printf("Actual bitrate: %f kbps\n", actualBitRate);
+ printf("Average encode time: %f s\n", avgEncTime);
+ printf("Average decode time: %f s\n", avgDecTime);
+ (*_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;
+
+ _inputVideoBuffer.Free();
+ _encodedVideoBuffer.Reset();
+ _decodedVideoBuffer.Free();
+
+ _encoder->Release();
+ _decoder->Release();
+
+ Teardown();
+}
+
+bool
+NormalTest::Encode()
+{
+ _lengthEncFrame = 0;
+ fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile);
+ if (feof(_sourceFile) != 0)
+ {
+ return true;
+ }
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
+ _inputVideoBuffer.SetTimeStamp(_framecnt);
+
+ // This multiple attempt ridiculousness is to accomodate VP7:
+ // 1. The wrapper can unilaterally reduce the framerate for low bitrates.
+ // 2. The codec inexplicably likes to reject some frames. Perhaps there
+ // is a good reason for this...
+ int encodingAttempts = 0;
+ double starttime = 0;
+ double endtime = 0;
+ while (_lengthEncFrame == 0)
+ {
+ starttime = clock()/(double)CLOCKS_PER_SEC;
+
+ _inputVideoBuffer.SetWidth(_inst.width);
+ _inputVideoBuffer.SetHeight(_inst.height);
+ //_lengthEncFrame = _encoder->Encode(_inputVideoBuffer, _encodedVideoBuffer, _frameInfo,
+ // _inst.frameRate, _requestKeyFrame && !(_framecnt%50));
+
+ endtime = clock()/(double)CLOCKS_PER_SEC;
+
+ _encodedVideoBuffer.SetCaptureHeight(_inst.height);
+ _encodedVideoBuffer.SetCaptureWidth(_inst.width);
+ if (_lengthEncFrame < 0)
+ {
+ (*_log) << "Error in encoder: " << _lengthEncFrame << std::endl;
+ fprintf(stderr,"\n\nError in encoder: %d\n\n", _lengthEncFrame);
+ exit(EXIT_FAILURE);
+ }
+ _sumEncBytes += _lengthEncFrame;
+
+ encodingAttempts++;
+ if (encodingAttempts > 50)
+ {
+ (*_log) << "Unable to encode frame: " << _framecnt << std::endl;
+ fprintf(stderr,"\n\nUnable to encode frame: %d\n\n", _framecnt);
+ exit(EXIT_FAILURE);
+ }
+ }
+ _totalEncodeTime += endtime - starttime;
+
+ if (encodingAttempts > 1)
+ {
+ (*_log) << encodingAttempts << " attempts required to encode frame: " <<
+ _framecnt + 1 << std::endl;
+ fprintf(stderr,"\n%d attempts required to encode frame: %d\n", encodingAttempts,
+ _framecnt + 1);
+ }
+
+ return false;
+}
+
+int
+NormalTest::Decode()
+{
+ double starttime = clock()/(double)CLOCKS_PER_SEC;
+ _encodedVideoBuffer.SetWidth(_inst.width);
+ _encodedVideoBuffer.SetHeight(_inst.height);
+ int lengthDecFrame = 0;
+ //int lengthDecFrame = _decoder->Decode(_encodedVideoBuffer, _decodedVideoBuffer);
+ //_totalDecodeTime += (double)((clock()/(double)CLOCKS_PER_SEC) - starttime);
+ if (lengthDecFrame < 0)
+ {
+ return lengthDecFrame;
+ }
+ _encodedVideoBuffer.Reset();
+ _encodedVideoBuffer.UpdateLength(0);
+ return lengthDecFrame;
+}
+
diff --git a/src/modules/video_coding/codecs/test_framework/normal_test.h b/src/modules/video_coding/codecs/test_framework/normal_test.h
new file mode 100644
index 0000000..c7bb676
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/normal_test.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_
+
+#include "test.h"
+
+class NormalTest : public Test
+{
+public:
+ NormalTest();
+ NormalTest(std::string name, std::string description, unsigned int testNo);
+ NormalTest(std::string name, std::string description, WebRtc_UWord32 bitRate, unsigned int testNo);
+ virtual ~NormalTest() {};
+ virtual void Perform();
+
+protected:
+ virtual void Setup();
+ virtual void Teardown();
+ virtual bool Encode();
+ virtual int Decode();
+ virtual void CodecSpecific_InitBitrate()=0;
+ virtual int DoPacketLoss() {return 0;};
+
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ FILE* _encodedFile;
+ double _totalEncodeTime;
+ double _totalDecodeTime;
+ unsigned int _framecnt;
+ bool _requestKeyFrame;
+ unsigned int _testNo;
+ int _lengthEncFrame;
+ bool _appendNext;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_
+
diff --git a/src/modules/video_coding/codecs/test_framework/packet_loss_test.cc b/src/modules/video_coding/codecs/test_framework/packet_loss_test.cc
new file mode 100644
index 0000000..5b7f9c4
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/packet_loss_test.cc
@@ -0,0 +1,244 @@
+/*
+ * 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 "packet_loss_test.h"
+#include "video_source.h"
+#include <sstream>
+#include <cassert>
+#include <string.h>
+
+using namespace webrtc;
+
+PacketLossTest::PacketLossTest()
+:
+_lossRate(0.1),
+_lossProbability(0.1),
+_lastFrame(NULL),
+_lastFrameLength(0),
+NormalAsyncTest("PacketLossTest", "Encode, remove lost packets, decode", 300, 5)
+{
+}
+
+PacketLossTest::PacketLossTest(std::string name, std::string description)
+:
+_lossRate(0.1),
+_lossProbability(0.1),
+_lastFrame(NULL),
+_lastFrameLength(0),
+NormalAsyncTest(name, description, 300, 5)
+{
+}
+
+PacketLossTest::PacketLossTest(std::string name, std::string description, double lossRate, bool useNack, unsigned int rttFrames /* = 0*/)
+:
+_lossRate(lossRate),
+_lastFrame(NULL),
+_lastFrameLength(0),
+NormalAsyncTest(name, description, 300, 5, rttFrames)
+{
+ assert(lossRate >= 0 && lossRate <= 1);
+ if (useNack)
+ {
+ _lossProbability = 0;
+ }
+ else
+ {
+ _lossProbability = lossRate;
+ }
+}
+
+void
+PacketLossTest::Encoded(const EncodedImage& encodedImage)
+{
+ // push timestamp to queue
+ _frameQueue.push_back(encodedImage._timeStamp);
+ NormalAsyncTest::Encoded(encodedImage);
+}
+
+void
+PacketLossTest::Decoded(const RawImage& decodedImage)
+{
+ // check the frame queue if any frames have gone missing
+ assert(!_frameQueue.empty()); // decoded frame is not in the queue
+ while(_frameQueue.front() < decodedImage._timeStamp)
+ {
+ // this frame is missing
+ // write previous decoded frame again (frame freeze)
+ if (_decodedFile && _lastFrame)
+ {
+ fwrite(_lastFrame, 1, _lastFrameLength, _decodedFile);
+ }
+
+ // remove frame from queue
+ _frameQueue.pop_front();
+ }
+ assert(_frameQueue.front() == decodedImage._timeStamp); // decoded frame is not in the queue
+
+ // pop the current frame
+ _frameQueue.pop_front();
+
+ // save image for future freeze-frame
+ if (_lastFrameLength < decodedImage._length)
+ {
+ if (_lastFrame) delete [] _lastFrame;
+
+ _lastFrame = new WebRtc_UWord8[decodedImage._length];
+ }
+ memcpy(_lastFrame, decodedImage._buffer, decodedImage._length);
+ _lastFrameLength = decodedImage._length;
+
+ NormalAsyncTest::Decoded(decodedImage);
+}
+
+void
+PacketLossTest::Teardown()
+{
+ if (_totalKept + _totalThrown > 0)
+ {
+ printf("Target packet loss rate: %.4f\n", _lossProbability);
+ printf("Actual packet loss rate: %.4f\n", (_totalThrown * 1.0f) / (_totalKept + _totalThrown));
+ printf("Channel rate: %.2f kbps\n",
+ 0.001 * 8.0 * _sumChannelBytes / ((_framecnt * 1.0f) / _inst.maxFramerate));
+ }
+ else
+ {
+ printf("No packet losses inflicted\n");
+ }
+
+ NormalAsyncTest::Teardown();
+}
+
+void
+PacketLossTest::Setup()
+{
+ const VideoSource source(_inname, _inst.width, _inst.height, _inst.maxFramerate);
+
+ std::stringstream ss;
+ std::string lossRateStr;
+ ss << _lossRate;
+ ss >> lossRateStr;
+ _encodedName = "../../" + source.GetName() + "-" + lossRateStr;
+ _outname = "../../out-" + source.GetName() + "-" + lossRateStr;
+
+ if (_lossProbability != _lossRate)
+ {
+ _encodedName += "-nack";
+ _outname += "-nack";
+ }
+ _encodedName += ".vp8";
+ _outname += ".yuv";
+
+ _totalKept = 0;
+ _totalThrown = 0;
+ _sumChannelBytes = 0;
+
+ NormalAsyncTest::Setup();
+}
+
+void
+PacketLossTest::CodecSpecific_InitBitrate()
+{
+ assert(_bitRate > 0);
+ WebRtc_UWord32 simulatedBitRate;
+ if (_lossProbability != _lossRate)
+ {
+ // Simulating NACK
+ simulatedBitRate = WebRtc_UWord32(_bitRate / (1 + _lossRate));
+ }
+ else
+ {
+ simulatedBitRate = _bitRate;
+ }
+ _encoder->SetPacketLoss((WebRtc_UWord32)(_lossProbability * 255.0));
+ _encoder->SetRates(simulatedBitRate, _inst.maxFramerate);
+}
+
+int PacketLossTest::DoPacketLoss()
+{
+ // Only packet loss for delta frames
+ if (_frameToDecode->_frame->GetLength() == 0 || _frameToDecode->_frame->GetFrameType() != kDeltaFrame)
+ {
+ _sumChannelBytes += _frameToDecode->_frame->GetLength();
+ return 0;
+ }
+ //printf("Encoded: %d bytes\n", _encodedVideoBuffer.GetLength());
+ unsigned char *packet = NULL;
+ TestVideoEncodedBuffer newEncBuf;
+ newEncBuf.VerifyAndAllocate(_lengthSourceFrame);
+ _inBufIdx = 0;
+ _outBufIdx = 0;
+ int size = 1;
+ int kept = 0;
+ int thrown = 0;
+ int count = 0;
+ while ((size = NextPacket(1500, &packet)) > 0)
+ {
+ if (!PacketLoss(_lossProbability))
+ {
+ InsertPacket(&newEncBuf, packet, size);
+ kept++;
+ }
+ else
+ {
+ // Use the ByteLoss function if you want to lose only
+ // parts of a packet, and not the whole packet.
+
+ //int size2 = ByteLoss(size, packet, 15);
+ thrown++;
+ //if (size2 != size)
+ //{
+ // InsertPacket(&newEncBuf, packet, size2);
+ //}
+ }
+ }
+ int lossResult = (thrown!=0); // 0 = no loss 1 = loss(es)
+ if (lossResult)
+ {
+ lossResult += (kept==0); // 2 = all lost = full frame
+ }
+ _frameToDecode->_frame->CopyBuffer(newEncBuf.GetLength(), newEncBuf.GetBuffer());
+ _sumChannelBytes += newEncBuf.GetLength();
+ _totalKept += kept;
+ _totalThrown += thrown;
+ return lossResult;
+ //printf("Threw away: %d out of %d packets\n", thrown, thrown + kept);
+ //printf("Encoded left: %d bytes\n", _encodedVideoBuffer.GetLength());
+}
+
+int PacketLossTest::NextPacket(int mtu, unsigned char **pkg)
+{
+ unsigned char *buf = _frameToDecode->_frame->GetBuffer();
+ *pkg = buf + _inBufIdx;
+ if (static_cast<long>(_frameToDecode->_frame->GetLength()) - _inBufIdx <= mtu)
+ {
+ int size = _frameToDecode->_frame->GetLength() - _inBufIdx;
+ _inBufIdx = _frameToDecode->_frame->GetLength();
+ return size;
+ }
+ _inBufIdx += mtu;
+ return mtu;
+}
+
+int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose)
+{
+ return size;
+}
+
+void PacketLossTest::InsertPacket(TestVideoEncodedBuffer *buf, unsigned char *pkg, int size)
+{
+ if (static_cast<long>(buf->GetSize()) - _outBufIdx < size)
+ {
+ printf("InsertPacket error!\n");
+ return;
+ }
+ memcpy(buf->GetBuffer() + _outBufIdx, pkg, size);
+ buf->UpdateLength(buf->GetLength() + size);
+ _outBufIdx += size;
+}
diff --git a/src/modules/video_coding/codecs/test_framework/packet_loss_test.h b/src/modules/video_coding/codecs/test_framework/packet_loss_test.h
new file mode 100644
index 0000000..ea37681
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/packet_loss_test.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_
+
+#include <list>
+
+#include "normal_async_test.h"
+
+class PacketLossTest : public NormalAsyncTest
+{
+public:
+ PacketLossTest();
+ virtual ~PacketLossTest() {if(_lastFrame) {delete [] _lastFrame; _lastFrame = NULL;}}
+ virtual void Encoded(const webrtc::EncodedImage& encodedImage);
+ virtual void Decoded(const webrtc::RawImage& decodedImage);
+protected:
+ PacketLossTest(std::string name, std::string description);
+ PacketLossTest(std::string name,
+ std::string description,
+ double lossRate,
+ bool useNack,
+ unsigned int rttFrames = 0);
+
+ virtual void Setup();
+ virtual void Teardown();
+ virtual void CodecSpecific_InitBitrate();
+ virtual int DoPacketLoss();
+ virtual int NextPacket(int size, unsigned char **pkg);
+ virtual int ByteLoss(int size, unsigned char *pkg, int bytesToLose);
+ virtual void InsertPacket(TestVideoEncodedBuffer *buf, unsigned char *pkg, int size);
+ int _inBufIdx;
+ int _outBufIdx;
+
+ // When NACK is being simulated _lossProbabilty is zero,
+ // otherwise it is set equal to _lossRate.
+ // Desired channel loss rate.
+ double _lossRate;
+ // Probability used to simulate packet drops.
+ double _lossProbability;
+
+ int _totalKept;
+ int _totalThrown;
+ int _sumChannelBytes;
+ std::list<WebRtc_UWord32> _frameQueue;
+ WebRtc_UWord8* _lastFrame;
+ WebRtc_UWord32 _lastFrameLength;
+};
+
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_
diff --git a/src/modules/video_coding/codecs/test_framework/performance_test.cc b/src/modules/video_coding/codecs/test_framework/performance_test.cc
new file mode 100644
index 0000000..b160625
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/performance_test.cc
@@ -0,0 +1,290 @@
+/*
+ * 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 "performance_test.h"
+#include "tick_util.h"
+#include <assert.h>
+
+using namespace webrtc;
+
+#define NUM_FRAMES 300
+
+PerformanceTest::PerformanceTest(WebRtc_UWord32 bitRate)
+:
+_numCodecs(0),
+_tests(NULL),
+_encoders(NULL),
+_decoders(NULL),
+_threads(NULL),
+_rawImageLock(NULL),
+_encodeEvents(new EventWrapper*[1]),
+_stopped(true),
+_encodeCompleteCallback(NULL),
+_decodeCompleteCallback(NULL),
+NormalAsyncTest(bitRate)
+{
+}
+
+PerformanceTest::PerformanceTest(WebRtc_UWord32 bitRate, WebRtc_UWord8 numCodecs)
+:
+_numCodecs(numCodecs),
+_tests(new PerformanceTest*[_numCodecs]),
+_encoders(new VideoEncoder*[_numCodecs]),
+_decoders(new VideoDecoder*[_numCodecs]),
+_threads(new ThreadWrapper*[_numCodecs]),
+_rawImageLock(RWLockWrapper::CreateRWLock()),
+_encodeEvents(new EventWrapper*[_numCodecs]),
+_stopped(true),
+_encodeCompleteCallback(NULL),
+_decodeCompleteCallback(NULL),
+NormalAsyncTest(bitRate)
+{
+ for (int i=0; i < _numCodecs; i++)
+ {
+ _tests[i] = new PerformanceTest(bitRate);
+ _encodeEvents[i] = EventWrapper::Create();
+ }
+}
+
+PerformanceTest::~PerformanceTest()
+{
+ if (_encoders != NULL)
+ {
+ delete [] _encoders;
+ }
+ if (_decoders != NULL)
+ {
+ delete [] _decoders;
+ }
+ if (_tests != NULL)
+ {
+ delete [] _tests;
+ }
+ if (_threads != NULL)
+ {
+ delete [] _threads;
+ }
+ if (_rawImageLock != NULL)
+ {
+ delete _rawImageLock;
+ }
+ if (_encodeEvents != NULL)
+ {
+ delete [] _encodeEvents;
+ }
+}
+
+void
+PerformanceTest::Setup()
+{
+ _inname = "../../../../testFiles/foreman.yuv";
+ NormalAsyncTest::Setup(); // Setup input and output files
+ CodecSettings(352, 288, 30, _bitRate); // common to all codecs
+ for (int i=0; i < _numCodecs; i++)
+ {
+ _encoders[i] = CreateEncoder();
+ _decoders[i] = CreateDecoder();
+ if (_encoders[i] == NULL)
+ {
+ printf("Must create a codec specific test!\n");
+ exit(EXIT_FAILURE);
+ }
+ if(_encoders[i]->InitEncode(&_inst, 4, 1440) < 0)
+ {
+ exit(EXIT_FAILURE);
+ }
+ if (_decoders[i]->InitDecode(&_inst, 1))
+ {
+ exit(EXIT_FAILURE);
+ }
+ _tests[i]->SetEncoder(_encoders[i]);
+ _tests[i]->SetDecoder(_decoders[i]);
+ _tests[i]->_rawImageLock = _rawImageLock;
+ _encodeEvents[i]->Reset();
+ _tests[i]->_encodeEvents[0] = _encodeEvents[i];
+ _tests[i]->_inst = _inst;
+ _threads[i] = ThreadWrapper::CreateThread(PerformanceTest::RunThread, _tests[i]);
+ unsigned int id = 0;
+ _tests[i]->_stopped = false;
+ _threads[i]->Start(id);
+ }
+}
+
+void
+PerformanceTest::Perform()
+{
+ Setup();
+ EventWrapper& sleepEvent = *EventWrapper::Create();
+ const WebRtc_Word64 startTime = TickTime::MillisecondTimestamp();
+ for (int i=0; i < NUM_FRAMES; i++)
+ {
+ {
+ // Read a new frame from file
+ WriteLockScoped imageLock(*_rawImageLock);
+ _lengthEncFrame = 0;
+ fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile);
+ if (feof(_sourceFile) != 0)
+ {
+ rewind(_sourceFile);
+ }
+ _inputVideoBuffer.VerifyAndAllocate(_inst.width*_inst.height*3/2);
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
+ _inputVideoBuffer.SetTimeStamp((unsigned int) (_encFrameCnt * 9e4 / static_cast<float>(_inst.maxFramerate)));
+ _inputVideoBuffer.SetWidth(_inst.width);
+ _inputVideoBuffer.SetHeight(_inst.height);
+ for (int i=0; i < _numCodecs; i++)
+ {
+ _tests[i]->_inputVideoBuffer.CopyPointer(_inputVideoBuffer);
+ _encodeEvents[i]->Set();
+ }
+ }
+ if (i < NUM_FRAMES - 1)
+ {
+ sleepEvent.Wait(33);
+ }
+ }
+ for (int i=0; i < _numCodecs; i++)
+ {
+ _tests[i]->_stopped = true;
+ _encodeEvents[i]->Set();
+ _threads[i]->Stop();
+ }
+ const WebRtc_UWord32 totalTime =
+ static_cast<WebRtc_UWord32>(TickTime::MillisecondTimestamp() - startTime);
+ printf("Total time: %u\n", totalTime);
+ delete &sleepEvent;
+ Teardown();
+}
+
+void PerformanceTest::Teardown()
+{
+ if (_encodeCompleteCallback != NULL)
+ {
+ delete _encodeCompleteCallback;
+ }
+ if (_decodeCompleteCallback != NULL)
+ {
+ delete _decodeCompleteCallback;
+ }
+ // main test only, all others have numCodecs = 0:
+ if (_numCodecs > 0)
+ {
+ WriteLockScoped imageLock(*_rawImageLock);
+ _inputVideoBuffer.Free();
+ NormalAsyncTest::Teardown();
+ }
+ for (int i=0; i < _numCodecs; i++)
+ {
+ _encoders[i]->Release();
+ delete _encoders[i];
+ _decoders[i]->Release();
+ delete _decoders[i];
+ _tests[i]->_inputVideoBuffer.ClearPointer();
+ _tests[i]->_rawImageLock = NULL;
+ _tests[i]->Teardown();
+ delete _tests[i];
+ delete _encodeEvents[i];
+ delete _threads[i];
+ }
+}
+
+bool
+PerformanceTest::RunThread(void* obj)
+{
+ PerformanceTest& test = *static_cast<PerformanceTest*>(obj);
+ return test.PerformSingleTest();
+}
+
+bool
+PerformanceTest::PerformSingleTest()
+{
+ if (_encodeCompleteCallback == NULL)
+ {
+ _encodeCompleteCallback = new VideoEncodeCompleteCallback(NULL, &_frameQueue, *this);
+ _encoder->RegisterEncodeCompleteCallback(_encodeCompleteCallback);
+ }
+ if (_decodeCompleteCallback == NULL)
+ {
+ _decodeCompleteCallback = new VideoDecodeCompleteCallback(NULL, *this);
+ _decoder->RegisterDecodeCompleteCallback(_decodeCompleteCallback);
+ }
+ (*_encodeEvents)->Wait(WEBRTC_EVENT_INFINITE); // The first event is used for every single test
+ CodecSpecific_InitBitrate();
+ bool complete = false;
+ {
+ ReadLockScoped imageLock(*_rawImageLock);
+ complete = Encode();
+ }
+ 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);
+ return false;
+ }
+ else if (ret < 0)
+ {
+ fprintf(stderr, "\n\nPositive return value from decode!\n\n");
+ return false;
+ }
+ }
+ }
+ if (_stopped)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool PerformanceTest::Encode()
+{
+ RawImage rawImage;
+ VideoBufferToRawImage(_inputVideoBuffer, rawImage);
+ VideoFrameType frameType = kDeltaFrame;
+ if (_requestKeyFrame && !(_encFrameCnt%50))
+ {
+ frameType = kKeyFrame;
+ }
+ void* codecSpecificInfo = CreateEncoderSpecificInfo();
+ int ret = _encoder->Encode(rawImage, codecSpecificInfo, frameType);
+ if (codecSpecificInfo != NULL)
+ {
+ // TODO(holmer): implement virtual function for deleting this and remove warnings
+ delete codecSpecificInfo;
+ codecSpecificInfo = NULL;
+ }
+ assert(ret >= 0);
+ return false;
+}
+
+int PerformanceTest::Decode(int lossValue)
+{
+ EncodedImage encodedImage;
+ VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage);
+ encodedImage._completeFrame = !lossValue;
+ int ret = _decoder->Decode(encodedImage, _missingFrames, _frameToDecode->_codecSpecificInfo);
+ _missingFrames = false;
+ return ret;
+}
diff --git a/src/modules/video_coding/codecs/test_framework/performance_test.h b/src/modules/video_coding/codecs/test_framework/performance_test.h
new file mode 100644
index 0000000..d060832
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/performance_test.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_
+
+#include "normal_async_test.h"
+#include "thread_wrapper.h"
+#include "rw_lock_wrapper.h"
+#include "event_wrapper.h"
+
+class PerformanceTest : public NormalAsyncTest
+{
+public:
+ PerformanceTest(WebRtc_UWord32 bitRate, WebRtc_UWord8 numCodecs);
+ virtual ~PerformanceTest();
+
+ virtual void Perform();
+ virtual void Print() {};
+
+protected:
+ PerformanceTest(WebRtc_UWord32 bitRate);
+ virtual void Setup();
+ virtual bool Encode();
+ virtual int Decode(int lossValue = 0);
+ virtual void Teardown();
+ static bool RunThread(void* obj);
+ bool PerformSingleTest();
+
+ virtual webrtc::VideoEncoder* CreateEncoder() const { return NULL; };
+ virtual webrtc::VideoDecoder* CreateDecoder() const { return NULL; };
+
+ WebRtc_UWord8 _numCodecs;
+ PerformanceTest** _tests;
+ webrtc::VideoEncoder** _encoders;
+ webrtc::VideoDecoder** _decoders;
+ webrtc::ThreadWrapper** _threads;
+ webrtc::RWLockWrapper* _rawImageLock;
+ webrtc::EventWrapper** _encodeEvents;
+ FrameQueue _frameQueue;
+ bool _stopped;
+ webrtc::EncodedImageCallback* _encodeCompleteCallback;
+ webrtc::DecodedImageCallback* _decodeCompleteCallback;
+ FILE* _outFile;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_
diff --git a/src/modules/video_coding/codecs/test_framework/plotBenchmark.m b/src/modules/video_coding/codecs/test_framework/plotBenchmark.m
new file mode 100644
index 0000000..33c8eb6
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/plotBenchmark.m
@@ -0,0 +1,427 @@
+function plotBenchmark(fileNames, export)
+%PLOTBENCHMARK Plots and exports video codec benchmarking results.
+% PLOTBENCHMARK(FILENAMES, EXPORT) parses the video codec benchmarking result
+% files given by the cell array of strings FILENAME. It plots the results and
+% optionally exports each plot to an appropriately named file.
+%
+% EXPORT parameter:
+% 'none' No file exports.
+% 'eps' Exports to eps files (default).
+% 'pdf' Exports to eps files and uses the command-line utility
+% epstopdf to obtain pdf files.
+%
+% Example:
+% plotBenchmark({'H264Benchmark.txt' 'LSVXBenchmark.txt'}, 'pdf')
+
+if (nargin < 1)
+ error('Too few input arguments');
+elseif (nargin < 2)
+ export = 'eps';
+end
+
+if ~iscell(fileNames)
+ if ischar(fileNames)
+ % one single file name as a string is ok
+ if size(fileNames,1) > 1
+ % this is a char matrix, not ok
+ error('First argument must not be a char matrix');
+ end
+ % wrap in a cell array
+ fileNames = {fileNames};
+ else
+ error('First argument must be a cell array of strings');
+ end
+end
+
+if ~ischar(export)
+ error('Second argument must be a string');
+end
+
+outpath = 'BenchmarkPlots';
+[status, errMsg] = mkdir(outpath);
+if status == 0
+ error(errMsg);
+end
+
+nCases = 0;
+testCases = [];
+% Read each test result file
+for fileIdx = 1:length(fileNames)
+ if ~isstr(fileNames{fileIdx})
+ error('First argument must be a cell array of strings');
+ end
+
+ fid = fopen(fileNames{fileIdx}, 'rt');
+ if fid == -1
+ error(['Unable to open ' fileNames{fileIdx}]);
+ end
+
+ version = '1.0';
+ if ~strcmp(fgetl(fid), ['#!benchmark' version])
+ fclose(fid);
+ error(['Requires benchmark file format version ' version]);
+ end
+
+ % Parse results file into testCases struct
+ codec = fgetl(fid);
+ tline = fgetl(fid);
+ while(tline ~= -1)
+ nCases = nCases + 1;
+
+ delim = strfind(tline, ',');
+ name = tline(1:delim(1)-1);
+ % Drop underscored suffix from name
+ underscore = strfind(name, '_');
+ if ~isempty(underscore)
+ name = name(1:underscore(1)-1);
+ end
+
+ resolution = tline(delim(1)+1:delim(2)-1);
+ frameRate = tline(delim(2)+1:end);
+
+ tline = fgetl(fid);
+ delim = strfind(tline, ',');
+ bitrateLabel = tline(1:delim(1)-1);
+ bitrate = sscanf(tline(delim(1):end),',%f');
+
+ tline = fgetl(fid);
+ delim = strfind(tline, ',');
+ psnrLabel = tline(1:delim(1)-1);
+ psnr = sscanf(tline(delim(1):end),',%f');
+
+
+ % Default data for the optional lines
+ speedLabel = 'Default';
+ speed = 0;
+ ssimLabel = 'Default';
+ ssim = 0;
+
+ tline = fgetl(fid);
+ delim = strfind(tline, ',');
+
+ while ~isempty(delim)
+ % More data
+ % Check type of data
+ if strncmp(lower(tline), 'speed', 5)
+ % Speed data included
+ speedLabel = tline(1:delim(1)-1);
+ speed = sscanf(tline(delim(1):end), ',%f');
+
+ tline = fgetl(fid);
+
+ elseif strncmp(lower(tline), 'encode time', 11)
+ % Encode and decode times included
+ % TODO: take care of the data
+
+ % pop two lines from file
+ tline = fgetl(fid);
+ tline = fgetl(fid);
+
+ elseif strncmp(tline, 'SSIM', 4)
+ % SSIM data included
+ ssimLabel = tline(1:delim(1)-1);
+ ssim = sscanf(tline(delim(1):end), ',%f');
+
+ tline = fgetl(fid);
+ end
+ delim = strfind(tline, ',');
+ end
+
+ testCases = [testCases struct('codec', codec, 'name', name, 'resolution', ...
+ resolution, 'frameRate', frameRate, 'bitrate', bitrate, 'psnr', psnr, ...
+ 'speed', speed, 'bitrateLabel', bitrateLabel, 'psnrLabel', psnrLabel, ...
+ 'speedLabel', speedLabel, ...
+ 'ssim', ssim, 'ssimLabel', ssimLabel)];
+
+ tline = fgetl(fid);
+ end
+
+ fclose(fid);
+end
+
+i = 0;
+casesPsnr = testCases;
+while ~isempty(casesPsnr)
+ i = i + 1;
+ casesPsnr = plotOnePsnr(casesPsnr, i, export, outpath);
+end
+
+casesSSIM = testCases;
+while ~isempty(casesSSIM)
+ i = i + 1;
+ casesSSIM = plotOneSSIM(casesSSIM, i, export, outpath);
+end
+
+casesSpeed = testCases;
+while ~isempty(casesSpeed)
+ if casesSpeed(1).speed == 0
+ casesSpeed = casesSpeed(2:end);
+ else
+ i = i + 1;
+ casesSpeed = plotOneSpeed(casesSpeed, i, export, outpath);
+ end
+end
+
+
+
+%%%%%%%%%%%%%%%%%%
+%% SUBFUNCTIONS %%
+%%%%%%%%%%%%%%%%%%
+
+function casesOut = plotOnePsnr(cases, num, export, outpath)
+% Find matching specs
+plotIdx = 1;
+for i = 2:length(cases)
+ if strcmp(cases(1).resolution, cases(i).resolution) & ...
+ strcmp(cases(1).frameRate, cases(i).frameRate)
+ plotIdx = [plotIdx i];
+ end
+end
+
+% Return unplotted cases
+casesOut = cases(setdiff(1:length(cases), plotIdx));
+cases = cases(plotIdx);
+
+% Prune similar results
+for i = 1:length(cases)
+ simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10);
+ while ~isempty(simIndx)
+ diffIndx = setdiff(1:length(cases(i).bitrate), simIndx);
+ cases(i).psnr = cases(i).psnr(diffIndx);
+ cases(i).bitrate = cases(i).bitrate(diffIndx);
+ simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10);
+ end
+end
+
+% Prepare figure with axis labels and so on
+hFig = figure(num);
+clf;
+hold on;
+grid on;
+axis([0 1100 20 50]);
+set(gca, 'XTick', 0:200:1000);
+set(gca, 'YTick', 20:10:60);
+xlabel(cases(1).bitrateLabel);
+ylabel(cases(1).psnrLabel);
+res = cases(1).resolution;
+frRate = cases(1).frameRate;
+title([res ', ' frRate]);
+
+hLines = [];
+codecs = {};
+sequences = {};
+i = 0;
+while ~isempty(cases)
+ i = i + 1;
+ [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'psnr', i, sequences, 1);
+
+ % Stored to generate the legend
+ hLines = [hLines ; hLine];
+ codecs = {codecs{:} codec};
+end
+legend(hLines, codecs, 4);
+hold off;
+
+if ~strcmp(export, 'none')
+ % Export figure to an eps file
+ res = stripws(res);
+ frRate = stripws(frRate);
+ exportName = [outpath '/psnr-' res '-' frRate];
+ exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk');
+end
+
+if strcmp(export, 'pdf')
+ % Use the epstopdf utility to convert to pdf
+ system(['epstopdf ' exportName '.eps']);
+end
+
+
+function casesOut = plotOneSSIM(cases, num, export, outpath)
+% Find matching specs
+plotIdx = 1;
+for i = 2:length(cases)
+ if strcmp(cases(1).resolution, cases(i).resolution) & ...
+ strcmp(cases(1).frameRate, cases(i).frameRate)
+ plotIdx = [plotIdx i];
+ end
+end
+
+% Return unplotted cases
+casesOut = cases(setdiff(1:length(cases), plotIdx));
+cases = cases(plotIdx);
+
+% Prune similar results
+for i = 1:length(cases)
+ simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10);
+ while ~isempty(simIndx)
+ diffIndx = setdiff(1:length(cases(i).bitrate), simIndx);
+ cases(i).ssim = cases(i).ssim(diffIndx);
+ cases(i).bitrate = cases(i).bitrate(diffIndx);
+ simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10);
+ end
+end
+
+% Prepare figure with axis labels and so on
+hFig = figure(num);
+clf;
+hold on;
+grid on;
+axis([0 1100 0.5 1]); % y-limit are set to 'auto' below
+set(gca, 'XTick', 0:200:1000);
+%set(gca, 'YTick', 20:10:60);
+xlabel(cases(1).bitrateLabel);
+ylabel(cases(1).ssimLabel);
+res = cases(1).resolution;
+frRate = cases(1).frameRate;
+title([res ', ' frRate]);
+
+hLines = [];
+codecs = {};
+sequences = {};
+i = 0;
+while ~isempty(cases)
+ i = i + 1;
+ [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'ssim', i, sequences, 1);
+
+ % Stored to generate the legend
+ hLines = [hLines ; hLine];
+ codecs = {codecs{:} codec};
+end
+%set(gca,'YLimMode','auto')
+set(gca,'YLim',[0.5 1])
+set(gca,'YScale','log')
+legend(hLines, codecs, 4);
+hold off;
+
+if ~strcmp(export, 'none')
+ % Export figure to an eps file
+ res = stripws(res);
+ frRate = stripws(frRate);
+ exportName = [outpath '/psnr-' res '-' frRate];
+ exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk');
+end
+
+if strcmp(export, 'pdf')
+ % Use the epstopdf utility to convert to pdf
+ system(['epstopdf ' exportName '.eps']);
+end
+
+
+function casesOut = plotOneSpeed(cases, num, export, outpath)
+% Find matching specs
+plotIdx = 1;
+for i = 2:length(cases)
+ if strcmp(cases(1).resolution, cases(i).resolution) & ...
+ strcmp(cases(1).frameRate, cases(i).frameRate) & ...
+ strcmp(cases(1).name, cases(i).name)
+ plotIdx = [plotIdx i];
+ end
+end
+
+% Return unplotted cases
+casesOut = cases(setdiff(1:length(cases), plotIdx));
+cases = cases(plotIdx);
+
+% Prune similar results
+for i = 1:length(cases)
+ simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25);
+ while ~isempty(simIndx)
+ diffIndx = setdiff(1:length(cases(i).psnr), simIndx);
+ cases(i).psnr = cases(i).psnr(diffIndx);
+ cases(i).speed = cases(i).speed(diffIndx);
+ simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25);
+ end
+end
+
+hFig = figure(num);
+clf;
+hold on;
+%grid on;
+xlabel(cases(1).psnrLabel);
+ylabel(cases(1).speedLabel);
+res = cases(1).resolution;
+name = cases(1).name;
+frRate = cases(1).frameRate;
+title([name ', ' res ', ' frRate]);
+
+hLines = [];
+codecs = {};
+sequences = {};
+i = 0;
+while ~isempty(cases)
+ i = i + 1;
+ [cases, hLine, codec, sequences] = plotOneCodec(cases, 'psnr', 'speed', i, sequences, 0);
+
+ % Stored to generate the legend
+ hLines = [hLines ; hLine];
+ codecs = {codecs{:} codec};
+end
+legend(hLines, codecs, 1);
+hold off;
+
+if ~strcmp(export, 'none')
+ % Export figure to an eps file
+ res = stripws(res);
+ frRate = stripws(frRate);
+ exportName = [outpath '/speed-' name '-' res '-' frRate];
+ exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk');
+end
+
+if strcmp(export, 'pdf')
+ % Use the epstopdf utility to convert to pdf
+ system(['epstopdf ' exportName '.eps']);
+end
+
+
+function [casesOut, hLine, codec, sequences] = plotOneCodec(cases, xfield, yfield, num, sequences, annotatePlot)
+plotStr = {'gx-', 'bo-', 'r^-', 'kd-', 'cx-', 'go--', 'b^--'};
+% Find matching codecs
+plotIdx = 1;
+for i = 2:length(cases)
+ if strcmp(cases(1).codec, cases(i).codec)
+ plotIdx = [plotIdx i];
+ end
+end
+
+% Return unplotted cases
+casesOut = cases(setdiff(1:length(cases), plotIdx));
+cases = cases(plotIdx);
+
+for i = 1:length(cases)
+ % Plot a single case
+ hLine = plot(getfield(cases(i), xfield), getfield(cases(i), yfield), plotStr{num}, ...
+ 'LineWidth', 1.1, 'MarkerSize', 6);
+end
+
+% hLine handle and codec are returned to construct the legend afterwards
+codec = cases(1).codec;
+
+if annotatePlot == 0
+ return;
+end
+
+for i = 1:length(cases)
+ % Print the codec name as a text label
+ % Ensure each codec is only printed once
+ sequencePlotted = 0;
+ for j = 1:length(sequences)
+ if strcmp(cases(i).name, sequences{j})
+ sequencePlotted = 1;
+ break;
+ end
+ end
+
+ if sequencePlotted == 0
+ text(getfield(cases(i), xfield, {1}), getfield(cases(i), yfield, {1}), ...
+ [' ' cases(i).name]);
+ sequences = {sequences{:} cases(i).name};
+ end
+end
+
+
+% Strip whitespace from string
+function str = stripws(str)
+if ~isstr(str)
+ error('String required');
+end
+str = str(setdiff(1:length(str), find(isspace(str) == 1)));
diff --git a/src/modules/video_coding/codecs/test_framework/test.cc b/src/modules/video_coding/codecs/test_framework/test.cc
new file mode 100644
index 0000000..b84cd19
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/test.cc
@@ -0,0 +1,534 @@
+/*
+ * 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 "test.h"
+#include "video_source.h"
+#include "vplib.h"
+#include "event_wrapper.h"
+#include "thread_wrapper.h"
+#include <iostream>
+#include <fstream>
+#include <cmath>
+#include <ctime>
+#include <string.h>
+#include <cassert>
+#include <vector>
+
+using namespace webrtc;
+
+long filesize(const char *filename); // local function defined at end of file
+
+struct SSIMcontext
+{
+ SSIMcontext() :
+ refFileName(NULL), testFileName(NULL), width(0), height(0),
+ SSIMptr(NULL), startFrame(-1), endFrame(-1), evnt(NULL) {};
+ SSIMcontext(const char *ref, const char *test, int w, int h, double *Sptr,
+ int start, int end, EventWrapper* ev) :
+ refFileName(ref), testFileName(test), width(w), height(h),
+ SSIMptr(Sptr), startFrame(start), endFrame(end), evnt(ev) {};
+ const char *refFileName;
+ const char *testFileName;
+ int width;
+ int height;
+ double *SSIMptr;
+ int startFrame;
+ int endFrame;
+ EventWrapper* evnt;
+};
+
+Test::Test(std::string name, std::string description)
+:
+_name(name),
+_description(description),
+_bitRate(0),
+_inname(""),
+_outname(""),
+_encodedName("")
+{
+ memset(&_inst, 0, sizeof(_inst));
+ unsigned int seed = static_cast<unsigned int>(0);
+ std::srand(seed);
+}
+
+Test::Test(std::string name, std::string description, WebRtc_UWord32 bitRate)
+:
+_name(name),
+_description(description),
+_bitRate(bitRate),
+_inname(""),
+_outname(""),
+_encodedName("")
+{
+ memset(&_inst, 0, sizeof(_inst));
+ unsigned int seed = static_cast<unsigned int>(0);
+ std::srand(seed);
+}
+
+void
+Test::Print()
+{
+ std::cout << _name << " completed!" << std::endl;
+ (*_log) << _name << std::endl;
+ (*_log) << _description << std::endl;
+ (*_log) << "Input file: " << _inname << std::endl;
+ (*_log) << "Output file: " << _outname << std::endl;
+ double psnr = -1.0, ssim = -1.0;
+ PSNRfromFiles(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &psnr);
+ ssim = SSIMfromFilesMT(4 /* number of threads*/);
+
+ (*_log) << "PSNR: " << psnr << std::endl;
+ std::cout << "PSNR: " << psnr << std::endl << std::endl;
+ (*_log) << "SSIM: " << ssim << std::endl;
+ std::cout << "SSIM: " << ssim << std::endl << std::endl;
+ (*_log) << std::endl;
+}
+
+void
+Test::Setup()
+{
+ int widhei = _inst.width*_inst.height;
+ _lengthSourceFrame = 3*widhei/2;
+ _sourceBuffer = new unsigned char[_lengthSourceFrame];
+}
+
+void
+Test::CodecSettings(int width, int height, WebRtc_UWord32 frameRate /*=30*/, WebRtc_UWord32 bitRate /*=0*/)
+{
+ if (bitRate > 0)
+ {
+ _bitRate = bitRate;
+ }
+ else if (_bitRate == 0)
+ {
+ _bitRate = 600;
+ }
+ _inst.maxFramerate = (unsigned char)frameRate;
+ _inst.startBitrate = (int)_bitRate;
+ _inst.maxBitrate = 8000;
+ _inst.width = width;
+ _inst.height = height;
+}
+
+void
+Test::Teardown()
+{
+ delete [] _sourceBuffer;
+}
+
+void
+Test::SetEncoder(webrtc::VideoEncoder*encoder)
+{
+ _encoder = encoder;
+}
+
+void
+Test::SetDecoder(VideoDecoder*decoder)
+{
+ _decoder = decoder;
+}
+
+void
+Test::SetLog(std::fstream* log)
+{
+ _log = log;
+}
+
+int
+Test::PSNRfromFiles(const char *refFileName, const char *testFileName, int width, int height, double *YPSNRptr)
+{
+ FILE *refFp = fopen(refFileName, "rb");
+ if( refFp == NULL ) {
+ // cannot open reference file
+ fprintf(stderr, "Cannot open file %s\n", refFileName);
+ return -1;
+ }
+
+ FILE *testFp = fopen(testFileName, "rb");
+ if( testFp == NULL ) {
+ // cannot open test file
+ fprintf(stderr, "Cannot open file %s\n", testFileName);
+ return -2;
+ }
+
+ double mse = 0.0;
+ double mseLogSum = 0.0;
+ int frames = 0;
+
+ int frameBytes = 3*width*height/2; // bytes in one frame I420
+ unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420
+ unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420
+
+ int refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ int testBytes = (int) fread(test, 1, frameBytes, testFp);
+
+ while( refBytes == frameBytes && testBytes == frameBytes )
+ {
+ mse = 0.0;
+
+ // calculate Y sum-square-difference
+ for( int k = 0; k < width * height; k++ )
+ {
+ mse += (test[k] - ref[k]) * (test[k] - ref[k]);
+ }
+
+ // divide by number of pixels
+ mse /= (double) (width * height);
+
+ // accumulate for total average
+ mseLogSum += std::log10( mse );
+ frames++;
+
+ refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ testBytes = (int) fread(test, 1, frameBytes, testFp);
+ }
+
+ // ypsnrAvg = sum( 10 log (255^2 / MSE) ) / frames
+ // = 20 * log(255) - 10 * mseLogSum / frames
+ *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames;
+
+ delete [] ref;
+ delete [] test;
+
+ fclose(refFp);
+ fclose(testFp);
+
+ return 0;
+}
+int
+Test::SSIMfromFiles(const char *refFileName, const char *testFileName, int width, int height, double *SSIMptr,
+ int startFrame /*= -1*/, int endFrame /*= -1*/)
+{
+ FILE *refFp = fopen(refFileName, "rb");
+ if( refFp == NULL ) {
+ // cannot open reference file
+ fprintf(stderr, "Cannot open file %s\n", refFileName);
+ return -1;
+ }
+
+ FILE *testFp = fopen(testFileName, "rb");
+ if( testFp == NULL ) {
+ // cannot open test file
+ fprintf(stderr, "Cannot open file %s\n", testFileName);
+ return -2;
+ }
+
+ int frames = 0;
+
+ int frameBytes = 3*width*height/2; // bytes in one frame I420
+ unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420
+ unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420
+
+ if (startFrame >= 0)
+ {
+ if (fseek(refFp, frameBytes * startFrame, SEEK_SET) != 0){
+ fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, refFileName);
+ return -1;
+ }
+ if (fseek(testFp, frameBytes * startFrame, SEEK_SET) != 0){
+ fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, testFileName);
+ return -1;
+ }
+ }
+
+ int refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ int testBytes = (int) fread(test, 1, frameBytes, testFp);
+
+ //
+ // SSIM: variable definition, window function, initialization
+ int window = 10;
+ int flag_window = 0; //0 for uniform window filter, 1 for gaussian symmetric window
+ float variance_window = 2.0; //variance for window function
+ float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1)
+ //statistics per column of window (#columns = window+1), 0 element for avg over all columns
+ float avgTest[12];
+ float avgRef[12];
+ float contrastTest[12];
+ float contrastRef[12];
+ float crossCorr[12];
+ //
+ //offsets for stability
+ float offset1 = 0.1f;
+ float offset2 = 0.1f;
+ float offset3 = offset2/2;
+ //
+ //define window for SSIM: take uniform filter for now
+ float sumfil = 0.0;
+ int nn=-1;
+ for(int j=-window/2;j<=window/2;j++)
+ for(int i=-window/2;i<=window/2;i++)
+ {
+ nn+=1;
+ if (flag_window == 0)
+ ssimFilter[nn] = 1.0;
+ else
+ {
+ float dist = (float)(i*i) + (float)(j*j);
+ float tmp = 0.5f*dist/variance_window;
+ ssimFilter[nn] = exp(-tmp);
+ }
+ sumfil +=ssimFilter[nn];
+ }
+ //normalize window
+ nn=-1;
+ for(int j=-window/2;j<=window/2;j++)
+ for(int i=-window/2;i<=window/2;i++)
+ {
+ nn+=1;
+ ssimFilter[nn] = ssimFilter[nn]/((float)sumfil);
+ }
+ //
+ float ssimScene = 0.0; //avgerage SSIM for sequence
+ //
+ //SSIM: done with variables and defintion
+ //
+
+ while( refBytes == frameBytes && testBytes == frameBytes &&
+ !(endFrame >= 0 && frames > endFrame - startFrame))
+ {
+ float ssimFrame = 0.0;
+ int sh = window/2+1;
+ int numPixels = 0;
+ for(int i=sh;i<height-sh;i++)
+ for(int j=sh;j<width-sh;j++)
+ {
+ avgTest[0] = 0.0;
+ avgRef[0] = 0.0;
+ contrastTest[0] = 0.0;
+ contrastRef[0] = 0.0;
+ crossCorr[0] = 0.0;
+
+ numPixels +=1;
+
+ //for uniform window, only need to loop over whole window for first column pixel in image, and then shift
+ if (j == sh || flag_window == 1)
+ {
+ //initialize statistics
+ for(int k=1;k<window+2;k++)
+ {
+ avgTest[k] = 0.0;
+ avgRef[k] = 0.0;
+ contrastTest[k] = 0.0;
+ contrastRef[k] = 0.0;
+ crossCorr[k] = 0.0;
+ }
+ int nn=-1;
+ //compute contrast and correlation
+ for(int jj=-window/2;jj<=window/2;jj++)
+ for(int ii=-window/2;ii<=window/2;ii++)
+ {
+ nn+=1;
+ int i2 = i+ii;
+ int j2 = j+jj;
+ float tmp1 = (float)test[i2*width+j2];
+ float tmp2 = (float)ref[i2*width+j2];
+ //local average of each signal
+ avgTest[jj+window/2+1] += ssimFilter[nn]*tmp1;
+ avgRef[jj+window/2+1] += ssimFilter[nn]*tmp2;
+ //local correlation/contrast of each signal
+ contrastTest[jj+window/2+1] += ssimFilter[nn]*tmp1*tmp1;
+ contrastRef[jj+window/2+1] += ssimFilter[nn]*tmp2*tmp2;
+ //local cross correlation
+ crossCorr[jj+window/2+1] += ssimFilter[nn]*tmp1*tmp2;
+ }
+ }
+ //for uniform window case, can shift window horiz, then compute statistics for last column in window
+ else
+ {
+ //shift statistics horiz.
+ for(int k=1;k<window+1;k++)
+ {
+ avgTest[k]=avgTest[k+1];
+ avgRef[k]=avgRef[k+1];
+ contrastTest[k] = contrastTest[k+1];
+ contrastRef[k] = contrastRef[k+1];
+ crossCorr[k] = crossCorr[k+1];
+ }
+ //compute statistics for last column
+ avgTest[window+1] = 0.0;
+ avgRef[window+1] = 0.0;
+ contrastTest[window+1] = 0.0;
+ contrastRef[window+1] = 0.0;
+ crossCorr[window+1] = 0.0;
+ int nn = (window+1)*window - 1;
+ int jj = window/2;
+ int j2 = j + jj;
+ for(int ii=-window/2;ii<=window/2;ii++)
+ {
+ nn+=1;
+ int i2 = i+ii;
+ float tmp1 = (float)test[i2*width+j2];
+ float tmp2 = (float)ref[i2*width+j2];
+ //local average of each signal
+ avgTest[jj+window/2+1] += ssimFilter[nn]*tmp1;
+ avgRef[jj+window/2+1] += ssimFilter[nn]*tmp2;
+ //local correlation/contrast of each signal
+ contrastTest[jj+window/2+1] += ssimFilter[nn]*tmp1*tmp1;
+ contrastRef[jj+window/2+1] += ssimFilter[nn]*tmp2*tmp2;
+ //local cross correlation
+ crossCorr[jj+window/2+1] += ssimFilter[nn]*tmp1*tmp2;
+ }
+ }
+
+ //sum over all columns
+ for(int k=1;k<window+2;k++)
+ {
+ avgTest[0] += avgTest[k];
+ avgRef[0] += avgRef[k];
+ contrastTest[0] += contrastTest[k];
+ contrastRef[0] += contrastRef[k];
+ crossCorr[0] += crossCorr[k];
+ }
+
+ float tmp1 = (contrastTest[0] - avgTest[0]*avgTest[0]);
+ if (tmp1 < 0.0) tmp1 = 0.0;
+ contrastTest[0] = sqrt(tmp1);
+ float tmp2 = (contrastRef[0] - avgRef[0]*avgRef[0]);
+ if (tmp2 < 0.0) tmp2 = 0.0;
+ contrastRef[0] = sqrt(tmp2);
+ crossCorr[0] = crossCorr[0] - avgTest[0]*avgRef[0];
+
+ float ssimCorrCoeff = (crossCorr[0]+offset3)/(contrastTest[0]*contrastRef[0] + offset3);
+ float ssimLuminance = (2*avgTest[0]*avgRef[0]+offset1)/(avgTest[0]*avgTest[0] + avgRef[0]*avgRef[0] + offset1);
+ float ssimContrast = (2*contrastTest[0]*contrastRef[0]+offset2)/(contrastTest[0]*contrastTest[0] + contrastRef[0]*contrastRef[0] + offset2);
+
+ float ssimPixel = ssimCorrCoeff * ssimLuminance * ssimContrast;
+ ssimFrame += ssimPixel;
+ }
+ ssimFrame = ssimFrame / (numPixels);
+ //printf("***SSIM for frame ***%f \n",ssimFrame);
+ ssimScene += ssimFrame;
+ //
+ //SSIM: done with SSIM computation
+ //
+
+ frames++;
+
+ refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ testBytes = (int) fread(test, 1, frameBytes, testFp);
+
+ }
+
+ //SSIM: normalize/average for sequence
+ ssimScene = ssimScene / frames;
+ *SSIMptr = ssimScene;
+
+ delete [] ref;
+ delete [] test;
+
+ fclose(refFp);
+ fclose(testFp);
+
+ return 0;
+}
+
+bool
+Test::SSIMthread(void *vctx)
+{
+ SSIMcontext *ctx = (SSIMcontext *) vctx;
+ SSIMfromFiles(ctx->refFileName, ctx->testFileName, ctx->width, ctx->height, ctx->SSIMptr, ctx->startFrame, ctx->endFrame);
+ ctx->evnt->Set();
+ return false;
+}
+
+double Test::SSIMfromFilesMT(const int numThreads)
+{
+ int numFrames = filesize(_inname.c_str()) / _lengthSourceFrame;
+ std::vector<int> nFramesVec(numThreads);
+ std::vector<double> ssimVec(numThreads);
+ int framesPerCore = (numFrames + numThreads - 1) / numThreads; // rounding up
+ int i = 0;
+ int nFrames;
+ for (nFrames = numFrames; nFrames >= framesPerCore; nFrames -= framesPerCore)
+ {
+ nFramesVec[i++] = framesPerCore;
+ }
+ if (nFrames > 0)
+ {
+ assert(i == numThreads - 1);
+ nFramesVec[i] = nFrames; // remainder
+ }
+
+ int frameIx = 0;
+ std::vector<EventWrapper*> eventVec(numThreads);
+ std::vector<ThreadWrapper*> threadVec(numThreads);
+ std::vector<SSIMcontext> ctxVec(numThreads);
+ for (i = 0; i < numThreads; i++)
+ {
+ eventVec[i] = EventWrapper::Create();
+ ctxVec[i] = SSIMcontext(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &ssimVec[i], frameIx, frameIx + nFramesVec[i] - 1, eventVec[i]);
+ threadVec[i] = ThreadWrapper::CreateThread(SSIMthread, &(ctxVec[i]), kLowPriority);
+ unsigned int id;
+ threadVec[i]->Start(id);
+ frameIx += nFramesVec[i];
+ }
+
+ // wait for all events
+ for (i = 0; i < numThreads; i++) {
+ eventVec[i]->Wait(100000 /* ms*/);
+ threadVec[i]->Stop();
+ delete threadVec[i];
+ delete eventVec[i];
+ }
+
+ double avgSsim = 0;
+ for (i = 0; i < numThreads; i++)
+ {
+ avgSsim += (ssimVec[i] * nFramesVec[i]);
+ }
+
+ avgSsim /= numFrames;
+ return avgSsim;
+}
+
+
+double Test::ActualBitRate(int nFrames)
+{
+ return 8.0 * _sumEncBytes / (nFrames / _inst.maxFramerate);
+}
+
+bool Test::PacketLoss(double lossRate)
+{
+ return RandUniform() < lossRate;
+}
+
+void
+Test::VideoBufferToRawImage(TestVideoBuffer& videoBuffer, RawImage &image)
+{
+ image._buffer = videoBuffer.GetBuffer();
+ image._size = videoBuffer.GetSize();
+ image._length = videoBuffer.GetLength();
+ image._width = videoBuffer.GetWidth();
+ image._height = videoBuffer.GetHeight();
+ image._timeStamp = videoBuffer.GetTimeStamp();
+}
+void
+Test::VideoEncodedBufferToEncodedImage(TestVideoEncodedBuffer& videoBuffer, EncodedImage &image)
+{
+ image._buffer = videoBuffer.GetBuffer();
+ image._length = videoBuffer.GetLength();
+ image._size = videoBuffer.GetSize();
+ image._frameType = static_cast<VideoFrameType>(videoBuffer.GetFrameType());
+ image._timeStamp = videoBuffer.GetTimeStamp();
+ image._encodedWidth = videoBuffer.GetCaptureWidth();
+ image._encodedHeight = videoBuffer.GetCaptureHeight();
+ image._completeFrame = true;
+}
+
+long filesize(const char *filename)
+{
+FILE *f = fopen(filename,"rb"); /* open the file in read only */
+
+long size = 0;
+ if (fseek(f,0,SEEK_END)==0) /* seek was successful */
+ size = ftell(f);
+ fclose(f);
+ return size;
+}
diff --git a/src/modules/video_coding/codecs/test_framework/test.h b/src/modules/video_coding/codecs/test_framework/test.h
new file mode 100644
index 0000000..3d9842b
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/test.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_
+
+#include "video_codec_interface.h"
+#include "video_buffer.h"
+#include <string>
+#include <fstream>
+#include <cstdlib>
+
+class Test
+{
+public:
+ Test(std::string name, std::string description);
+ Test(std::string name, std::string description, WebRtc_UWord32 bitRate);
+ virtual ~Test() {};
+ virtual void Perform()=0;
+ virtual void Print();
+ void SetEncoder(webrtc::VideoEncoder *encoder);
+ void SetDecoder(webrtc::VideoDecoder *decoder);
+ void SetLog(std::fstream* log);
+
+protected:
+ virtual void Setup();
+ virtual void CodecSettings(int width,
+ int height,
+ WebRtc_UWord32 frameRate=30,
+ WebRtc_UWord32 bitRate=0);
+ virtual void Teardown();
+ static int PSNRfromFiles(const char *refFileName,
+ const char *testFileName,
+ int width,
+ int height,
+ double *YPSNRptr);
+ static int SSIMfromFiles(const char *refFileName,
+ const char *testFileName,
+ int width,
+ int height,
+ double *SSIMptr,
+ int startByte = -1, int endByte = -1);
+ double SSIMfromFilesMT(int numThreads);
+ static bool SSIMthread(void *ctx);
+
+ double ActualBitRate(int nFrames);
+ static bool PacketLoss(double lossRate);
+ static double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); }
+ static void VideoBufferToRawImage(TestVideoBuffer& videoBuffer,
+ webrtc::RawImage &image);
+ static void VideoEncodedBufferToEncodedImage(TestVideoEncodedBuffer& videoBuffer,
+ webrtc::EncodedImage &image);
+
+ webrtc::VideoEncoder* _encoder;
+ webrtc::VideoDecoder* _decoder;
+ WebRtc_UWord32 _bitRate;
+ unsigned int _lengthSourceFrame;
+ unsigned char* _sourceBuffer;
+ TestVideoBuffer _inputVideoBuffer;
+ TestVideoEncodedBuffer _encodedVideoBuffer;
+ TestVideoBuffer _decodedVideoBuffer;
+ webrtc::VideoCodec _inst;
+ std::fstream* _log;
+ std::string _inname;
+ std::string _outname;
+ std::string _encodedName;
+ int _sumEncBytes;
+
+private:
+ std::string _name;
+ std::string _description;
+
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_
diff --git a/src/modules/video_coding/codecs/test_framework/test_framework.gyp b/src/modules/video_coding/codecs/test_framework/test_framework.gyp
new file mode 100644
index 0000000..861e1a4
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/test_framework.gyp
@@ -0,0 +1,62 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ '../../../../common_settings.gypi', # Common settings
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test_framework',
+ 'type': '<(library)',
+
+ 'dependencies': [
+ '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
+ '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib',
+ ],
+
+ 'include_dirs': [
+ '../interface',
+ '../../../../common_video/interface',
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../interface',
+ ],
+ },
+
+ 'sources': [
+ # header files
+ 'benchmark.h',
+ 'normal_async_test.h',
+ 'normal_test.h',
+ 'packet_loss_test.h',
+ 'performance_test.h',
+ 'test.h',
+ 'unit_test.h',
+ 'video_buffer.h',
+ 'video_source.h',
+
+ # source files
+ 'benchmark.cc',
+ 'normal_async_test.cc',
+ 'normal_test.cc',
+ 'packet_loss_test.cc',
+ 'performance_test.cc',
+ 'test.cc',
+ 'unit_test.cc',
+ 'video_buffer.cc',
+ 'video_source.cc',
+
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/modules/video_coding/codecs/test_framework/unit_test.cc b/src/modules/video_coding/codecs/test_framework/unit_test.cc
new file mode 100644
index 0000000..ba6a647
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/unit_test.cc
@@ -0,0 +1,815 @@
+/*
+ * 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 "unit_test.h"
+#include "video_source.h"
+#include "tick_util.h"
+#include <cassert>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+using namespace webrtc;
+
+UnitTest::UnitTest()
+:
+Test("UnitTest", "Unit test"),
+_tests(0),
+_errors(0),
+_source(NULL),
+_refFrame(NULL),
+_refEncFrame(NULL),
+_refDecFrame(NULL),
+_refEncFrameLength(0),
+_sourceFile(NULL),
+_encodeCompleteCallback(NULL),
+_decodeCompleteCallback(NULL)
+{
+}
+
+UnitTest::UnitTest(std::string name, std::string description)
+:
+_tests(0),
+_errors(0),
+_source(NULL),
+_refFrame(NULL),
+_refEncFrame(NULL),
+_refDecFrame(NULL),
+_refEncFrameLength(0),
+_sourceFile(NULL),
+_encodeCompleteCallback(NULL),
+_decodeCompleteCallback(NULL),
+Test(name, description)
+{
+}
+
+UnitTest::~UnitTest()
+{
+ if (_encodeCompleteCallback) {
+ delete _encodeCompleteCallback;
+ }
+
+ if (_decodeCompleteCallback) {
+ delete _decodeCompleteCallback;
+ }
+
+ if (_source) {
+ delete _source;
+ }
+
+ if (_refFrame) {
+ delete [] _refFrame;
+ }
+
+ if (_refDecFrame) {
+ delete [] _refDecFrame;
+ }
+
+ if (_sourceBuffer) {
+ delete [] _sourceBuffer;
+ }
+
+ if (_sourceFile) {
+ fclose(_sourceFile);
+ }
+
+ if (_refEncFrame) {
+ delete [] _refEncFrame;
+ }
+}
+
+WebRtc_Word32
+UnitTestEncodeCompleteCallback::Encoded(EncodedImage& encodedImage,
+ const void* codecSpecificInfo,
+ const webrtc::RTPFragmentationHeader*
+ fragmentation)
+{
+ _encodedVideoBuffer->VerifyAndAllocate(encodedImage._size);
+ _encodedVideoBuffer->CopyBuffer(encodedImage._size, encodedImage._buffer);
+ _encodedVideoBuffer->UpdateLength(encodedImage._length);
+ _encodedVideoBuffer->SetFrameType(encodedImage._frameType);
+ _encodedVideoBuffer->SetCaptureWidth(
+ (WebRtc_UWord16)encodedImage._encodedWidth);
+ _encodedVideoBuffer->SetCaptureHeight(
+ (WebRtc_UWord16)encodedImage._encodedHeight);
+ _encodedVideoBuffer->SetTimeStamp(encodedImage._timeStamp);
+ _encodeComplete = true;
+ _encodedFrameType = encodedImage._frameType;
+ return 0;
+}
+
+WebRtc_Word32 UnitTestDecodeCompleteCallback::Decoded(RawImage& image)
+{
+ _decodedVideoBuffer->VerifyAndAllocate(image._length);
+ _decodedVideoBuffer->CopyBuffer(image._length, image._buffer);
+ _decodedVideoBuffer->SetWidth(image._width);
+ _decodedVideoBuffer->SetHeight(image._height);
+ _decodedVideoBuffer->SetTimeStamp(image._timeStamp);
+ _decodeComplete = true;
+ return 0;
+}
+
+bool
+UnitTestEncodeCompleteCallback::EncodeComplete()
+{
+ if (_encodeComplete)
+ {
+ _encodeComplete = false;
+ return true;
+ }
+ return false;
+}
+
+VideoFrameType
+UnitTestEncodeCompleteCallback::EncodedFrameType() const
+{
+ return _encodedFrameType;
+}
+
+bool
+UnitTestDecodeCompleteCallback::DecodeComplete()
+{
+ if (_decodeComplete)
+ {
+ _decodeComplete = false;
+ return true;
+ }
+ return false;
+}
+
+WebRtc_UWord32
+UnitTest::WaitForEncodedFrame() const
+{
+ WebRtc_Word64 startTime = TickTime::MillisecondTimestamp();
+ while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitEncTimeMs)
+ {
+ if (_encodeCompleteCallback->EncodeComplete())
+ {
+ return _encodedVideoBuffer.GetLength();
+ }
+ }
+ return 0;
+}
+
+WebRtc_UWord32
+UnitTest::WaitForDecodedFrame() const
+{
+ WebRtc_Word64 startTime = TickTime::MillisecondTimestamp();
+ while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitDecTimeMs)
+ {
+ if (_decodeCompleteCallback->DecodeComplete())
+ {
+ return _decodedVideoBuffer.GetLength();
+ }
+ }
+ return 0;
+}
+
+WebRtc_UWord32
+UnitTest::CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate,
+ WebRtc_UWord32 /* frameRate */)
+{
+ return _encoder->SetRates(bitRate, _inst.maxFramerate);
+}
+
+void
+UnitTest::Setup()
+{
+ // Use _sourceFile as a check to prevent multiple Setup() calls.
+ if (_sourceFile != NULL)
+ {
+ return;
+ }
+
+ if (_encodeCompleteCallback == NULL)
+ {
+ _encodeCompleteCallback =
+ new UnitTestEncodeCompleteCallback(&_encodedVideoBuffer);
+ }
+ if (_decodeCompleteCallback == NULL)
+ {
+ _decodeCompleteCallback =
+ new UnitTestDecodeCompleteCallback(&_decodedVideoBuffer);
+ }
+
+ _encoder->RegisterEncodeCompleteCallback(_encodeCompleteCallback);
+ _decoder->RegisterDecodeCompleteCallback(_decodeCompleteCallback);
+
+ _source = new VideoSource("test/testFiles/foreman_cif.yuv", kCIF);
+
+ _lengthSourceFrame = _source->GetFrameLength();
+ _refFrame = new unsigned char[_lengthSourceFrame];
+ _refDecFrame = new unsigned char[_lengthSourceFrame];
+ _sourceBuffer = new unsigned char [_lengthSourceFrame];
+ _sourceFile = fopen(_source->GetFileName().c_str(), "rb");
+ VIDEO_TEST_EXIT_ON_ERR(_sourceFile != NULL);
+
+ _inst.maxFramerate = _source->GetFrameRate();
+ _bitRate = 300;
+ _inst.startBitrate = 300;
+ _inst.maxBitrate = 4000;
+ _inst.width = _source->GetWidth();
+ _inst.height = _source->GetHeight();
+
+ // Get input frame.
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ VIDEO_TEST_EXIT_ON_ERR(fread(_refFrame, 1, _lengthSourceFrame, _sourceFile)
+ == _lengthSourceFrame);
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame);
+ rewind(_sourceFile);
+
+ // Get a reference encoded frame.
+ _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+
+ RawImage image;
+ VideoBufferToRawImage(_inputVideoBuffer, image);
+
+ // Ensures our initial parameters are valid.
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ _encoder->Encode(image, NULL);
+ _refEncFrameLength = WaitForEncodedFrame();
+ VIDEO_TEST_EXIT_ON_ERR(_refEncFrameLength > 0);
+ _refEncFrame = new unsigned char[_refEncFrameLength];
+ memcpy(_refEncFrame, _encodedVideoBuffer.GetBuffer(), _refEncFrameLength);
+
+ // Get a reference decoded frame.
+ _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ int frameLength = 0;
+ int i=0;
+ while (frameLength == 0)
+ {
+ if (i > 0)
+ {
+ // Insert yet another frame
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ VIDEO_TEST_EXIT_ON_ERR(fread(_refFrame, 1, _lengthSourceFrame,
+ _sourceFile) == _lengthSourceFrame);
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame);
+ _inputVideoBuffer.SetWidth(_source->GetWidth());
+ _inputVideoBuffer.SetHeight(_source->GetHeight());
+ VideoBufferToRawImage(_inputVideoBuffer, image);
+ _encoder->Encode(image, NULL);
+ VIDEO_TEST_EXIT_ON_ERR(WaitForEncodedFrame() > 0);
+ }
+ EncodedImage encodedImage;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ VIDEO_TEST_EXIT_ON_ERR(_decoder->Decode(encodedImage, 0, NULL)
+ == WEBRTC_VIDEO_CODEC_OK);
+ frameLength = WaitForDecodedFrame();
+ _encodedVideoBuffer.Reset();
+ _encodedVideoBuffer.UpdateLength(0);
+ i++;
+ }
+ rewind(_sourceFile);
+ VIDEO_TEST(frameLength == _lengthSourceFrame);
+ memcpy(_refDecFrame, _decodedVideoBuffer.GetBuffer(), _lengthSourceFrame);
+}
+
+void
+UnitTest::Teardown()
+{
+ // Use _sourceFile as a check to prevent multiple Teardown() calls.
+ if (_sourceFile == NULL)
+ {
+ return;
+ }
+
+ _encoder->Release();
+ _decoder->Release();
+
+ fclose(_sourceFile);
+ _sourceFile = NULL;
+ delete [] _refFrame;
+ _refFrame = NULL;
+ delete [] _refEncFrame;
+ _refEncFrame = NULL;
+ delete [] _refDecFrame;
+ _refDecFrame = NULL;
+ delete [] _sourceBuffer;
+ _sourceBuffer = NULL;
+}
+
+void
+UnitTest::Print()
+{
+ printf("Unit Test\n\n%i tests completed\n", _tests);
+ if (_errors > 0)
+ {
+ printf("%i FAILED\n\n", _errors);
+ }
+ else
+ {
+ printf("ALL PASSED\n\n");
+ }
+}
+
+int
+UnitTest::DecodeWithoutAssert()
+{
+ EncodedImage encodedImage;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ int ret = _decoder->Decode(encodedImage, 0, NULL);
+ int frameLength = WaitForDecodedFrame();
+ _encodedVideoBuffer.Reset();
+ _encodedVideoBuffer.UpdateLength(0);
+ return ret == WEBRTC_VIDEO_CODEC_OK ? frameLength : ret;
+}
+
+int
+UnitTest::Decode()
+{
+ EncodedImage encodedImage;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ if (encodedImage._length == 0)
+ {
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+ int ret = _decoder->Decode(encodedImage, 0, NULL);
+ int frameLength = WaitForDecodedFrame();
+ assert(ret == WEBRTC_VIDEO_CODEC_OK && (frameLength == 0 || frameLength
+ == _lengthSourceFrame));
+ VIDEO_TEST(ret == WEBRTC_VIDEO_CODEC_OK && (frameLength == 0 || frameLength
+ == _lengthSourceFrame));
+ _encodedVideoBuffer.Reset();
+ _encodedVideoBuffer.UpdateLength(0);
+ return ret == WEBRTC_VIDEO_CODEC_OK ? frameLength : ret;
+}
+
+// Test pure virtual VideoEncoder and VideoDecoder APIs.
+void
+UnitTest::Perform()
+{
+ UnitTest::Setup();
+ int frameLength;
+ RawImage inputImage;
+ EncodedImage encodedImage;
+ EventWrapper& sleepEvent = *EventWrapper::Create();
+
+ //----- Encoder parameter tests -----
+
+ //-- Calls before InitEncode() --
+ // We want to revert the initialization done in Setup().
+ VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VideoBufferToRawImage(_inputVideoBuffer, inputImage);
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL)
+ == WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+ VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+
+ //-- InitEncode() errors --
+ // Null pointer.
+ VIDEO_TEST(_encoder->InitEncode(NULL, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ // bit rate exceeds max bit rate
+ WebRtc_Word32 tmpBitRate = _inst.startBitrate;
+ WebRtc_Word32 tmpMaxBitRate = _inst.maxBitrate;
+ _inst.startBitrate = 4000;
+ _inst.maxBitrate = 3000;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _inst.startBitrate = tmpBitRate;
+ _inst.maxBitrate = tmpMaxBitRate; //unspecified value
+
+ // Bad framerate.
+ _inst.maxFramerate = 0;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ // Seems like we should allow any framerate in range [0, 255].
+ //_inst.frameRate = 100;
+ //VIDEO_TEST(_encoder->InitEncode(&_inst, 1) == -1); // FAILS
+ _inst.maxFramerate = 30;
+
+ // Bad bitrate.
+ _inst.startBitrate = -1;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _inst.maxBitrate = _inst.startBitrate - 1;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _inst.maxBitrate = 0;
+ _inst.startBitrate = 300;
+
+ // Bad maxBitRate.
+ _inst.maxBitrate = 200;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _inst.maxBitrate = 4000;
+
+ // Bad width.
+ _inst.width = 0;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) < 0);
+ // Should there be a width and height cap?
+ //_inst.width = 10000;
+ //VIDEO_TEST(_encoder->InitEncode(&_inst, 1) == -1);
+ _inst.width = _source->GetWidth();
+
+ // Bad height.
+ _inst.height = 0;
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) < 0);
+ _inst.height = _source->GetHeight();
+
+ // Bad number of cores.
+ VIDEO_TEST(_encoder->InitEncode(&_inst, -1, 1440) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+
+ //-- Encode() errors --
+
+ // inputVideoBuffer unallocated.
+ _inputVideoBuffer.Free();
+ VideoBufferToRawImage(_inputVideoBuffer, inputImage);
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame);
+
+ //----- Encoder stress tests -----
+
+ // Vary frame rate and I-frame request.
+ VideoBufferToRawImage(_inputVideoBuffer, inputImage);
+ for (int i = 1; i <= 60; i++)
+ {
+ VideoFrameType frameType = !(i % 2) ? kKeyFrame : kDeltaFrame;
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL, frameType) ==
+ WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(WaitForEncodedFrame() > 0);
+ sleepEvent.Wait(10); // Allow the encoder's queue to realize it's empty.
+ }
+
+ // Init then encode.
+ _encodedVideoBuffer.UpdateLength(0);
+ _encodedVideoBuffer.Reset();
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(WaitForEncodedFrame() > 0);
+
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ _encoder->Encode(inputImage, NULL);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST(frameLength > 0);
+ VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength,
+ _encodedVideoBuffer.GetBuffer(), frameLength) == true);
+
+ // Reset then encode.
+ _encodedVideoBuffer.UpdateLength(0);
+ _encodedVideoBuffer.Reset();
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK);
+ WaitForEncodedFrame();
+ VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ _encoder->Encode(inputImage, NULL);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST(frameLength > 0);
+ VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength,
+ _encodedVideoBuffer.GetBuffer(), frameLength) == true);
+
+ // Release then encode.
+ _encodedVideoBuffer.UpdateLength(0);
+ _encodedVideoBuffer.Reset();
+ VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK);
+ WaitForEncodedFrame();
+ VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ _encoder->Encode(inputImage, NULL);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST(frameLength > 0);
+ VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength,
+ _encodedVideoBuffer.GetBuffer(), frameLength) == true);
+
+ //----- Decoder parameter tests -----
+
+ //-- Calls before InitDecode() --
+ // We want to revert the initialization done in Setup().
+ VIDEO_TEST(_decoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ VIDEO_TEST(_decoder->Decode(encodedImage, false, NULL) ==
+ WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+ WaitForDecodedFrame();
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ //-- Decode() errors --
+ // Unallocated encodedVideoBuffer.
+ _encodedVideoBuffer.Free();
+ //_encodedVideoBuffer.UpdateLength(10); // Buffer NULL but length > 0
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ VIDEO_TEST(_decoder->Decode(encodedImage, false, NULL) ==
+ WEBRTC_VIDEO_CODEC_ERR_PARAMETER);
+ _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+
+ //----- Decoder stress tests -----
+ unsigned char* tmpBuf = new unsigned char[_lengthSourceFrame];
+
+ // "Random" and zero data.
+ // We either expect an error, or at the least, no output.
+ // This relies on the codec's ability to detect an erroneous bitstream.
+ /*
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+ for (int i = 0; i < 100; i++)
+ {
+ VIDEO_TEST_EXIT_ON_ERR(fread(tmpBuf, 1, _refEncFrameLength, _sourceFile)
+ == _refEncFrameLength);
+ _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, tmpBuf);
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ FillDecoderSpecificInfo(encodedImage);
+ int ret = _decoder->Decode(encodedImage, false, _decoderSpecificInfo);
+ VIDEO_TEST(ret <= 0);
+ if (ret == 0)
+ {
+ VIDEO_TEST(WaitForDecodedFrame() == 0);
+ }
+
+ memset(tmpBuf, 0, _refEncFrameLength);
+ _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, tmpBuf);
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ FillDecoderSpecificInfo(encodedImage);
+ ret = _decoder->Decode(encodedImage, false, _decoderSpecificInfo);
+ VIDEO_TEST(ret <= 0);
+ if (ret == 0)
+ {
+ VIDEO_TEST(WaitForDecodedFrame() == 0);
+ }
+ }
+ */
+ rewind(_sourceFile);
+
+ _encodedVideoBuffer.UpdateLength(_refEncFrameLength);
+ _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, _refEncFrame);
+
+ // Init then decode.
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+ frameLength = 0;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ while (frameLength == 0)
+ {
+ _decoder->Decode(encodedImage, false, NULL);
+ frameLength = WaitForDecodedFrame();
+ }
+ VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength,
+ _refDecFrame, _lengthSourceFrame) == true);
+
+ // Reset then decode.
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ frameLength = 0;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ while (frameLength == 0)
+ {
+ _decoder->Decode(encodedImage, false, NULL);
+ frameLength = WaitForDecodedFrame();
+ }
+ VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength,
+ _refDecFrame, _lengthSourceFrame) == true);
+
+ // Decode with other size, reset, then decode with original size again
+ // to verify that decoder is reset to a "fresh" state upon Reset().
+ {
+ // assert that input frame size is a factor of two, so that we can use
+ // quarter size below
+ VIDEO_TEST((_inst.width % 2 == 0) && (_inst.height % 2 == 0));
+
+ VideoCodec tempInst;
+ memcpy(&tempInst, &_inst, sizeof(VideoCodec));
+ tempInst.width /= 2;
+ tempInst.height /= 2;
+
+ // Encode reduced (quarter) frame size
+ VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&tempInst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_OK);
+ RawImage tempInput(inputImage._buffer, inputImage._length/4,
+ inputImage._size/4);
+ _encoder->Encode(tempInput, NULL);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST(frameLength > 0);
+
+ // Reset then decode.
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ frameLength = 0;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ while (frameLength == 0)
+ {
+ _decoder->Decode(encodedImage, false, NULL);
+ frameLength = WaitForDecodedFrame();
+ }
+
+ // Encode original frame again
+ VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) ==
+ WEBRTC_VIDEO_CODEC_OK);
+ _encoder->Encode(inputImage, NULL);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST(frameLength > 0);
+
+ // Reset then decode original frame again.
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ frameLength = 0;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ while (frameLength == 0)
+ {
+ _decoder->Decode(encodedImage, false, NULL);
+ frameLength = WaitForDecodedFrame();
+ }
+
+ // check that decoded frame matches with reference
+ VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength,
+ _refDecFrame, _lengthSourceFrame) == true);
+
+ }
+
+ // Release then decode.
+ VIDEO_TEST(_decoder->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+ frameLength = 0;
+ VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+ while (frameLength == 0)
+ {
+ _decoder->Decode(encodedImage, false, NULL);
+ frameLength = WaitForDecodedFrame();
+ }
+ VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength,
+ _refDecFrame, _lengthSourceFrame) == true);
+ _encodedVideoBuffer.UpdateLength(0);
+ _encodedVideoBuffer.Reset();
+
+ delete [] tmpBuf;
+
+ //----- Function tests -----
+ int frames = 0;
+ // Do not specify maxBitRate (as in ViE).
+ _inst.maxBitrate = 0;
+
+ //-- Timestamp propagation --
+ VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+ if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\nTimestamp propagation test...\n");
+ frames = 0;
+ int frameDelay = 0;
+ int encTimeStamp;
+ _decodedVideoBuffer.SetTimeStamp(0);
+ while (fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile) ==
+ _lengthSourceFrame)
+ {
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
+ _inputVideoBuffer.SetTimeStamp(frames);
+ VideoBufferToRawImage(_inputVideoBuffer, inputImage);
+ VIDEO_TEST_EXIT_ON_ERR(_encoder->Encode(inputImage, NULL) ==
+ WEBRTC_VIDEO_CODEC_OK);
+ frameLength = WaitForEncodedFrame();
+ //VIDEO_TEST_EXIT_ON_ERR(frameLength);
+ VIDEO_TEST(frameLength > 0);
+ encTimeStamp = _encodedVideoBuffer.GetTimeStamp();
+ VIDEO_TEST(_inputVideoBuffer.GetTimeStamp() == encTimeStamp);
+
+ frameLength = Decode();
+ if (frameLength == 0)
+ {
+ frameDelay++;
+ }
+
+ encTimeStamp -= frameDelay;
+ if (encTimeStamp < 0)
+ {
+ encTimeStamp = 0;
+ }
+ VIDEO_TEST(_decodedVideoBuffer.GetTimeStamp() == encTimeStamp);
+ frames++;
+ sleepEvent.Wait(33);
+ }
+ delete &sleepEvent;
+ VIDEO_TEST_EXIT_ON_ERR(feof(_sourceFile) != 0);
+ rewind(_sourceFile);
+
+ RateControlTests();
+
+ Teardown();
+}
+
+void
+UnitTest::RateControlTests()
+{
+ FILE *outFile = NULL;
+ std::string outFileName;
+ int frames = 0;
+ RawImage inputImage;
+ WebRtc_UWord32 frameLength;
+ EventWrapper& sleepEvent = *EventWrapper::Create();
+
+ // Do not specify maxBitRate (as in ViE).
+ _inst.maxBitrate = 0;
+ //-- Verify rate control --
+ VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+ // add: should also be 0, and 1
+ const int bitRate[] =
+ {100, 200, 300, 400, 500, 600, 800, 1000, 2000, 3000, 4000, 10000};
+ const int nBitrates = sizeof(bitRate)/sizeof(*bitRate);
+
+ printf("\nRate control test\n");
+ for (int i = 0; i < nBitrates; i++)
+ {
+ _bitRate = bitRate[i];
+ int totalBytes = 0;
+ _encoder->Reset();
+ _inst.startBitrate = _bitRate;
+ _encoder->InitEncode(&_inst, 4, 1440);
+ _decoder->Reset();
+ _decoder->InitDecode(&_inst, 1);
+ frames = 0;
+
+ if (_bitRate > _inst.maxBitrate)
+ {
+ CodecSpecific_SetBitrate(_bitRate, _inst.maxFramerate);
+ }
+ else
+ {
+ CodecSpecific_SetBitrate(_bitRate, _inst.maxFramerate);
+ }
+
+ while (fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile) ==
+ _lengthSourceFrame)
+ {
+ _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
+ _inputVideoBuffer.SetTimeStamp(_inputVideoBuffer.GetTimeStamp() +
+ static_cast<WebRtc_UWord32>(9e4 /
+ static_cast<float>(_inst.maxFramerate)));
+ VideoBufferToRawImage(_inputVideoBuffer, inputImage);
+ VIDEO_TEST_EXIT_ON_ERR(_encoder->Encode(inputImage, NULL) ==
+ WEBRTC_VIDEO_CODEC_OK);
+ frameLength = WaitForEncodedFrame();
+ VIDEO_TEST_EXIT_ON_ERR(frameLength > 0);
+ //VIDEO_TEST(frameLength > 0);
+ totalBytes += frameLength;
+ frames++;
+
+ _encodedVideoBuffer.UpdateLength(0);
+ _encodedVideoBuffer.Reset();
+
+ sleepEvent.Wait(10);
+ }
+ WebRtc_UWord32 actualBitrate =
+ (totalBytes / frames * _inst.maxFramerate * 8)/1000;
+ printf("Target bitrate: %d kbps, actual bitrate: %d kbps\n", _bitRate,
+ actualBitrate);
+ // Test for close match over reasonable range.
+ if (_bitRate >= 100 && _bitRate <= 4000)
+ {
+ //VIDEO_TEST(fabs(actualBitrate - _bitRate) < 0.05 * _bitRate);
+ VIDEO_TEST(abs(WebRtc_Word32(actualBitrate - _bitRate)) <
+ 0.1 * _bitRate); // for VP8
+ }
+ VIDEO_TEST_EXIT_ON_ERR(feof(_sourceFile) != 0);
+ rewind(_sourceFile);
+ }
+}
+
+bool
+UnitTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes,
+ const void* ptrB, unsigned int bLengthBytes)
+{
+ if (aLengthBytes != bLengthBytes)
+ {
+ return false;
+ }
+
+ return memcmp(ptrA, ptrB, aLengthBytes) == 0;
+}
diff --git a/src/modules/video_coding/codecs/test_framework/unit_test.h b/src/modules/video_coding/codecs/test_framework/unit_test.h
new file mode 100644
index 0000000..875b584
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/unit_test.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_
+
+#include "test.h"
+#include "event_wrapper.h"
+
+// Disable "conditional expression is constant" warnings on the perfectly
+// acceptable
+// do { ... } while (0) constructions below.
+// Refer to http://stackoverflow.com/questions/1946445/
+// is-there-better-way-to-write-do-while0-construct-to-avoid-compiler-warnings
+// for some discussion of the issue.
+#pragma warning(disable : 4127)
+
+#define VIDEO_TEST(expr) \
+ do \
+ { \
+ _tests++; \
+ if (!(expr)) \
+ { \
+ fprintf(stderr, "Error at line %i of %s\nAssertion failed: %s\n\n",\
+ __LINE__, __FILE__, #expr); \
+ _errors++; \
+ } \
+ } while (0)
+
+#define VIDEO_TEST_EXIT_ON_ERR(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ fprintf(stderr, "Error at line %i of %s\nAssertion failed: %s\n", \
+ __LINE__, __FILE__, #expr); \
+ fprintf(stderr, "Exiting...\n\n"); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+class VideoSource;
+class UnitTestEncodeCompleteCallback;
+class UnitTestDecodeCompleteCallback;
+
+class UnitTest : public Test
+{
+public:
+ UnitTest();
+ virtual ~UnitTest();
+ virtual void Perform();
+ virtual void Print();
+
+protected:
+ UnitTest(std::string name, std::string description);
+ virtual WebRtc_UWord32 CodecSpecific_SetBitrate(
+ WebRtc_UWord32 bitRate,
+ WebRtc_UWord32 /* frameRate */);
+ virtual void Setup();
+ virtual void Teardown();
+ virtual void RateControlTests();
+ virtual int Decode();
+ virtual int DecodeWithoutAssert();
+ virtual int SetCodecSpecificParameters() {return 0;};
+
+ virtual bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes,
+ const void *ptrB, unsigned int bLengthBytes);
+
+ WebRtc_UWord32 WaitForEncodedFrame() const;
+ WebRtc_UWord32 WaitForDecodedFrame() const;
+
+ int _tests;
+ int _errors;
+
+ VideoSource* _source;
+ unsigned char* _refFrame;
+ unsigned char* _refEncFrame;
+ unsigned char* _refDecFrame;
+ int _refEncFrameLength;
+ FILE* _sourceFile;
+
+ UnitTestEncodeCompleteCallback* _encodeCompleteCallback;
+ UnitTestDecodeCompleteCallback* _decodeCompleteCallback;
+ enum { kMaxWaitEncTimeMs = 100 };
+ enum { kMaxWaitDecTimeMs = 25 };
+};
+
+class UnitTestEncodeCompleteCallback : public webrtc::EncodedImageCallback
+{
+public:
+ UnitTestEncodeCompleteCallback(TestVideoEncodedBuffer* buffer,
+ WebRtc_UWord32 decoderSpecificSize = 0,
+ void* decoderSpecificInfo = NULL) :
+ _encodedVideoBuffer(buffer),
+ _decoderSpecificSize(decoderSpecificSize),
+ _decoderSpecificInfo(decoderSpecificInfo),
+ _encodeComplete(false) {}
+ WebRtc_Word32 Encoded(webrtc::EncodedImage& encodedImage,
+ const void* codecSpecificInfo,
+ const webrtc::RTPFragmentationHeader*
+ fragmentation = NULL);
+ bool EncodeComplete();
+ // Note that this only makes sense if an encode has been completed
+ webrtc::VideoFrameType EncodedFrameType() const;
+private:
+ TestVideoEncodedBuffer* _encodedVideoBuffer;
+ void* _decoderSpecificInfo;
+ WebRtc_UWord32 _decoderSpecificSize;
+ bool _encodeComplete;
+ webrtc::VideoFrameType _encodedFrameType;
+};
+
+class UnitTestDecodeCompleteCallback : public webrtc::DecodedImageCallback
+{
+public:
+ UnitTestDecodeCompleteCallback(TestVideoBuffer* buffer) :
+ _decodedVideoBuffer(buffer), _decodeComplete(false) {}
+ WebRtc_Word32 Decoded(webrtc::RawImage& image);
+ bool DecodeComplete();
+private:
+ TestVideoBuffer* _decodedVideoBuffer;
+ bool _decodeComplete;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_
+
diff --git a/src/modules/video_coding/codecs/test_framework/video_buffer.cc b/src/modules/video_coding/codecs/test_framework/video_buffer.cc
new file mode 100644
index 0000000..3958e90
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/video_buffer.cc
@@ -0,0 +1,319 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include "video_buffer.h"
+
+using namespace webrtc;
+
+TestVideoBuffer::TestVideoBuffer():
+_buffer(0),
+_bufferSize(0),
+_bufferLength(0),
+_startOffset(0),
+_timeStamp(0),
+_width(0),
+_height(0)
+{
+ //
+}
+
+
+TestVideoBuffer::~TestVideoBuffer()
+{
+ _timeStamp = 0;
+ _startOffset = 0;
+ _bufferLength = 0;
+ _bufferSize = 0;
+
+ if(_buffer)
+ {
+ delete [] _buffer;
+ _buffer = 0;
+ }
+}
+
+TestVideoBuffer::TestVideoBuffer(const TestVideoBuffer& rhs)
+:
+_buffer(0),
+_bufferSize(rhs._bufferSize),
+_bufferLength(rhs._bufferLength),
+_startOffset(rhs._startOffset),
+_timeStamp(rhs._timeStamp),
+_width(rhs._width),
+_height(rhs._height)
+{
+ // make sure that our buffer is big enough
+ _buffer = new unsigned char[_bufferSize];
+
+ // only copy required length
+ memcpy(_buffer + _startOffset, rhs._buffer, _bufferLength); // GetBuffer() includes _startOffset
+}
+
+void TestVideoBuffer::SetTimeStamp(unsigned int timeStamp)
+{
+ _timeStamp = timeStamp;
+}
+
+unsigned int
+TestVideoBuffer::GetWidth() const
+{
+ return _width;
+}
+
+unsigned int
+TestVideoBuffer::GetHeight() const
+{
+ return _height;
+}
+
+void
+TestVideoBuffer::SetWidth(unsigned int width)
+{
+ _width = width;
+}
+
+void
+TestVideoBuffer::SetHeight(unsigned int height)
+{
+ _height = height;
+}
+
+
+void TestVideoBuffer::Free()
+{
+ _timeStamp = 0;
+ _startOffset = 0;
+ _bufferLength = 0;
+ _bufferSize = 0;
+ _height = 0;
+ _width = 0;
+
+ if(_buffer)
+ {
+ delete [] _buffer;
+ _buffer = 0;
+ }
+}
+
+void TestVideoBuffer::VerifyAndAllocate(unsigned int minimumSize)
+{
+ if(minimumSize > _bufferSize)
+ {
+ // make sure that our buffer is big enough
+ unsigned char * newBufferBuffer = new unsigned char[minimumSize];
+ if(_buffer)
+ {
+ // copy the old data
+ memcpy(newBufferBuffer, _buffer, _bufferSize);
+ delete [] _buffer;
+ }
+ _buffer = newBufferBuffer;
+ _bufferSize = minimumSize;
+ }
+}
+
+int TestVideoBuffer::SetOffset(unsigned int length)
+{
+ if (length > _bufferSize ||
+ length > _bufferLength)
+ {
+ return -1;
+ }
+
+ unsigned int oldOffset = _startOffset;
+
+ if(oldOffset > length)
+ {
+ unsigned int newLength = _bufferLength + (oldOffset-length);// increase by the diff
+ assert(newLength <= _bufferSize);
+ _bufferLength = newLength;
+ }
+ if(oldOffset < length)
+ {
+ if(_bufferLength > (length-oldOffset))
+ {
+ _bufferLength -= (length-oldOffset); // decrease by the diff
+ }
+ }
+ _startOffset = length; // update
+
+ return 0;
+}
+
+void TestVideoBuffer::UpdateLength(unsigned int newLength)
+{
+ assert(newLength +_startOffset <= _bufferSize);
+ _bufferLength = newLength;
+}
+
+void TestVideoBuffer::CopyBuffer(unsigned int length, const unsigned char* buffer)
+{
+ assert(length+_startOffset <= _bufferSize);
+ memcpy(_buffer+_startOffset, buffer, length);
+ _bufferLength = length;
+}
+
+void TestVideoBuffer::CopyBuffer(TestVideoBuffer& fromVideoBuffer)
+{
+ assert(fromVideoBuffer.GetLength() + fromVideoBuffer.GetStartOffset() <= _bufferSize);
+ assert(fromVideoBuffer.GetSize() <= _bufferSize);
+
+ _bufferLength = fromVideoBuffer.GetLength();
+ _startOffset = fromVideoBuffer.GetStartOffset();
+ _timeStamp = fromVideoBuffer.GetTimeStamp();
+ _height = fromVideoBuffer.GetHeight();
+ _width = fromVideoBuffer.GetWidth();
+
+ // only copy required length
+ memcpy(_buffer+_startOffset, fromVideoBuffer.GetBuffer(), fromVideoBuffer.GetLength()); // GetBuffer() includes _startOffset
+
+}
+
+void TestVideoBuffer::CopyPointer(const TestVideoBuffer& fromVideoBuffer)
+{
+ _bufferSize = fromVideoBuffer.GetSize();
+ _bufferLength = fromVideoBuffer.GetLength();
+ _startOffset = fromVideoBuffer.GetStartOffset();
+ _timeStamp = fromVideoBuffer.GetTimeStamp();
+ _height = fromVideoBuffer.GetHeight();
+ _width = fromVideoBuffer.GetWidth();
+
+ _buffer = fromVideoBuffer.GetBuffer();
+}
+
+void TestVideoBuffer::ClearPointer()
+{
+ _buffer = NULL;
+}
+
+void TestVideoBuffer::SwapBuffers(TestVideoBuffer& videoBuffer)
+{
+ unsigned char* tempBuffer = _buffer;
+ unsigned int tempSize = _bufferSize;
+ unsigned int tempLength =_bufferLength;
+ unsigned int tempOffset = _startOffset;
+ unsigned int tempTime = _timeStamp;
+ unsigned int tempHeight = _height;
+ unsigned int tempWidth = _width;
+
+ _buffer = videoBuffer.GetBuffer();
+ _bufferSize = videoBuffer.GetSize();
+ _bufferLength = videoBuffer.GetLength();
+ _startOffset = videoBuffer.GetStartOffset();
+ _timeStamp = videoBuffer.GetTimeStamp();
+ _height = videoBuffer.GetHeight();
+ _width = videoBuffer.GetWidth();
+
+
+ videoBuffer.Set(tempBuffer, tempSize, tempLength, tempOffset, tempTime);
+ videoBuffer.SetHeight(tempHeight);
+ videoBuffer.SetWidth(tempWidth);
+}
+
+void TestVideoBuffer::Set(unsigned char* tempBuffer,unsigned int tempSize,unsigned int tempLength, unsigned int tempOffset,unsigned int timeStamp)
+{
+ _buffer = tempBuffer;
+ _bufferSize = tempSize;
+ _bufferLength = tempLength;
+ _startOffset = tempOffset;
+ _timeStamp = timeStamp;
+}
+
+unsigned char* TestVideoBuffer::GetBuffer() const
+{
+ return _buffer+_startOffset;
+}
+
+unsigned int TestVideoBuffer::GetStartOffset() const
+{
+ return _startOffset;
+}
+
+unsigned int TestVideoBuffer::GetSize() const
+{
+ return _bufferSize;
+}
+
+unsigned int TestVideoBuffer::GetLength() const
+{
+ return _bufferLength;
+}
+
+unsigned int TestVideoBuffer::GetTimeStamp() const
+{
+ return _timeStamp;
+}
+
+/**
+* TestVideoEncodedBuffer
+*
+*/
+
+TestVideoEncodedBuffer::TestVideoEncodedBuffer() :
+ _captureWidth(0),
+ _captureHeight(0),
+ _frameRate(-1)
+{
+ _frameType = kDeltaFrame;
+}
+
+TestVideoEncodedBuffer::~TestVideoEncodedBuffer()
+{
+}
+
+void TestVideoEncodedBuffer::SetCaptureWidth(unsigned short width)
+{
+ _captureWidth = width;
+}
+
+void TestVideoEncodedBuffer::SetCaptureHeight(unsigned short height)
+{
+ _captureHeight = height;
+}
+
+unsigned short TestVideoEncodedBuffer::GetCaptureWidth()
+{
+ return _captureWidth;
+}
+
+unsigned short TestVideoEncodedBuffer::GetCaptureHeight()
+{
+ return _captureHeight;
+}
+
+VideoFrameType TestVideoEncodedBuffer::GetFrameType()
+{
+ return _frameType;
+}
+
+void TestVideoEncodedBuffer::SetFrameType(VideoFrameType frametype)
+{
+ _frameType = frametype;
+}
+
+void TestVideoEncodedBuffer::Reset()
+{
+ _captureWidth = 0;
+ _captureHeight = 0;
+ _frameRate = -1;
+ _frameType = kDeltaFrame;
+}
+
+void TestVideoEncodedBuffer::SetFrameRate(float frameRate)
+{
+ _frameRate = frameRate;
+}
+
+float TestVideoEncodedBuffer::GetFrameRate()
+{
+ return _frameRate;
+}
diff --git a/src/modules/video_coding/codecs/test_framework/video_buffer.h b/src/modules/video_coding/codecs/test_framework/video_buffer.h
new file mode 100644
index 0000000..824440e
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/video_buffer.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_
+
+#include "typedefs.h"
+#include "video_image.h"
+
+class TestVideoBuffer
+{
+public:
+ TestVideoBuffer();
+
+ virtual ~TestVideoBuffer();
+
+ TestVideoBuffer(const TestVideoBuffer& rhs);
+
+ /**
+ * Verifies that current allocated buffer size is larger than or equal to the input size.
+ * If the current buffer size is smaller, a new allocation is made and the old buffer data is copied to the new buffer.
+ */
+ void VerifyAndAllocate(unsigned int minimumSize);
+
+ void UpdateLength(unsigned int newLength);
+
+ void SwapBuffers(TestVideoBuffer& videoBuffer);
+
+ void CopyBuffer(unsigned int length, const unsigned char* fromBuffer);
+
+ void CopyBuffer(TestVideoBuffer& fromVideoBuffer);
+
+ // Use with care, and remember to call ClearPointer() when done.
+ void CopyPointer(const TestVideoBuffer& fromVideoBuffer);
+
+ void ClearPointer();
+
+ int SetOffset(unsigned int length); // Sets offset to beginning of frame in buffer
+
+ void Free(); // Deletes frame buffer and resets members to zero
+
+ void SetTimeStamp(unsigned int timeStamp); // Sets timestamp of frame (90kHz)
+
+ /**
+ * Gets pointer to frame buffer
+ */
+ unsigned char* GetBuffer() const;
+
+ /**
+ * Gets allocated buffer size
+ */
+ unsigned int GetSize() const;
+
+ /**
+ * Gets length of frame
+ */
+ unsigned int GetLength() const;
+
+ /**
+ * Gets timestamp of frame (90kHz)
+ */
+ unsigned int GetTimeStamp() const;
+
+ unsigned int GetWidth() const;
+ unsigned int GetHeight() const;
+
+ void SetWidth(unsigned int width);
+ void SetHeight(unsigned int height);
+
+private:
+ TestVideoBuffer& operator=(const TestVideoBuffer& inBuffer);
+
+private:
+ void Set(unsigned char* buffer,unsigned int size,unsigned int length,unsigned int offset, unsigned int timeStamp);
+ unsigned int GetStartOffset() const;
+
+ unsigned char* _buffer; // Pointer to frame buffer
+ unsigned int _bufferSize; // Allocated buffer size
+ unsigned int _bufferLength; // Length (in bytes) of frame
+ unsigned int _startOffset; // Offset (in bytes) to beginning of frame in buffer
+ unsigned int _timeStamp; // Timestamp of frame (90kHz)
+ unsigned int _width;
+ unsigned int _height;
+};
+
+class TestVideoEncodedBuffer: public TestVideoBuffer
+{
+public:
+ TestVideoEncodedBuffer();
+ ~TestVideoEncodedBuffer();
+
+ void SetCaptureWidth(unsigned short width);
+ void SetCaptureHeight(unsigned short height);
+ unsigned short GetCaptureWidth();
+ unsigned short GetCaptureHeight();
+
+ webrtc::VideoFrameType GetFrameType();
+ void SetFrameType(webrtc::VideoFrameType frametype);
+
+ void Reset();
+
+ void SetFrameRate(float frameRate);
+ float GetFrameRate();
+
+private:
+ TestVideoEncodedBuffer& operator=(const TestVideoEncodedBuffer& inBuffer);
+
+private:
+ unsigned short _captureWidth;
+ unsigned short _captureHeight;
+ webrtc::VideoFrameType _frameType;
+ float _frameRate;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_
diff --git a/src/modules/video_coding/codecs/test_framework/video_source.cc b/src/modules/video_coding/codecs/test_framework/video_source.cc
new file mode 100644
index 0000000..2045bd9
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/video_source.cc
@@ -0,0 +1,417 @@
+/*
+ * 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 "video_source.h"
+#include "vplib.h"
+#include <cassert>
+#include <stdio.h>
+
+VideoSource::VideoSource()
+:
+_fileName("../../../../testFiles/foreman.yuv"),
+_width(352),
+_height(288),
+_type(webrtc::kI420),
+_frameRate(30)
+{
+}
+
+VideoSource::VideoSource(std::string fileName, VideoSize size,
+ int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/)
+:
+_fileName(fileName),
+_type(type),
+_frameRate(frameRate)
+{
+ assert(size != kUndefined && size != kNumberOfVideoSizes);
+ assert(type != webrtc::kUnknown);
+ assert(frameRate > 0);
+ assert(GetWidthHeight(size, _width, _height) == 0);
+}
+
+VideoSource::VideoSource(std::string fileName, int width, int height,
+ int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/)
+:
+_fileName(fileName),
+_width(width),
+_height(height),
+_type(type),
+_frameRate(frameRate)
+{
+ assert(width > 0);
+ assert(height > 0);
+ assert(type != webrtc::kUnknown);
+ assert(frameRate > 0);
+}
+
+VideoSize
+VideoSource::GetSize() const
+{
+ return GetSize(_width, _height);
+}
+
+VideoSize
+VideoSource::GetSize(WebRtc_UWord16 width, WebRtc_UWord16 height)
+{
+ if(width == 128 && height == 96)
+ {
+ return kSQCIF;
+ }else if(width == 160 && height == 120)
+ {
+ return kQQVGA;
+ }else if(width == 176 && height == 144)
+ {
+ return kQCIF;
+ }else if(width == 320 && height == 240)
+ {
+ return kQVGA;
+ }else if(width == 352 && height == 288)
+ {
+ return kCIF;
+ }else if(width == 640 && height == 480)
+ {
+ return kVGA;
+ }else if(width == 720 && height == 480)
+ {
+ return kNTSC;
+ }else if(width == 704 && height == 576)
+ {
+ return k4CIF;
+ }else if(width == 800 && height == 600)
+ {
+ return kSVGA;
+ }else if(width == 960 && height == 720)
+ {
+ return kHD;
+ }else if(width == 1024 && height == 768)
+ {
+ return kXGA;
+ }else if(width == 1440 && height == 1080)
+ {
+ return kFullHD;
+ }else if(width == 400 && height == 240)
+ {
+ return kWQVGA;
+ }else if(width == 800 && height == 480)
+ {
+ return kWVGA;
+ }else if(width == 1280 && height == 720)
+ {
+ return kWHD;
+ }else if(width == 1920 && height == 1080)
+ {
+ return kWFullHD;
+ }
+ return kUndefined;
+}
+
+unsigned int
+VideoSource::GetFrameLength() const
+{
+ return webrtc::CalcBufferSize(_type, _width, _height);
+}
+
+const char*
+VideoSource::GetMySizeString() const
+{
+ return VideoSource::GetSizeString(GetSize());
+}
+
+const char*
+VideoSource::GetSizeString(VideoSize size)
+{
+ switch (size)
+ {
+ case kSQCIF:
+ return "SQCIF";
+ case kQQVGA:
+ return "QQVGA";
+ case kQCIF:
+ return "QCIF";
+ case kQVGA:
+ return "QVGA";
+ case kCIF:
+ return "CIF";
+ case kVGA:
+ return "VGA";
+ case kNTSC:
+ return "NTSC";
+ case k4CIF:
+ return "4CIF";
+ case kSVGA:
+ return "SVGA";
+ case kHD:
+ return "HD";
+ case kXGA:
+ return "XGA";
+ case kFullHD:
+ return "Full_HD";
+ case kWQVGA:
+ return "WQVGA";
+ case kWHD:
+ return "WHD";
+ case kWFullHD:
+ return "WFull_HD";
+ default:
+ return "Undefined";
+ }
+}
+
+std::string
+VideoSource::GetFilePath() const
+{
+ size_t slashPos = _fileName.find_last_of("/\\");
+ if (slashPos == std::string::npos)
+ {
+ return ".";
+ }
+
+ return _fileName.substr(0, slashPos);
+}
+
+std::string
+VideoSource::GetName() const
+{
+ // Remove path.
+ size_t slashPos = _fileName.find_last_of("/\\");
+ if (slashPos == std::string::npos)
+ {
+ slashPos = 0;
+ }
+ else
+ {
+ slashPos++;
+ }
+
+ // Remove extension and underscored suffix if it exists.
+ return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"),
+ _fileName.find_last_of(".")) - slashPos);
+}
+
+void
+VideoSource::Convert(const VideoSource &target, bool force /* = false */) const
+{
+ // Ensure target rate is less than or equal to source
+ // (i.e. we are only temporally downsampling).
+ assert(target.GetFrameRate() <= _frameRate);
+ // Only supports YUV420 currently.
+ assert(_type == webrtc::kI420 && target.GetType() == webrtc::kI420);
+ if (!force && (FileExists(target.GetFileName().c_str()) ||
+ (target.GetWidth() == _width && target.GetHeight() == _height && target.GetFrameRate() == _frameRate)))
+ {
+ // Assume that the filename uniquely defines the content.
+ // If the file already exists, it is the correct file.
+ return;
+ }
+ FILE *inFile = NULL;
+ FILE *outFile = NULL;
+
+ inFile = fopen(_fileName.c_str(), "rb");
+ assert(inFile != NULL);
+
+ outFile = fopen(target.GetFileName().c_str(), "wb");
+ assert(outFile != NULL);
+
+ FrameDropper fd;
+ fd.SetFrameRate(target.GetFrameRate(), _frameRate);
+
+ const size_t lengthOutFrame = webrtc::CalcBufferSize(target.GetType(),
+ target.GetWidth(), target.GetHeight());
+ assert(lengthOutFrame > 0);
+ unsigned char *outFrame = new unsigned char[lengthOutFrame];
+
+ const size_t lengthInFrame = webrtc::CalcBufferSize(_type, _width, _height);
+ assert(lengthInFrame > 0);
+ unsigned char *inFrame = new unsigned char[lengthInFrame];
+
+ while (fread(inFrame, 1, lengthInFrame, inFile) == lengthInFrame)
+ {
+ if (!fd.DropFrame())
+ {
+ assert(target.GetWidth() == _width &&
+ target.GetHeight() == _height); // Add video interpolator here!
+ fwrite(outFrame, 1, lengthOutFrame, outFile);
+ }
+ }
+
+ delete inFrame;
+ delete outFrame;
+ fclose(inFile);
+ fclose(outFile);
+}
+
+bool VideoSource::FileExists(const char* fileName)
+{
+ FILE* fp = NULL;
+ fp = fopen(fileName, "rb");
+ if(fp != NULL)
+ {
+ fclose(fp);
+ return true;
+ }
+ return false;
+}
+
+
+int
+VideoSource::GetWidthHeight( VideoSize size, int & width, int& height)
+{
+ switch(size)
+ {
+ case kSQCIF:
+ width = 128;
+ height = 96;
+ return 0;
+ case kQQVGA:
+ width = 160;
+ height = 120;
+ return 0;
+ case kQCIF:
+ width = 176;
+ height = 144;
+ return 0;
+ case kCGA:
+ width = 320;
+ height = 200;
+ return 0;
+ case kQVGA:
+ width = 320;
+ height = 240;
+ return 0;
+ case kSIF:
+ width = 352;
+ height = 240;
+ return 0;
+ case kWQVGA:
+ width = 400;
+ height = 240;
+ return 0;
+ case kCIF:
+ width = 352;
+ height = 288;
+ return 0;
+ case kW288p:
+ width = 512;
+ height = 288;
+ return 0;
+ case k448p:
+ width = 576;
+ height = 448;
+ return 0;
+ case kVGA:
+ width = 640;
+ height = 480;
+ return 0;
+ case k432p:
+ width = 720;
+ height = 432;
+ return 0;
+ case kW432p:
+ width = 768;
+ height = 432;
+ return 0;
+ case k4SIF:
+ width = 704;
+ height = 480;
+ return 0;
+ case kW448p:
+ width = 768;
+ height = 448;
+ return 0;
+ case kNTSC:
+ width = 720;
+ height = 480;
+ return 0;
+ case kFW448p:
+ width = 800;
+ height = 448;
+ return 0;
+ case kWVGA:
+ width = 800;
+ height = 480;
+ return 0;
+ case k4CIF:
+ width = 704;
+ height = 576;
+ return 0;
+ case kSVGA:
+ width = 800;
+ height = 600;
+ return 0;
+ case kW544p:
+ width = 960;
+ height = 544;
+ return 0;
+ case kW576p:
+ width = 1024;
+ height = 576;
+ return 0;
+ case kHD:
+ width = 960;
+ height = 720;
+ return 0;
+ case kXGA:
+ width = 1024;
+ height = 768;
+ return 0;
+ case kFullHD:
+ width = 1440;
+ height = 1080;
+ return 0;
+ case kWHD:
+ width = 1280;
+ height = 720;
+ return 0;
+ case kWFullHD:
+ width = 1920;
+ height = 1080;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+FrameDropper::FrameDropper()
+:
+_dropsBetweenRenders(0),
+_frameCounter(0)
+{
+}
+
+bool
+FrameDropper::DropFrame()
+{
+ _frameCounter++;
+ if (_frameCounter > _dropsBetweenRenders)
+ {
+ _frameCounter = 0;
+ return false;
+ }
+ return true;
+}
+
+unsigned int
+FrameDropper::DropsBetweenRenders()
+{
+ return _dropsBetweenRenders;
+}
+
+void
+FrameDropper::SetFrameRate(double frameRate, double maxFrameRate)
+{
+ if (frameRate >= 1.0)
+ {
+ _dropsBetweenRenders = static_cast<unsigned int>(maxFrameRate / frameRate + 0.5) - 1;
+ }
+ else
+ {
+ _dropsBetweenRenders = 0;
+ }
+}
diff --git a/src/modules/video_coding/codecs/test_framework/video_source.h b/src/modules/video_coding/codecs/test_framework/video_source.h
new file mode 100644
index 0000000..18b51c2
--- /dev/null
+++ b/src/modules/video_coding/codecs/test_framework/video_source.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_
+
+#include <string>
+
+#include "vplib.h"
+
+enum VideoSize
+ {
+ kUndefined,
+ kSQCIF, // 128*96 = 12 288
+ kQQVGA, // 160*120 = 19 200
+ kQCIF, // 176*144 = 25 344
+ kCGA, // 320*200 = 64 000
+ kQVGA, // 320*240 = 76 800
+ kSIF, // 352*240 = 84 480
+ kWQVGA, // 400*240 = 96 000
+ kCIF, // 352*288 = 101 376
+ kW288p, // 512*288 = 147 456 (WCIF)
+ k448p, // 576*448 = 281 088
+ kVGA, // 640*480 = 307 200
+ k432p, // 720*432 = 311 040
+ kW432p, // 768*432 = 331 776
+ k4SIF, // 704*480 = 337 920
+ kW448p, // 768*448 = 344 064
+ kNTSC, // 720*480 = 345 600
+ kFW448p, // 800*448 = 358 400
+ kWVGA, // 800*480 = 384 000
+ k4CIF, // 704�576 = 405 504
+ kSVGA, // 800*600 = 480 000
+ kW544p, // 960*544 = 522 240
+ kW576p, // 1024*576 = 589 824 (W4CIF)
+ kHD, // 960*720 = 691 200
+ kXGA, // 1024*768 = 786 432
+ kWHD, // 1280*720 = 921 600
+ kFullHD, // 1440*1080 = 1 555 200
+ kWFullHD, // 1920*1080 = 2 073 600
+
+ kNumberOfVideoSizes
+ };
+
+class VideoSource
+{
+public:
+ VideoSource();
+ VideoSource(std::string fileName, VideoSize size, int frameRate = 30,
+ webrtc::VideoType type = webrtc::kI420);
+ VideoSource(std::string fileName, int width, int height, int frameRate = 30,
+ webrtc::VideoType type = webrtc::kI420);
+
+ std::string GetFileName() const { return _fileName; }
+ int GetWidth() const { return _width; }
+ int GetHeight() const { return _height; }
+ webrtc::VideoType GetType() const { return _type; }
+ int GetFrameRate() const { return _frameRate; }
+
+ // Returns the file path without a trailing slash.
+ std::string GetFilePath() const;
+
+ // Returns the filename with the path (including the leading slash) removed.
+ std::string GetName() const;
+
+ VideoSize GetSize() const;
+ static VideoSize GetSize(WebRtc_UWord16 width, WebRtc_UWord16 height);
+ unsigned int GetFrameLength() const;
+
+ // Returns a human-readable size string.
+ static const char* GetSizeString(VideoSize size);
+ const char* GetMySizeString() const;
+
+ // Opens the video source, converting and writing to the specified target.
+ // If force is true, the conversion will be done even if the target file
+ // already exists.
+ void Convert(const VideoSource& target, bool force = false) const;
+ static bool FileExists(const char* fileName);
+private:
+ static int GetWidthHeight( VideoSize size, int& width, int& height);
+ std::string _fileName;
+ int _width;
+ int _height;
+ webrtc::VideoType _type;
+ int _frameRate;
+};
+
+class FrameDropper
+{
+public:
+ FrameDropper();
+ bool DropFrame();
+ unsigned int DropsBetweenRenders();
+ void SetFrameRate(double frameRate, double maxFrameRate);
+
+private:
+ unsigned int _dropsBetweenRenders;
+ unsigned int _frameCounter;
+};
+
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_
+
diff --git a/src/modules/video_coding/codecs/vp8/main/interface/vp8.h b/src/modules/video_coding/codecs/vp8/main/interface/vp8.h
new file mode 100644
index 0000000..ab82f34
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/interface/vp8.h
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+/*
+ * vp8.h
+ * WEBRTC VP8 wrapper interface
+ */
+
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_
+
+#include "video_codec_interface.h"
+
+// VPX forward declaration
+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;
+
+//#define VP8_LATEST
+
+namespace webrtc
+{
+
+/******************************/
+/* VP8Encoder class */
+/******************************/
+class VP8Encoder : public VideoEncoder
+{
+public:
+ VP8Encoder();
+ virtual ~VP8Encoder();
+
+// Free encoder memory.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 Release();
+
+// Reset encoder state and prepare for a new call.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
+// WEBRTC_VIDEO_CODEC_ERROR
+ virtual WebRtc_Word32 Reset();
+
+// Initialize the encoder with the information from the codecSettings
+//
+// Input:
+// - codecSettings : Codec settings
+// - numberOfCores : Number of cores available for the encoder
+// - maxPayloadSize : The maximum size each payload is allowed
+// to have. Usually MTU - overhead.
+//
+// Return value : Set bit rate if OK
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
+// WEBRTC_VIDEO_CODEC_ERR_SIZE
+// WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED
+// WEBRTC_VIDEO_CODEC_MEMORY
+// WEBRTC_VIDEO_CODEC_ERROR
+ virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings,
+ WebRtc_Word32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize);
+
+// Encode an I420 image (as a part of a video stream). The encoded image
+// will be returned to the user through the encode complete callback.
+//
+// Input:
+// - inputImage : Image to be encoded
+// - frameTypes : Frame type to be generated by the encoder.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
+// WEBRTC_VIDEO_CODEC_MEMORY
+// WEBRTC_VIDEO_CODEC_ERROR
+// WEBRTC_VIDEO_CODEC_TIMEOUT
+
+ virtual WebRtc_Word32 Encode(const RawImage& inputImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ VideoFrameType frameType);
+
+// Register an encode complete callback object.
+//
+// Input:
+// - callback : Callback object which handles encoded images.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback*
+ callback);
+
+// Inform the encoder of the new packet loss rate in the network
+//
+// - packetLoss : Fraction lost
+// (loss rate in percent = 100 * packetLoss / 255)
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERROR
+//
+ virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 packetLoss);
+
+// Inform the encoder about the new target bit rate.
+//
+// - newBitRate : New target bit rate
+// - frameRate : The target frame rate
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRateKbit,
+ WebRtc_UWord32 frameRate);
+
+// Get version number for the codec.
+//
+// Input:
+// - version : Pointer to allocated char buffer.
+// - buflen : Length of provided char buffer.
+//
+// Output:
+// - version : Version number string written to char buffer.
+//
+// Return value : >0 - Length of written string.
+// <0 - WEBRTC_VIDEO_CODEC_ERR_SIZE
+ virtual WebRtc_Word32 Version(WebRtc_Word8 *version,
+ WebRtc_Word32 length) const;
+ static WebRtc_Word32 VersionStatic(WebRtc_Word8 *version,
+ WebRtc_Word32 length);
+
+private:
+// Call encoder initialize function and set speed.
+ WebRtc_Word32 InitAndSetSpeed();
+
+// Determine maximum target for Intra frames
+//
+// Input:
+// - optimalBuffersize : Optimal buffer size
+// Return Value : Max target size for Intra frames represented as
+// percentage of the per frame bandwidth
+#ifdef VP8_LATEST
+ WebRtc_Word32 MaxIntraTarget(WebRtc_Word32 optimalBuffersize);
+#endif
+ EncodedImage _encodedImage;
+ EncodedImageCallback* _encodedCompleteCallback;
+ WebRtc_Word32 _width;
+ WebRtc_Word32 _height;
+ WebRtc_Word32 _maxBitRateKbit;
+ int _maxFrameRate;
+ bool _inited;
+ WebRtc_UWord32 _timeStamp;
+ WebRtc_UWord16 _pictureID;
+ bool _pictureLossIndicationOn;
+ bool _feedbackModeOn;
+ bool _nextRefIsGolden;
+ bool _lastAcknowledgedIsGolden;
+ bool _haveReceivedAcknowledgement;
+ WebRtc_UWord16 _pictureIDLastSentRef;
+ WebRtc_UWord16 _pictureIDLastAcknowledgedRef;
+ int _cpuSpeed;
+
+ vpx_codec_ctx_t* _encoder;
+ vpx_codec_enc_cfg_t* _cfg;
+ vpx_image_t* _raw;
+};// end of VP8Encoder class
+
+/******************************/
+/* VP8Decoder class */
+/******************************/
+class VP8Decoder : public VideoDecoder
+{
+public:
+ VP8Decoder();
+ virtual ~VP8Decoder();
+
+// Initialize the decoder.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK.
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERROR
+ virtual WebRtc_Word32 InitDecode(const VideoCodec* inst,
+ WebRtc_Word32 numberOfCores);
+
+// Decode encoded image (as a part of a video stream). The decoded image
+// will be returned to the user through the decode complete callback.
+//
+// Input:
+// - inputImage : Encoded image to be decoded
+// - missingFrames : True if one or more frames have been lost
+// since the previous decode call.
+// - codecSpecificInfo : pointer to specific codec data
+// - renderTimeMs : Render time in Ms
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERROR
+// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
+ virtual WebRtc_Word32 Decode(const EncodedImage& inputImage,
+ bool missingFrames,
+ const void* /*codecSpecificInfo*/,
+ WebRtc_Word64 /*renderTimeMs*/);
+
+// Register a decode complete callback object.
+//
+// Input:
+// - callback : Callback object which handles decoded images.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
+ virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback*
+ callback);
+
+// Free decoder memory.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK if OK
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_ERROR
+ virtual WebRtc_Word32 Release();
+
+// Reset decoder state and prepare for a new call.
+//
+// Return value : WEBRTC_VIDEO_CODEC_OK.
+// <0 - Errors:
+// WEBRTC_VIDEO_CODEC_UNINITIALIZED
+// WEBRTC_VIDEO_CODEC_ERROR
+ 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
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_
diff --git a/src/modules/video_coding/codecs/vp8/main/source/Android.mk b/src/modules/video_coding/codecs/vp8/main/source/Android.mk
new file mode 100644
index 0000000..6559915
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/Android.mk
@@ -0,0 +1,53 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_vp8
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+LOCAL_SRC_FILES := vp8.cc
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS :=
+MY_CFLAGS_C :=
+MY_DEFS := '-DNO_TCMALLOC' \
+ '-DNO_HEAPCHECKER' \
+ '-DWEBRTC_TARGET_PC' \
+ '-DWEBRTC_LINUX' \
+ '-DWEBRTC_THREAD_RR' \
+ '-DWEBRTC_ANDROID' \
+ '-DANDROID'
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \
+ $(LOCAL_PATH)/../interface \
+ $(LOCAL_PATH)/../../../interface \
+ $(LOCAL_PATH)/../../../../../../modules/interface \
+ $(LOCAL_PATH)/../../../../../../system_wrappers/interface \
+ external/libvpx
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS :=
+
+LOCAL_LDFLAGS :=
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_SHARED_LIBRARIES := libcutils \
+ libdl \
+ libstlport
+LOCAL_ADDITIONAL_DEPENDENCIES :=
+
+include external/stlport/libstlport.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/modules/video_coding/codecs/vp8/main/source/vp8.cc b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc
new file mode 100644
index 0000000..26b47f6
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc
@@ -0,0 +1,1037 @@
+/*
+ * 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.
+ */
+
+/*
+ * vp8.cc
+ *
+ * This file contains the WEBRTC VP8 wrapper implementation
+ *
+ */
+#include "vp8.h"
+#include "tick_util.h"
+
+#include "vpx/vpx_encoder.h"
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8cx.h"
+#include "vpx/vp8dx.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "module_common_types.h"
+
+#define VP8_FREQ_HZ 90000
+//#define DEV_PIC_LOSS
+
+namespace webrtc
+{
+
+VP8Encoder::VP8Encoder():
+ _encodedImage(),
+ _encodedCompleteCallback(NULL),
+ _width(0),
+ _height(0),
+ _maxBitRateKbit(0),
+ _inited(false),
+ _timeStamp(0),
+ _pictureID(0),
+ _pictureLossIndicationOn(false),
+ _feedbackModeOn(false),
+ _nextRefIsGolden(true),
+ _lastAcknowledgedIsGolden(true),
+ _haveReceivedAcknowledgement(false),
+ _pictureIDLastSentRef(0),
+ _pictureIDLastAcknowledgedRef(0),
+ _cpuSpeed(-6), // default value
+ _encoder(NULL),
+ _cfg(NULL),
+ _raw(NULL)
+{
+ srand((WebRtc_UWord32)TickTime::MillisecondTimestamp());
+}
+
+VP8Encoder::~VP8Encoder()
+{
+ Release();
+}
+
+WebRtc_Word32
+VP8Encoder::VersionStatic(WebRtc_Word8* version, WebRtc_Word32 length)
+{
+ const WebRtc_Word8* str = "WebM/VP8 version 1.0.0\n"; // Bali
+ WebRtc_Word32 verLen = (WebRtc_Word32)strlen(str);
+ if (verLen > length)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ strncpy(version, str, length);
+ return verLen;
+}
+
+WebRtc_Word32
+VP8Encoder::Version(WebRtc_Word8 *version, WebRtc_Word32 length) const
+{
+ return VersionStatic(version, length);
+}
+
+WebRtc_Word32
+VP8Encoder::Release()
+{
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ _encodedImage._buffer = NULL;
+ }
+ if (_encoder != NULL)
+ {
+ if (vpx_codec_destroy(_encoder))
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ delete _encoder;
+ _encoder = NULL;
+ }
+ if (_cfg != NULL)
+ {
+ delete _cfg;
+ _cfg = NULL;
+ }
+ if (_raw != NULL)
+ {
+ vpx_img_free(_raw);
+ delete _raw;
+ _raw = NULL;
+ }
+ _inited = false;
+
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Encoder::Reset()
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ if (_encoder != NULL)
+ {
+ if (vpx_codec_destroy(_encoder))
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ delete _encoder;
+ _encoder = NULL;
+ }
+
+ _timeStamp = 0;
+
+ _encoder = new vpx_codec_ctx_t;
+
+ return InitAndSetSpeed();
+}
+
+WebRtc_Word32
+VP8Encoder::SetRates(WebRtc_UWord32 newBitRateKbit, WebRtc_UWord32 newFrameRate)
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (_encoder->err)
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ if (newFrameRate < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+
+ // update bit rate
+ if (_maxBitRateKbit > 0 &&
+ newBitRateKbit > static_cast<WebRtc_UWord32>(_maxBitRateKbit))
+ {
+ newBitRateKbit = _maxBitRateKbit;
+ }
+ _cfg->rc_target_bitrate = newBitRateKbit; // in kbit/s
+
+ // update frame rate
+ if (newFrameRate != _maxFrameRate)
+ {
+ _maxFrameRate = static_cast<int>(newFrameRate);
+ _cfg->g_timebase.num = 1;
+ _cfg->g_timebase.den = _maxFrameRate;
+ }
+
+ // update encoder context
+ if (vpx_codec_enc_config_set(_encoder, _cfg))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Encoder::InitEncode(const VideoCodec* inst,
+ WebRtc_Word32 numberOfCores,
+ WebRtc_UWord32 /*maxPayloadSize */)
+{
+ if (inst == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (inst->maxFramerate < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (inst->startBitrate < 0 || inst->maxBitrate < 0)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ // allow zero to represent an unspecified maxBitRate
+ if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (inst->width < 1 || inst->height < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (numberOfCores < 1)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+#ifdef DEV_PIC_LOSS
+ // we need to know if we use feedback
+ _feedbackModeOn = inst->codecSpecific.VP8.feedbackModeOn;
+ _pictureLossIndicationOn = inst->codecSpecific.VP8.pictureLossIndicationOn;
+#endif
+
+ WebRtc_Word32 retVal = Release();
+ if (retVal < 0)
+ {
+ return retVal;
+ }
+ if (_encoder == NULL)
+ {
+ _encoder = new vpx_codec_ctx_t;
+ }
+ if (_cfg == NULL)
+ {
+ _cfg = new vpx_codec_enc_cfg_t;
+ }
+ if (_raw == NULL)
+ {
+ _raw = new vpx_image_t;
+ }
+
+ _timeStamp = 0;
+ _maxBitRateKbit = inst->maxBitrate;
+ _maxFrameRate = inst->maxFramerate;
+ _width = inst->width;
+ _height = inst->height;
+
+ // random start 16 bits is enough
+ _pictureID = (WebRtc_UWord16)rand();
+
+ // allocate memory for encoded image
+ if (_encodedImage._buffer != NULL)
+ {
+ delete [] _encodedImage._buffer;
+ }
+ _encodedImage._size = (3 * inst->width * inst->height) >> 1;
+ _encodedImage._buffer = new WebRtc_UWord8[_encodedImage._size];
+
+ vpx_img_alloc(_raw, IMG_FMT_I420, inst->width, inst->height, 1);
+ // populate encoder configuration with default values
+ if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), _cfg, 0))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ _cfg->g_w = inst->width;
+ _cfg->g_h = inst->height;
+ if (_maxBitRateKbit > 0 &&
+ inst->startBitrate > static_cast<unsigned int>(_maxBitRateKbit))
+ {
+ _cfg->rc_target_bitrate = _maxBitRateKbit;
+ }
+ else
+ {
+ _cfg->rc_target_bitrate = inst->startBitrate; // in kbit/s
+ }
+
+ // setting the time base of the codec
+ _cfg->g_timebase.num = 1;
+ _cfg->g_timebase.den = _maxFrameRate;
+
+ _cfg->g_error_resilient = 1; //enabled
+ _cfg->g_lag_in_frames = 0; // 0- no frame lagging
+
+ _cfg->g_threads = numberOfCores;
+
+ // rate control settings
+ _cfg->rc_dropframe_thresh = 0;
+ _cfg->rc_end_usage = VPX_CBR;
+ _cfg->g_pass = VPX_RC_ONE_PASS;
+ _cfg->rc_resize_allowed = 0;
+ _cfg->rc_min_quantizer = 4;
+ _cfg->rc_max_quantizer = 56;
+ _cfg->rc_undershoot_pct = 100;
+ _cfg->rc_overshoot_pct = 15;
+ _cfg->rc_buf_initial_sz = 500;
+ _cfg->rc_buf_optimal_sz = 600;
+ _cfg->rc_buf_sz = 1000;
+#ifdef VP8_LATEST
+ _cfg->rc_max_intra_bitrate_pct = MaxIntraTarget(_cfg->rc_buf_optimal_sz);
+#endif
+
+
+#ifdef DEV_PIC_LOSS
+ // this can only be off if we know we use feedback
+ if (_pictureLossIndicationOn)
+ {
+ // don't generate key frame unless we tell you
+ _cfg->kf_mode = VPX_KF_DISABLED;
+ }
+ else
+#endif
+ {
+ _cfg->kf_mode = VPX_KF_AUTO;
+ _cfg->kf_max_dist = 300;
+ }
+
+ switch (inst->codecSpecific.VP8.complexity)
+ {
+ case kComplexityHigh:
+ {
+ _cpuSpeed = -5;
+ break;
+ }
+ case kComplexityHigher:
+ {
+ _cpuSpeed = -4;
+ break;
+ }
+ case kComplexityMax:
+ {
+ _cpuSpeed = -3;
+ break;
+ }
+ default:
+ {
+ _cpuSpeed = -6;
+ break;
+ }
+ }
+
+ return InitAndSetSpeed();
+}
+
+WebRtc_Word32
+VP8Encoder::InitAndSetSpeed()
+{
+ // construct encoder context
+ vpx_codec_enc_cfg_t cfg_copy = *_cfg;
+ if (vpx_codec_enc_init(_encoder, vpx_codec_vp8_cx(), _cfg, 0))
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ vpx_codec_control(_encoder, VP8E_SET_CPUUSED, _cpuSpeed);
+
+ *_cfg = cfg_copy;
+
+ _inited = true;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+#ifdef VP8_LATEST
+WebRtc_Word32
+VP8Encoder::MaxIntraTarget(WebRtc_Word32 optimalBuffersize)
+{
+ // Set max to 1 / 2 of the optimal buffer level (normalize by target BR).
+ // Max target size = 0.5 * optimalBufferSize * targetBR[Kbps].
+ // This values is presented in percentage of perFrameBw.
+ // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
+ // The target in % is as follows:
+ WebRtc_Word32 targetPct = (optimalBuffersize >> 1) * _maxFrameRate / 10;
+
+ // Don't go below 3 times the per frame bandwidth.
+ const WebRtc_Word32 minIntraTh = 300;
+ targetPct = (targetPct < minIntraTh) ? minIntraTh: targetPct;
+
+ return targetPct;
+}
+#endif
+
+WebRtc_Word32
+VP8Encoder::Encode(const RawImage& inputImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ VideoFrameType frameTypes)
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (inputImage._buffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (_encodedCompleteCallback == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+
+ // image in vpx_image_t format
+ _raw->planes[PLANE_Y] = inputImage._buffer;
+ _raw->planes[PLANE_U] = &inputImage._buffer[_height * _width];
+ _raw->planes[PLANE_V] = &inputImage._buffer[_height * _width * 5 >> 2];
+
+ int flags = 0;
+ if (frameTypes == kKeyFrame)
+ {
+ flags |= VPX_EFLAG_FORCE_KF; // will update both golden and altref
+ _encodedImage._frameType = kKeyFrame;
+ _pictureIDLastSentRef = _pictureID;
+ }
+ else
+ {
+#ifdef DEV_PIC_LOSS
+ if (_feedbackModeOn && codecSpecificInfo)
+ {
+ const CodecSpecificInfo* info = static_cast<const
+ CodecSpecificInfo*>(codecSpecificInfo);
+ if (info->codecType == kVideoCodecVP8)
+ {
+ // codecSpecificInfo will contain received RPSI and SLI
+ // picture IDs. This will help us decide on when to switch type
+ // of reference frame
+
+ // if we receive SLI
+ // force using an old golden or altref as a reference
+
+ if (info->codecSpecific.VP8.hasReceivedSLI)
+ {
+ // if this is older than my last acked ref we can ignore it
+ // info->codecSpecific.VP8.pictureIdSLI valid 6 bits => 64 frames
+
+ // since picture id can wrap check if in between our last sent and last acked
+
+ bool sendRefresh = false;
+ // check for a wrap in picture ID
+ if ((_pictureIDLastAcknowledgedRef & 0x3f) > (_pictureID & 0x3f))
+ {
+ // we have a wrap
+ if ( info->codecSpecific.VP8.pictureIdSLI > (_pictureIDLastAcknowledgedRef&0x3f)||
+ info->codecSpecific.VP8.pictureIdSLI < (_pictureID & 0x3f))
+ {
+ sendRefresh = true;
+ }
+ }
+ else if (info->codecSpecific.VP8.pictureIdSLI > (_pictureIDLastAcknowledgedRef&0x3f)&&
+ info->codecSpecific.VP8.pictureIdSLI < (_pictureID & 0x3f))
+ {
+ sendRefresh = true;
+ }
+
+ // right now we could also ignore it if it's older than our last sent ref since
+ // last sent ref only refers back to last acked
+ // _pictureIDLastSentRef;
+ if (sendRefresh)
+ {
+ flags |= VP8_EFLAG_NO_REF_LAST; // Don't reference the last frame
+
+ if (_haveReceivedAcknowledgement)
+ {
+ // we cant set this if we refer to a key frame
+ if (_lastAcknowledgedIsGolden)
+ {
+ flags |= VP8_EFLAG_NO_REF_ARF; // Don't reference the alternate reference frame
+ }
+ else
+ {
+ flags |= VP8_EFLAG_NO_REF_GF; // Don't reference the golden frame
+ }
+ }
+ }
+ }
+ if (info->codecSpecific.VP8.hasReceivedRPSI)
+ {
+ if ((info->codecSpecific.VP8.pictureIdRPSI & 0x3fff) == (_pictureIDLastSentRef & 0x3fff)) // compare 14 bits
+ {
+ // remote peer have received our last reference frame
+ // switch frame type
+ _haveReceivedAcknowledgement = true;
+ _nextRefIsGolden = !_nextRefIsGolden;
+ _pictureIDLastAcknowledgedRef = _pictureIDLastSentRef;
+ }
+ }
+ }
+ const WebRtc_UWord16 periodX = 64; // we need a period X to decide on the distance between golden and altref
+ if (_pictureID % periodX == 0)
+ {
+ // only required if we have had a loss
+ // however we don't acknowledge a SLI so if that is lost it's no good
+ flags |= VP8_EFLAG_NO_REF_LAST; // Don't reference the last frame
+
+ if (_nextRefIsGolden)
+ {
+ flags |= VP8_EFLAG_FORCE_GF; // force a golden
+ flags |= VP8_EFLAG_NO_UPD_ARF; // don't update altref
+ if (_haveReceivedAcknowledgement)
+ {
+ // we can't set this if we refer to a key frame
+ // pw temporary as proof of concept
+ flags |= VP8_EFLAG_NO_REF_GF; // Don't reference the golden frame
+ }
+ }
+ else
+ {
+ flags |= VP8_EFLAG_FORCE_ARF; // force an altref
+ flags |= VP8_EFLAG_NO_UPD_GF; // Don't update golden
+ if (_haveReceivedAcknowledgement)
+ {
+ // we can't set this if we refer to a key frame
+ // pw temporary as proof of concept
+ flags |= VP8_EFLAG_NO_REF_ARF; // Don't reference the alternate reference frame
+ }
+ }
+ // remember our last reference frame
+ _pictureIDLastSentRef = _pictureID;
+ }
+ else
+ {
+ flags |= VP8_EFLAG_NO_UPD_GF; // don't update golden
+ flags |= VP8_EFLAG_NO_UPD_ARF; // don't update altref
+ }
+ }
+#endif
+ _encodedImage._frameType = kDeltaFrame;
+ }
+
+ if (vpx_codec_encode(_encoder, _raw, _timeStamp, 1, flags, VPX_DL_REALTIME))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ _timeStamp++;
+
+ const vpx_codec_cx_pkt_t *pkt= vpx_codec_get_cx_data(_encoder, &iter); // no lagging => 1 frame at a time
+ if (pkt == NULL && !_encoder->err)
+ {
+ // dropped frame
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+ else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
+ {
+ // attach Picture ID
+ // we use 14 bits generating 1 or 2 bytes
+ // TODO(hlundin): update to follow latest RTP spec
+ WebRtc_UWord8 pictureIdSize = 2;
+ // TODO(hlundin): we should refactor this so that the pictureID is
+ // signaled through a codec specific struct and added in the RTP module.
+ if (_pictureID > 0x7f)
+ {
+ // more than 7 bits
+ _encodedImage._buffer[0] = 0x80 | (WebRtc_UWord8)(_pictureID >> 7);
+ _encodedImage._buffer[1] = (WebRtc_UWord8)(_pictureID & 0x7f);
+ }
+ else
+ {
+ _encodedImage._buffer[0] = (WebRtc_UWord8)_pictureID;
+ pictureIdSize = 1;
+ }
+
+ memcpy(_encodedImage._buffer+pictureIdSize, pkt->data.frame.buf, pkt->data.frame.sz);
+ _encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz) + pictureIdSize;
+ _encodedImage._encodedHeight = _raw->h;
+ _encodedImage._encodedWidth = _raw->w;
+
+ // check if encoded frame is a key frame
+ if (pkt->data.frame.flags & VPX_FRAME_IS_KEY)
+ {
+ _encodedImage._frameType = kKeyFrame;
+ }
+
+ if (_encodedImage._length > 0)
+ {
+ _encodedImage._timeStamp = inputImage._timeStamp;
+
+ // Figure out where partition boundaries are located.
+ RTPFragmentationHeader fragInfo;
+ fragInfo.VerifyAndAllocateFragmentationHeader(2); // two partitions: 1st and 2nd
+
+ // First partition
+ fragInfo.fragmentationOffset[0] = 0;
+ WebRtc_UWord8 *firstByte = &_encodedImage._buffer[pictureIdSize];
+ WebRtc_UWord32 tmpSize = (firstByte[2] << 16) | (firstByte[1] << 8)
+ | firstByte[0];
+ fragInfo.fragmentationLength[0] = (tmpSize >> 5) & 0x7FFFF;
+ // Let the PictureID belong to the first partition.
+ fragInfo.fragmentationLength[0] += pictureIdSize;
+ fragInfo.fragmentationPlType[0] = 0; // not known here
+ fragInfo.fragmentationTimeDiff[0] = 0;
+
+ // Second partition
+ fragInfo.fragmentationOffset[1] = fragInfo.fragmentationLength[0];
+ fragInfo.fragmentationLength[1] = _encodedImage._length -
+ fragInfo.fragmentationLength[0];
+ fragInfo.fragmentationPlType[1] = 0; // not known here
+ fragInfo.fragmentationTimeDiff[1] = 0;
+
+ _encodedCompleteCallback->Encoded(_encodedImage, NULL, &fragInfo);
+ }
+
+ _pictureID++; // prepare next
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+ return WEBRTC_VIDEO_CODEC_ERROR;
+}
+
+WebRtc_Word32
+VP8Encoder::SetPacketLoss(WebRtc_UWord32 packetLoss)
+{
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Encoder::RegisterEncodeCompleteCallback(EncodedImageCallback* callback)
+{
+ _encodedCompleteCallback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+VP8Decoder::VP8Decoder():
+ _decodeCompleteCallback(NULL),
+ _inited(false),
+ _feedbackModeOn(false),
+ _decoder(NULL),
+ _inst(NULL),
+ _numCores(1),
+ _lastKeyFrame(),
+ _imageFormat(VPX_IMG_FMT_NONE),
+ _refFrame(NULL)
+{
+}
+
+VP8Decoder::~VP8Decoder()
+{
+ _inited = true; // in order to do the actual release
+ Release();
+}
+
+WebRtc_Word32
+VP8Decoder::Reset()
+{
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ InitDecode(NULL, 1);
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Decoder::InitDecode(const VideoCodec* inst,
+ WebRtc_Word32 numberOfCores)
+{
+ vp8_postproc_cfg_t ppcfg;
+ WebRtc_Word32 retVal = Release();
+ if (retVal < 0 )
+ {
+ return retVal;
+ }
+ if (_decoder == NULL)
+ {
+ _decoder = new vpx_dec_ctx_t;
+ }
+#ifdef DEV_PIC_LOSS
+ if(inst && inst->codecType == kVideoCodecVP8)
+ {
+ _feedbackModeOn = inst->codecSpecific.VP8.feedbackModeOn;
+ }
+#endif
+
+ vpx_codec_dec_cfg_t cfg;
+ cfg.threads = numberOfCores;
+ cfg.h = cfg.w = 0; // set after decode
+
+ if(vpx_codec_dec_init(_decoder, vpx_codec_vp8_dx(), NULL, 0))
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+
+ // TODO(mikhal): evaluate post-proc settings
+ // config post-processing settings for decoder
+ ppcfg.post_proc_flag = VP8_DEBLOCK;
+ // Strength of deblocking filter. Valid range:[0,16]
+ ppcfg.deblocking_level = 5;
+ // 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;
+}
+
+WebRtc_Word32
+VP8Decoder::Decode(const EncodedImage& inputImage,
+ bool missingFrames,
+ const void* /*codecSpecificInfo*/,
+ WebRtc_Word64 /*renderTimeMs*/)
+ {
+ if (!_inited)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (inputImage._buffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (_decodeCompleteCallback == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (inputImage._length <= 0)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ if (inputImage._completeFrame == false)
+ {
+ // future improvement
+ // we can't decode this frame
+ if (_feedbackModeOn)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI;
+ }
+ else
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ }
+ vpx_dec_iter_t _iter = NULL;
+ vpx_image_t* img;
+ WebRtc_UWord64 pictureID = 0;
+
+ // scan for number of bytes used for picture ID
+ WebRtc_UWord8 numberOfBytes;
+ for (numberOfBytes = 0;(inputImage._buffer[numberOfBytes] & 0x80 ) &&
+ numberOfBytes < 8; numberOfBytes++)
+ {
+ pictureID += inputImage._buffer[numberOfBytes] & 0x7f;
+ pictureID <<= 7;
+ }
+ pictureID += inputImage._buffer[numberOfBytes] & 0x7f;
+ numberOfBytes++;
+
+ // check for missing frames
+ if (missingFrames)
+ {
+ // call decoder with zero data length to signal missing frames
+ if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ }
+
+ // we remove the picture ID here
+ if (vpx_codec_decode(_decoder,
+ inputImage._buffer + numberOfBytes,
+ inputImage._length - numberOfBytes,
+ 0,
+ VPX_DL_REALTIME))
+ {
+ 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.
+ WebRtc_UWord32 tempSize = _lastKeyFrame._size; // Save size.
+ _lastKeyFrame = inputImage; // Shallow copy.
+ _lastKeyFrame._buffer = tempBuffer; // Restore buffer ptr.
+ _lastKeyFrame._size = tempSize; // Restore buffer size.
+ if (!_lastKeyFrame._buffer)
+ {
+ // Allocate memory.
+ _lastKeyFrame._size = bytesToCopy;
+ _lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size];
+ }
+ // Copy encoded frame.
+ memcpy(_lastKeyFrame._buffer, inputImage._buffer + numberOfBytes,
+ bytesToCopy);
+ _lastKeyFrame._length = bytesToCopy;
+ }
+
+ int lastRefUpdates = 0;
+#ifdef DEV_PIC_LOSS
+ if (vpx_codec_control(_decoder, VP8D_GET_LAST_REF_UPDATES, &lastRefUpdates))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ int corrupted = 0;
+ if (vpx_codec_control(_decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted))
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+#endif
+
+ img = vpx_codec_get_frame(_decoder, &_iter);
+
+ // Allocate memory for decoded image
+ WebRtc_UWord32 requiredSize = (3 * img->h * img->w) >> 1;
+ if (_decodedImage._buffer != NULL)
+ {
+ delete [] _decodedImage._buffer;
+ _decodedImage._buffer = NULL;
+ }
+ if (_decodedImage._buffer == NULL)
+ {
+ _decodedImage._size = requiredSize;
+ _decodedImage._buffer = new WebRtc_UWord8[_decodedImage._size];
+ if (_decodedImage._buffer == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ }
+
+ WebRtc_UWord8* buf;
+ WebRtc_UWord32 locCnt = 0;
+ WebRtc_UWord32 plane, y;
+
+ for (plane = 0; plane < 3; plane++)
+ {
+ buf = img->planes[plane];
+ WebRtc_UWord32 shiftFactor = plane ? 1 : 0;
+ for(y = 0; y < img->d_h >> shiftFactor; y++)
+ {
+ memcpy(&_decodedImage._buffer[locCnt], buf, img->d_w >> shiftFactor);
+ locCnt += img->d_w >> shiftFactor;
+ buf += img->stride[plane];
+ }
+ }
+
+ // Set image parameters
+ _decodedImage._height = img->d_h;
+ _decodedImage._width = img->d_w;
+ _decodedImage._length = (3 * img->d_h * img->d_w) >> 1;
+ _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
+ // provide an API for now I added it temporarily
+ if((lastRefUpdates & VP8_GOLD_FRAME) || (lastRefUpdates & VP8_ALTR_FRAME))
+ {
+ if (!missingFrames && (inputImage._completeFrame == true))
+ //if (!corrupted) // TODO(pw): Can we engage this line intead of the above?
+ {
+ _decodeCompleteCallback->ReceivedDecodedReferenceFrame(pictureID);
+ }
+ }
+ _decodeCompleteCallback->ReceivedDecodedFrame(pictureID);
+
+#ifdef DEV_PIC_LOSS
+ if (corrupted)
+ {
+ // we can decode but with artifacts
+ return WEBRTC_VIDEO_CODEC_REQUEST_SLI;
+ }
+#endif
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Decoder::RegisterDecodeCompleteCallback(DecodedImageCallback* callback)
+{
+ _decodeCompleteCallback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VP8Decoder::Release()
+{
+ if (_decodedImage._buffer != NULL)
+ {
+ delete [] _decodedImage._buffer;
+ _decodedImage._buffer = NULL;
+ }
+ if (_lastKeyFrame._buffer != NULL)
+ {
+ delete [] _lastKeyFrame._buffer;
+ _lastKeyFrame._buffer = NULL;
+ }
+ if (_decoder != NULL)
+ {
+ if(vpx_codec_destroy(_decoder))
+ {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ 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<vpx_img_fmt_t>(_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<VideoDecoder*>(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
diff --git a/src/modules/video_coding/codecs/vp8/main/source/vp8.gyp b/src/modules/video_coding/codecs/vp8/main/source/vp8.gyp
new file mode 100644
index 0000000..ba351fd
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/vp8.gyp
@@ -0,0 +1,96 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ '../../../../../../common_settings.gypi', # Common settings
+ ],
+ 'targets': [
+ {
+ 'target_name': 'webrtc_vp8',
+ 'type': '<(library)',
+ 'dependencies': [
+ '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
+ ],
+ 'include_dirs': [
+ '../interface',
+ '../../../../../../common_video/interface',
+ '../../../interface',
+ '../../../../../interface',
+ ],
+ 'conditions': [
+ ['build_with_chromium==1', {
+ 'conditions': [
+ ['OS=="win"', {
+ 'dependencies': [
+ # We don't want to link with the static library inside Chromium
+ # on Windows. Chromium uses the ffmpeg DLL and exports the
+ # necessary libvpx symbols for us.
+ '../../../../../../../libvpx/libvpx.gyp:libvpx_include',
+ ],
+ },{
+ 'dependencies': [
+ '../../../../../../../libvpx/libvpx.gyp:libvpx',
+ ],
+ 'include_dirs': [
+ '../../../../../../../libvpx/source/libvpx',
+ ],
+ }],
+ ],
+ },{
+ 'dependencies': [
+ '../../../../../../../third_party/libvpx/libvpx.gyp:libvpx',
+ ],
+ 'include_dirs': [
+ '../../../../../../../third_party/libvpx',
+ ],
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../interface',
+ '../../../../../../common_video/interface',
+ '../../../interface',
+ ],
+ },
+ 'sources': [
+ '../interface/vp8.h',
+ 'vp8.cc',
+ ],
+ },
+
+ {
+ 'target_name': 'vp8_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'webrtc_vp8',
+ '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
+ '../../../test_framework/test_framework.gyp:test_framework',
+ '../../../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib'
+ ],
+ 'sources': [
+ # header files
+ '../test/benchmark.h',
+ '../test/normal_async_test.h',
+ '../test/packet_loss_test.h',
+ '../test/unit_test.h',
+ '../test/dual_decoder_test.h',
+
+ # source files
+ '../test/benchmark.cc',
+ '../test/normal_async_test.cc',
+ '../test/packet_loss_test.cc',
+ '../test/tester.cc',
+ '../test/unit_test.cc',
+ '../test/dual_decoder_test.cc',
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/modules/video_coding/codecs/vp8/main/test/benchmark.cc b/src/modules/video_coding/codecs/vp8/main/test/benchmark.cc
new file mode 100644
index 0000000..960ba02
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/benchmark.cc
@@ -0,0 +1,44 @@
+/*
+ * 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 "benchmark.h"
+#include "vp8.h"
+
+using namespace webrtc;
+
+VP8Benchmark::VP8Benchmark()
+:
+Benchmark("VP8Benchmark", "VP8 benchmark over a range of test cases", "../../VP8Benchmark.txt", "VP8")
+{
+}
+
+VP8Benchmark::VP8Benchmark(std::string name, std::string description)
+:
+Benchmark(name, description, "../../VP8Benchmark.txt", "VP8")
+{
+}
+
+VP8Benchmark::VP8Benchmark(std::string name, std::string description, std::string resultsFileName)
+:
+Benchmark(name, description, resultsFileName, "VP8")
+{
+}
+
+VideoEncoder*
+VP8Benchmark::GetNewEncoder()
+{
+ return new VP8Encoder();
+}
+
+VideoDecoder*
+VP8Benchmark::GetNewDecoder()
+{
+ return new VP8Decoder();
+}
diff --git a/src/modules/video_coding/codecs/vp8/main/test/benchmark.h b/src/modules/video_coding/codecs/vp8/main/test/benchmark.h
new file mode 100644
index 0000000..5143de3
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/benchmark.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_
+
+#include "../../../test_framework/benchmark.h"
+
+class VP8Benchmark : public Benchmark
+{
+public:
+ VP8Benchmark();
+ VP8Benchmark(std::string name, std::string description);
+ VP8Benchmark(std::string name, std::string description, std::string resultsFileName);
+
+protected:
+ virtual webrtc::VideoEncoder* GetNewEncoder();
+ virtual webrtc::VideoDecoder* GetNewDecoder();
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_
diff --git a/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc b/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc
new file mode 100644
index 0000000..a8cbe30
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc
@@ -0,0 +1,219 @@
+/*
+ * 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 "dual_decoder_test.h"
+
+#include <assert.h>
+#include <string.h> // memcmp
+#include <time.h>
+
+VP8DualDecoderTest::VP8DualDecoderTest(float bitRate)
+:
+VP8NormalAsyncTest(bitRate)
+{
+ _decoder2 = NULL;
+}
+
+VP8DualDecoderTest::VP8DualDecoderTest()
+:
+_decoder2(NULL),
+VP8NormalAsyncTest("VP8 Dual Decoder Test", "Tests VP8 dual decoder", 1)
+{}
+
+VP8DualDecoderTest::~VP8DualDecoderTest()
+{
+ if(_decoder2)
+ {
+ _decoder2->Release();
+ delete _decoder2;
+ }
+
+ _decodedVideoBuffer2.Free();
+}
+
+void
+VP8DualDecoderTest::Perform()
+{
+ _inname = "test/testFiles/foreman_cif.yuv";
+ CodecSettings(352, 288, 30, _bitRate);
+ Setup();
+ _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
+ _decodedVideoBuffer2.VerifyAndAllocate(_lengthSourceFrame);
+ if(_encoder->InitEncode(&_inst, 4, 1460) < 0)
+ {
+ exit(EXIT_FAILURE);
+ }
+ _decoder->InitDecode(&_inst,1);
+
+ FrameQueue frameQueue;
+ VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this);
+ DualDecoderCompleteCallback decCallback(&_decodedVideoBuffer);
+ DualDecoderCompleteCallback decCallback2(&_decodedVideoBuffer2);
+ _encoder->RegisterEncodeCompleteCallback(&encCallback);
+ _decoder->RegisterDecodeCompleteCallback(&decCallback);
+ 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)
+ {
+ if (_encFrameCnt == 10)
+ {
+ // initialize second decoder and copy state
+ _decoder2 = static_cast<webrtc::VP8Decoder *>(_decoder->Copy());
+ assert(_decoder2 != NULL);
+ _decoder2->RegisterDecodeCompleteCallback(&decCallback2);
+ }
+ CodecSpecific_InitBitrate();
+ complete = Encode();
+ 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();
+}
+
+
+int
+VP8DualDecoderTest::Decode(int lossValue)
+{
+ _sumEncBytes += _frameToDecode->_frame->GetLength();
+ double starttime = 0;
+ 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,
+ _frameToDecode->_codecSpecificInfo);
+ // second decoder
+ if (_decoder2)
+ {
+ int ret2 = _decoder2->Decode(encodedImage, _missingFrames,
+ _frameToDecode->_codecSpecificInfo, 0 /* dummy */);
+
+ // check return values
+ if (ret < 0 || ret2 < 0 || ret2 != ret)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ // compare decoded images
+ if (!CheckIfBitExact(_decodedVideoBuffer.GetBuffer(),
+ _decodedVideoBuffer.GetLength(),
+ _decodedVideoBuffer2.GetBuffer(), _decodedVideoBuffer.GetLength()))
+ {
+ fprintf(stderr,"\n\nClone output different from master.\n\n");
+ exit(EXIT_FAILURE);
+ }
+
+ }
+
+ _missingFrames = false;
+ return ret;
+}
+
+
+bool
+VP8DualDecoderTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes,
+ const void* ptrB, unsigned int bLengthBytes)
+{
+ if (aLengthBytes != bLengthBytes)
+ {
+ return false;
+ }
+
+ return memcmp(ptrA, ptrB, aLengthBytes) == 0;
+}
+
+WebRtc_Word32 DualDecoderCompleteCallback::Decoded(webrtc::RawImage& image)
+{
+ _decodedVideoBuffer->VerifyAndAllocate(image._length);
+ _decodedVideoBuffer->CopyBuffer(image._length, image._buffer);
+ _decodedVideoBuffer->SetWidth(image._width);
+ _decodedVideoBuffer->SetHeight(image._height);
+ _decodedVideoBuffer->SetTimeStamp(image._timeStamp);
+ _decodeComplete = true;
+ return 0;
+}
+
+bool DualDecoderCompleteCallback::DecodeComplete()
+{
+ if (_decodeComplete)
+ {
+ _decodeComplete = false;
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h b/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h
new file mode 100644
index 0000000..4af4e3e
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DUAL_DECODER_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DUAL_DECODER_TEST_H_
+
+#include "vp8.h"
+#include "normal_async_test.h"
+
+class DualDecoderCompleteCallback;
+
+class VP8DualDecoderTest : public VP8NormalAsyncTest
+{
+public:
+ VP8DualDecoderTest(float bitRate);
+ VP8DualDecoderTest();
+ virtual ~VP8DualDecoderTest();
+ virtual void Perform();
+protected:
+ VP8DualDecoderTest(std::string name, std::string description,
+ unsigned int testNo)
+ : VP8NormalAsyncTest(name, description, testNo) {}
+ virtual int Decode(int lossValue = 0);
+
+ webrtc::VP8Decoder* _decoder2;
+ TestVideoBuffer _decodedVideoBuffer2;
+ static bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes,
+ const void *ptrB, unsigned int bLengthBytes);
+private:
+};
+
+class DualDecoderCompleteCallback : public webrtc::DecodedImageCallback
+{
+public:
+ DualDecoderCompleteCallback(TestVideoBuffer* buffer)
+ : _decodedVideoBuffer(buffer), _decodeComplete(false) {}
+ WebRtc_Word32 Decoded(webrtc::RawImage& decodedImage);
+ bool DecodeComplete();
+private:
+ TestVideoBuffer* _decodedVideoBuffer;
+ bool _decodeComplete;
+};
+
+
+#endif
diff --git a/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc b/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc
new file mode 100644
index 0000000..9ec2233
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc
@@ -0,0 +1,81 @@
+/*
+ * 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 "normal_async_test.h"
+
+using namespace webrtc;
+
+VP8NormalAsyncTest::VP8NormalAsyncTest(WebRtc_UWord32 bitRate) :
+ NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", bitRate, 1),
+ _hasReceivedRPSI(false)
+{
+}
+
+VP8NormalAsyncTest::VP8NormalAsyncTest(WebRtc_UWord32 bitRate, unsigned int testNo):
+ NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", bitRate, testNo),
+ _hasReceivedRPSI(false)
+{
+}
+
+void
+VP8NormalAsyncTest::CodecSettings(int width, int height, WebRtc_UWord32 frameRate /*=30*/, WebRtc_UWord32 bitRate /*=0*/)
+{
+ if (bitRate > 0)
+ {
+ _bitRate = bitRate;
+
+ }else if (_bitRate == 0)
+ {
+ _bitRate = 600;
+ }
+ _inst.codecType = kVideoCodecVP8;
+ _inst.codecSpecific.VP8.feedbackModeOn = true;
+ _inst.codecSpecific.VP8.pictureLossIndicationOn = true;
+ _inst.codecSpecific.VP8.complexity;
+ _inst.maxFramerate = (unsigned char)frameRate;
+ _inst.startBitrate = _bitRate;
+ _inst.maxBitrate = 8000;
+ _inst.width = width;
+ _inst.height = height;
+}
+
+void
+VP8NormalAsyncTest::CodecSpecific_InitBitrate()
+{
+ if (_bitRate == 0)
+ {
+ _encoder->SetRates(600, _inst.maxFramerate);
+ }else
+ {
+ _encoder->SetRates(_bitRate, _inst.maxFramerate);
+ }
+}
+
+WebRtc_Word32
+VP8NormalAsyncTest::ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId)
+{
+ _pictureIdRPSI = pictureId;
+ _hasReceivedRPSI = true;
+ return 0;
+}
+
+void*
+VP8NormalAsyncTest::CreateEncoderSpecificInfo() const
+{
+ CodecSpecificInfo* vp8CodecSpecificInfo = new CodecSpecificInfo();
+ vp8CodecSpecificInfo->codecType = kVideoCodecVP8;
+ vp8CodecSpecificInfo->codecSpecific.VP8.hasReceivedRPSI = _hasReceivedRPSI;
+ vp8CodecSpecificInfo->codecSpecific.VP8.pictureIdRPSI = _pictureIdRPSI;
+ vp8CodecSpecificInfo->codecSpecific.VP8.hasReceivedSLI = false;
+
+ _hasReceivedRPSI = false;
+
+ return vp8CodecSpecificInfo;
+}
diff --git a/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.h b/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.h
new file mode 100644
index 0000000..ecfaa8a
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/normal_async_test.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_NORMAL_ASYNC_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_NORMAL_ASYNC_TEST_H_
+
+#include "../../../test_framework/normal_async_test.h"
+
+class VP8NormalAsyncTest : public NormalAsyncTest
+{
+public:
+ VP8NormalAsyncTest(WebRtc_UWord32 bitRate);
+ VP8NormalAsyncTest(WebRtc_UWord32 bitRate, unsigned int testNo);
+ VP8NormalAsyncTest() : NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", 1) {}
+protected:
+ VP8NormalAsyncTest(std::string name, std::string description, unsigned int testNo) : NormalAsyncTest(name, description, testNo) {}
+ virtual void CodecSpecific_InitBitrate();
+ virtual void CodecSettings(int width, int height, WebRtc_UWord32 frameRate=30, WebRtc_UWord32 bitRate=0);
+ virtual void* CreateEncoderSpecificInfo() const;
+ virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId);
+private:
+ mutable bool _hasReceivedRPSI;
+ WebRtc_UWord64 _pictureIdRPSI;
+};
+
+#endif
diff --git a/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc b/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc
new file mode 100644
index 0000000..3a7324e
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc
@@ -0,0 +1,57 @@
+/*
+ * 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 "packet_loss_test.h"
+#include <cassert>
+
+VP8PacketLossTest::VP8PacketLossTest()
+:
+PacketLossTest("VP8PacketLossTest", "Encode, remove lost packets, decode")
+{
+}
+
+VP8PacketLossTest::VP8PacketLossTest(std::string name, std::string description)
+:
+PacketLossTest(name, description)
+{
+}
+
+VP8PacketLossTest::VP8PacketLossTest(double lossRate, bool useNack)
+:
+PacketLossTest("VP8PacketLossTest", "Encode, remove lost packets, decode", lossRate, useNack)
+{
+}
+
+void
+VP8PacketLossTest::CodecSpecific_InitBitrate()
+{
+ assert(_bitRate > 0);
+ WebRtc_UWord32 simulatedBitRate;
+ if (_lossProbability != _lossRate)
+ {
+ // Simulating NACK
+ simulatedBitRate = (WebRtc_UWord32)(_bitRate / (1 + _lossRate));
+ }
+ else
+ {
+ simulatedBitRate = _bitRate;
+ }
+ _encoder->SetRates(simulatedBitRate, _inst.maxFramerate);
+}
+
+int VP8PacketLossTest::ByteLoss(int size, unsigned char* /* pkg */, int bytesToLose)
+{
+ int retLength = size - bytesToLose;
+ if (retLength < 4)
+ {
+ retLength = 4;
+ }
+ return retLength;
+}
diff --git a/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h b/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h
new file mode 100644
index 0000000..96c6e88
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_
+
+#include "../../../test_framework/packet_loss_test.h"
+
+class VP8PacketLossTest : public PacketLossTest
+{
+public:
+ VP8PacketLossTest();
+ VP8PacketLossTest(double lossRate, bool useNack);
+
+protected:
+ VP8PacketLossTest(std::string name, std::string description);
+ virtual void CodecSpecific_InitBitrate();
+ virtual int ByteLoss(int size, unsigned char *pkg, int bytesToLose);
+
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_
diff --git a/src/modules/video_coding/codecs/vp8/main/test/tester.cc b/src/modules/video_coding/codecs/vp8/main/test/tester.cc
new file mode 100644
index 0000000..cd5bb02
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/tester.cc
@@ -0,0 +1,57 @@
+/*
+ * 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 "packet_loss_test.h"
+#include "benchmark.h"
+#include "unit_test.h"
+#include "normal_async_test.h"
+#include "dual_decoder_test.h"
+#include "vp8.h"
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+using namespace webrtc;
+
+void PopulateTests(std::vector<Test*>* tests)
+{
+ tests->push_back(new VP8UnitTest());
+// tests->push_back(new VP8DualDecoderTest());
+// tests->push_back(new VP8Benchmark());
+// tests->push_back(new VP8PacketLossTest());
+// tests->push_back(new VP8NormalAsyncTest());
+}
+
+int main()
+{
+ VP8Encoder* enc;
+ VP8Decoder* dec;
+ std::vector<Test*> tests;
+ PopulateTests(&tests);
+ std::fstream log;
+ log.open("../../TestLog.txt", std::fstream::out | std::fstream::app);
+ std::vector<Test*>::iterator it;
+ for (it = tests.begin() ; it < tests.end(); it++)
+ {
+ enc = new VP8Encoder();
+ dec = new VP8Decoder();
+ (*it)->SetEncoder(enc);
+ (*it)->SetDecoder(dec);
+ (*it)->SetLog(&log);
+ (*it)->Perform();
+ (*it)->Print();
+ delete enc;
+ delete dec;
+ delete *it;
+ }
+ log.close();
+ tests.pop_back();
+ return 0;
+}
diff --git a/src/modules/video_coding/codecs/vp8/main/test/unit_test.cc b/src/modules/video_coding/codecs/vp8/main/test/unit_test.cc
new file mode 100644
index 0000000..80171b1
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/unit_test.cc
@@ -0,0 +1,168 @@
+/*
+ * 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 "unit_test.h"
+#include "../../../test_framework/video_source.h"
+#include "vp8.h"
+
+#include <string.h>
+
+using namespace webrtc;
+
+VP8UnitTest::VP8UnitTest()
+:
+UnitTest("VP8UnitTest", "Unit test")
+{
+}
+
+VP8UnitTest::VP8UnitTest(std::string name, std::string description)
+:
+UnitTest(name, description)
+{
+}
+
+void
+VP8UnitTest::Print()
+{
+ WebRtc_Word8 versionStr[64];
+
+ // GetVersion tests.
+
+ VIDEO_TEST(_encoder->Version(versionStr, sizeof(versionStr)) > 0);
+// printf("\n%s", versionStr);
+// UnitTest::Print();
+}
+
+WebRtc_UWord32
+VP8UnitTest::CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate, WebRtc_UWord32 /*frameRate*/)
+{
+ bitRate = _encoder->SetRates(bitRate, _inst.maxFramerate);
+ VIDEO_TEST_EXIT_ON_ERR(bitRate >= 0);
+ return bitRate;
+}
+
+bool
+VP8UnitTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes,
+ const void* ptrB, unsigned int bLengthBytes)
+{
+ const unsigned char* cPtrA = (const unsigned char*)ptrA;
+ const unsigned char* cPtrB = (const unsigned char*)ptrB;
+ // Skip picture ID before comparing
+ int aSkip = PicIdLength(cPtrA);
+ int bSkip = PicIdLength(cPtrB);
+ return UnitTest::CheckIfBitExact(cPtrA + aSkip, aLengthBytes,
+ cPtrB + bSkip, bLengthBytes);
+}
+
+int
+VP8UnitTest::PicIdLength(const unsigned char* ptr)
+{
+ WebRtc_UWord8 numberOfBytes;
+ WebRtc_UWord64 pictureID = 0;
+ for (numberOfBytes = 0; (ptr[numberOfBytes] & 0x80) && numberOfBytes < 8; numberOfBytes++)
+ {
+ pictureID += ptr[numberOfBytes] & 0x7f;
+ pictureID <<= 7;
+ }
+ pictureID += ptr[numberOfBytes] & 0x7f;
+ numberOfBytes++;
+ return numberOfBytes;
+}
+
+void
+VP8UnitTest::Perform()
+{
+ Setup();
+ FILE *outFile = NULL;
+ std::string outFileName;
+ VP8Encoder* enc = (VP8Encoder*)_encoder;
+ VP8Decoder* dec = (VP8Decoder*)_decoder;
+ int frameLength = 0;
+
+ //----- Encoder parameter tests -----
+ //-- Calls before InitEncode() --
+ VIDEO_TEST(enc->Release() == WEBRTC_VIDEO_CODEC_OK);
+ VIDEO_TEST(enc->SetRates(_bitRate, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+
+ VIDEO_TEST(enc->SetRates(_bitRate, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+ // VIDEO_TEST(enc->GetCodecConfigParameters(configParameters, sizeof(configParameters)) ==
+ // WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+
+
+ VideoCodec codecInst;
+ strncpy(codecInst.plName, "VP8", 31);
+ codecInst.plType = 126;
+ codecInst.maxBitrate = 0;
+ codecInst.minBitrate = 0;
+ codecInst.width = 1440;
+ codecInst.height = 1080;
+ codecInst.maxFramerate = 30;
+ codecInst.startBitrate = 300;
+ codecInst.codecSpecific.VP8.complexity = kComplexityNormal;
+ VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+
+
+ //-- Test two problematic level settings --
+ strncpy(codecInst.plName, "VP8", 31);
+ codecInst.plType = 126;
+ codecInst.maxBitrate = 0;
+ codecInst.minBitrate = 0;
+ codecInst.width = 352;
+ codecInst.height = 288;
+ codecInst.maxFramerate = 30;
+ codecInst.codecSpecific.VP8.complexity = kComplexityNormal;
+ codecInst.startBitrate = 300;
+ VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+
+ // Settings not correct for this profile
+ strncpy(codecInst.plName, "VP8", 31);
+ codecInst.plType = 126;
+ codecInst.maxBitrate = 0;
+ codecInst.minBitrate = 0;
+ codecInst.width = 176;
+ codecInst.height = 144;
+ codecInst.maxFramerate = 15;
+ codecInst.codecSpecific.VP8.complexity = kComplexityNormal;
+ codecInst.startBitrate = 300;
+ //VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED);
+
+ VIDEO_TEST_EXIT_ON_ERR(enc->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK);
+
+
+ //-- ProcessNewBitrate() errors --
+ // Bad bitrate.
+ VIDEO_TEST(enc->SetRates(_inst.maxBitrate + 1, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_OK);
+
+ // Signaling not used.
+
+ // Bad packetloss.
+// VIDEO_TEST(enc->SetPacketLoss(300) < 0);
+
+ //----- Decoder parameter tests -----
+ //-- Calls before InitDecode() --
+ VIDEO_TEST(dec->Release() == 0);
+ VIDEO_TEST_EXIT_ON_ERR(dec->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK);
+
+ //-- SetCodecConfigParameters() errors --
+ unsigned char tmpBuf[128];
+ VIDEO_TEST(dec->SetCodecConfigParameters(NULL, sizeof(tmpBuf)) == -1);
+ VIDEO_TEST(dec->SetCodecConfigParameters(tmpBuf, 1) == -1);
+ // Garbage data.
+ VIDEO_TEST(dec->SetCodecConfigParameters(tmpBuf, sizeof(tmpBuf)) == -1);
+
+ //----- Function tests -----
+ outFileName = "../../" + _source->GetName() + "-errResTest.yuv";
+ outFile = fopen(outFileName.c_str(), "wb");
+ VIDEO_TEST_EXIT_ON_ERR(outFile != NULL);
+
+ UnitTest::Perform();
+ Teardown();
+
+}
diff --git a/src/modules/video_coding/codecs/vp8/main/test/unit_test.h b/src/modules/video_coding/codecs/vp8/main/test/unit_test.h
new file mode 100644
index 0000000..5e6652d
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/test/unit_test.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_
+
+#include "../../../test_framework/unit_test.h"
+
+class VP8UnitTest : public UnitTest
+{
+public:
+ VP8UnitTest();
+ VP8UnitTest(std::string name, std::string description);
+ virtual void Perform();
+ virtual void Print();
+
+protected:
+ virtual WebRtc_UWord32 CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate,
+ WebRtc_UWord32 /*frameRate*/);
+ virtual bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes,
+ const void *ptrB, unsigned int bLengthBytes);
+ static int PicIdLength(const unsigned char* ptr);
+};
+
+////////////////////////////////////////////////////////////////
+// RESERVATIONS TO PASSING UNIT TEST ON VP8 CODEC //
+// Test that will not pass: //
+// 1. Check bit exact for decoded images. //
+// 2. Target bitrate - Allow a margin of 10% instead of 5% //
+// 3. Detecting errors in bit stream - NA. //
+////////////////////////////////////////////////////////////////
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_
diff --git a/src/modules/video_coding/main/OWNERS b/src/modules/video_coding/main/OWNERS
new file mode 100644
index 0000000..30eee35
--- /dev/null
+++ b/src/modules/video_coding/main/OWNERS
@@ -0,0 +1,4 @@
+holmer@google.com
+mikhal@google.com
+marpan@google.com
+hlundin@google.com
diff --git a/src/modules/video_coding/main/interface/video_coding.h b/src/modules/video_coding/main/interface/video_coding.h
new file mode 100644
index 0000000..5b0cc18
--- /dev/null
+++ b/src/modules/video_coding/main/interface/video_coding.h
@@ -0,0 +1,497 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_
+#define WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_
+
+#include "module.h"
+#include "module_common_types.h"
+#include "video_coding_defines.h"
+
+namespace webrtc
+{
+
+class VideoEncoder;
+class VideoDecoder;
+struct CodecSpecificInfo;
+
+class VideoCodingModule : public Module
+{
+public:
+ static VideoCodingModule* Create(const WebRtc_Word32 id);
+
+ static void Destroy(VideoCodingModule* module);
+
+ // Get number of supported codecs
+ //
+ // Return value : Number of supported codecs
+ static WebRtc_UWord8 NumberOfCodecs();
+
+ // Get supported codec settings with using id
+ //
+ // Input:
+ // - listId : Id or index of the codec to look up
+ // - codec : Memory where the codec settings will be stored
+ //
+ // Return value : VCM_OK, on success
+ // VCM_PARAMETER_ERROR if codec not supported or id too high
+ static WebRtc_Word32 Codec(const WebRtc_UWord8 listId, VideoCodec* codec);
+
+ // Get supported codec settings using codec type
+ //
+ // Input:
+ // - codecType : The codec type to get settings for
+ // - codec : Memory where the codec settings will be stored
+ //
+ // Return value : VCM_OK, on success
+ // VCM_PARAMETER_ERROR if codec not supported
+ static WebRtc_Word32 Codec(VideoCodecType codecType, VideoCodec* codec);
+
+ /*
+ * Sender
+ */
+
+ // Any encoder-related state of VCM will be initialized to the
+ // same state as when the VCM was created. This will not interrupt
+ // or effect decoding functionality of VCM. VCM will lose all the
+ // encoding-related settings by calling this function.
+ // For instance, a send codec has to be registered again.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 InitializeSender() = 0;
+
+ // Resets the encoder state to the same state as when the encoder
+ // was created.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 ResetEncoder() = 0;
+
+ // Registers a codec to be used for encoding. Calling this
+ // API multiple times overwrites any previously registered codecs.
+ //
+ // Input:
+ // - sendCodec : Settings for the codec to be registered.
+ // - numberOfCores : The number of cores the codec is allowed
+ // to use.
+ // - maxPayloadSize : The maximum size each payload is allowed
+ // to have. Usually MTU - overhead.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec,
+ WebRtc_UWord32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize) = 0;
+
+ // API to get the current send codec in use.
+ //
+ // Input:
+ // - currentSendCodec : Address where the sendCodec will be written.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const = 0;
+
+ // API to get the current send codec type
+ //
+ // Return value : Codec type, on success.
+ // kVideoCodecUnknown, on error or if no send codec is set
+ virtual VideoCodecType SendCodec() const = 0;
+
+ // Register an external encoder object. This can not be used together with
+ // external decoder callbacks.
+ //
+ // Input:
+ // - externalEncoder : Encoder object to be used for encoding frames inserted
+ // with the AddVideoFrame API.
+ // - payloadType : The payload type bound which this encoder is bound to.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder,
+ WebRtc_UWord8 payloadType,
+ bool internalSource = false) = 0;
+
+ // API to get codec config parameters to be sent out-of-band to a receiver.
+ //
+ // Input:
+ // - buffer : Memory where the codec config parameters should be written.
+ // - size : Size of the memory available.
+ //
+ // Return value : Number of bytes written, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size) = 0;
+
+ // API to get currently configured encoder target bit rate.
+ //
+ // Return value : The encoder target bit rate, on success.
+ // < 0, on error.
+ virtual WebRtc_UWord32 Bitrate() const = 0;
+
+ // API to get currently configured encoder target frame rate.
+ //
+ // Return value : The encoder target frame rate, on success.
+ // < 0, on error.
+ virtual WebRtc_UWord32 FrameRate() const = 0;
+
+ // Sets the parameters describing the send channel. These parameters are inputs to the
+ // Media Optimization inside the VCM and also specifies the target bit rate for the
+ // encoder. Bit rate used by NACK should already be compensated for by the user.
+ //
+ // Input:
+ // - availableBandWidth : Band width available for the VCM in kbit/s.
+ // - lossRate : Fractions of lost packets the past second.
+ // (loss rate in percent = 100 * packetLoss / 255)
+ // - RTT : Current round-trip time in ms.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetChannelParameters(WebRtc_UWord32 availableBandWidth,
+ WebRtc_UWord8 lossRate,
+ WebRtc_UWord32 RTT) = 0;
+
+ // Sets the parameters describing the receive channel. These parameters are inputs to the
+ // Media Optimization inside the VCM.
+ //
+ // Input:
+ // - RTT : Current round-trip time in ms.
+ // with the most amount available bandwidth in a conference
+ // scenario
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetReceiveChannelParameters(WebRtc_UWord32 RTT) = 0;
+
+ // Register a transport callback which will be called to deliver the encoded data and
+ // side information.
+ //
+ // Input:
+ // - transport : The callback object to register.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterTransportCallback(VCMPacketizationCallback* transport) = 0;
+
+ // Register video output information callback which will be called to deliver information
+ // about the video stream produced by the encoder, for instance the average frame rate and
+ // bit rate.
+ //
+ // Input:
+ // - outputInformation : The callback object to register.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterSendStatisticsCallback(
+ VCMSendStatisticsCallback* sendStats) = 0;
+
+ // Register a video quality settings callback which will be called when
+ // frame rate/dimensions need to be updated for video quality optimization
+ //
+ // Input:
+ // - videoQMSettings : The callback object to register.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error
+ virtual WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings) = 0;
+
+ // Register a video protection callback which will be called to deliver
+ // the requested FEC rate and NACK status (on/off).
+ //
+ // Input:
+ // - protection : The callback object to register.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protection) = 0;
+
+ // Enable or disable a video protection method.
+ //
+ // Input:
+ // - videoProtection : The method to enable or disable.
+ // - enable : True if the method should be enabled, false if
+ // it should be disabled.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetVideoProtection(VCMVideoProtection videoProtection,
+ bool enable) = 0;
+
+ // Add one raw video frame to the encoder. This function does all the necessary
+ // processing, then decides what frame type to encode, or if the frame should be
+ // dropped. If the frame should be encoded it passes the frame to the encoder
+ // before it returns.
+ //
+ // Input:
+ // - videoFrame : Video frame to encode.
+ // - codecSpecificInfo : Extra codec information, e.g., pre-parsed in-band signaling.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 AddVideoFrame(
+ const VideoFrame& videoFrame,
+ const VideoContentMetrics* _contentMetrics = NULL,
+ const CodecSpecificInfo* codecSpecificInfo = NULL) = 0;
+
+ // Next frame encoded should be of the type frameType.
+ //
+ // Input:
+ // - frameType : The frame type to encode next time a VideoFrame
+ // is added to the module.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 FrameTypeRequest(FrameType frameType) = 0;
+
+ // Frame Dropper enable. Can be used to disable the frame dropping when the encoder
+ // over-uses its bit rate. This API is designed to be used when the encoded frames
+ // are supposed to be stored to an AVI file, or when the I420 codec is used and the
+ // target bit rate shouldn't affect the frame rate.
+ //
+ // Input:
+ // - enable : True to enable the setting, false to disable it.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 EnableFrameDropper(bool enable) = 0;
+
+ // Sent frame counters
+ virtual WebRtc_Word32 SentFrameCount(VCMFrameCount& frameCount) const = 0;
+
+ /*
+ * Receiver
+ */
+
+ // The receiver state of the VCM will be initialized to the
+ // same state as when the VCM was created. This will not interrupt
+ // or effect the send side functionality of VCM. VCM will lose all the
+ // decoding-related settings by calling this function. All frames
+ // inside the jitter buffer are flushed and the delay is reset.
+ // For instance, a receive codec has to be registered again.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 InitializeReceiver() = 0;
+
+ // Register possible receive codecs, can be called multiple times for different codecs.
+ // The module will automatically switch between registered codecs depending on the
+ // payload type of incoming frames. The actual decoder will be created when needed.
+ //
+ // Input:
+ // - receiveCodec : Settings for the codec to be registered.
+ // - numberOfCores : Number of CPU cores that the decoder is allowed to use.
+ // - requireKeyFrame : Set this to true if you don't want any delta frames
+ // to be decoded until the first key frame has been decoded.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec,
+ WebRtc_Word32 numberOfCores,
+ bool requireKeyFrame = false) = 0;
+
+ // Register an externally defined decoder/renderer object. Can be a decoder only or a
+ // decoder coupled with a renderer. Note that RegisterReceiveCodec must be called to
+ // be used for decoding incoming streams.
+ //
+ // Input:
+ // - externalDecoder : The external decoder/renderer object.
+ // - payloadType : The payload type which this decoder should be
+ // registered to.
+ // - internalRenderTiming : True if the internal renderer (if any) of the decoder
+ // object can make sure to render at a given time in ms.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming) = 0;
+
+ // Register a receive callback. Will be called whenever there is a new frame ready
+ // for rendering.
+ //
+ // Input:
+ // - receiveCallback : The callback object to be used by the module when a
+ // frame is ready for rendering.
+ // De-register with a NULL pointer.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterReceiveCallback(VCMReceiveCallback* receiveCallback) = 0;
+
+ // Register a receive statistics callback which will be called to deliver information
+ // about the video stream received by the receiving side of the VCM, for instance the
+ // average frame rate and bit rate.
+ //
+ // Input:
+ // - receiveStats : The callback object to register.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterReceiveStatisticsCallback(
+ VCMReceiveStatisticsCallback* receiveStats) = 0;
+
+ // Register a frame type request callback. This callback will be called when the
+ // module needs to request specific frame types from the send side.
+ //
+ // Input:
+ // - frameTypeCallback : The callback object to be used by the module when
+ // requesting a specific type of frame from the send side.
+ // De-register with a NULL pointer.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterFrameTypeCallback(
+ VCMFrameTypeCallback* frameTypeCallback) = 0;
+
+ // Register a frame storage callback. This callback will be called right before an
+ // encoded frame is given to the decoder. Useful for recording the incoming video sequence.
+ //
+ // Input:
+ // - frameStorageCallback : The callback object used by the module
+ // to store a received encoded frame.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 RegisterFrameStorageCallback(
+ VCMFrameStorageCallback* frameStorageCallback) = 0;
+
+ // Registers a callback which is called whenever the receive side of the VCM
+ // encounters holes in the packet sequence and needs packets to be retransmitted.
+ //
+ // Input:
+ // - callback : The callback to be registered in the VCM.
+ //
+ // Return value : VCM_OK, on success.
+ // <0, on error.
+ virtual WebRtc_Word32 RegisterPacketRequestCallback(
+ VCMPacketRequestCallback* callback) = 0;
+
+ // Waits for the next frame in the jitter buffer to become complete
+ // (waits no longer than maxWaitTimeMs), then passes it to the decoder for decoding.
+ // Should be called as often as possible to get the most out of the decoder.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 Decode(WebRtc_UWord16 maxWaitTimeMs = 200) = 0;
+
+ // Waits for the next frame in the dual jitter buffer to become complete
+ // (waits no longer than maxWaitTimeMs), then passes it to the dual decoder
+ // for decoding. This will never trigger a render callback. Should be
+ // called frequently, and as long as it returns 1 it should be called again
+ // as soon as possible.
+ //
+ // Return value : 1, if a frame was decoded
+ // 0, if no frame was decoded
+ // < 0, on error.
+ virtual WebRtc_Word32 DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs = 200) = 0;
+
+ // Decodes a frame and sets an appropriate render time in ms relative to the system time.
+ // Should be used in conjunction with VCMFrameStorageCallback.
+ //
+ // Input:
+ // - frameFromStorage : Encoded frame read from file or received through
+ // the VCMFrameStorageCallback callback.
+ //
+ // Return value: : VCM_OK, on success
+ // < 0, on error
+ virtual WebRtc_Word32 DecodeFromStorage(const EncodedVideoData& frameFromStorage) = 0;
+
+ // Reset the decoder state to the initial state.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 ResetDecoder() = 0;
+
+ // API to get the codec which is currently used for decoding by the module.
+ //
+ // Input:
+ // - currentReceiveCodec : Settings for the codec to be registered.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const = 0;
+
+ // API to get the codec type currently used for decoding by the module.
+ //
+ // Return value : codecy type, on success.
+ // kVideoCodecUnknown, on error or if no receive codec is registered
+ virtual VideoCodecType ReceiveCodec() const = 0;
+
+ // Insert a parsed packet into the receiver side of the module. Will be placed in the
+ // jitter buffer waiting for the frame to become complete. Returns as soon as the packet
+ // has been placed in the jitter buffer.
+ //
+ // Input:
+ // - incomingPayload : Payload of the packet.
+ // - payloadLength : Length of the payload.
+ // - rtpInfo : The parsed header.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 IncomingPacket(const WebRtc_UWord8* incomingPayload,
+ WebRtc_UWord32 payloadLength,
+ const WebRtcRTPHeader& rtpInfo) = 0;
+
+ // Sets codec config parameters received out-of-band to the currently
+ // selected receive codec.
+ //
+ // Input:
+ // - payloadType : Payload type which specifies which codec to set these
+ // parameters to.
+ // - buffer : Codec config parameters.
+ // - length : Length of the parameter data.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType,
+ const WebRtc_UWord8* buffer,
+ WebRtc_Word32 length) = 0;
+
+ // Minimum playout delay (Used for lip-sync). This is the minimum delay required
+ // to sync with audio. Not included in VideoCodingModule::Delay()
+ // Defaults to 0 ms.
+ //
+ // Input:
+ // - minPlayoutDelayMs : Additional delay in ms.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs) = 0;
+
+ // Set the time required by the renderer to render a frame.
+ //
+ // Input:
+ // - timeMS : The time in ms required by the renderer to render a frame.
+ //
+ // Return value : VCM_OK, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 SetRenderDelay(WebRtc_UWord32 timeMS) = 0;
+
+ // The total delay desired by the VCM. Can be less than the minimum
+ // delay set with SetMinimumPlayoutDelay.
+ //
+ // Return value : Total delay in ms, on success.
+ // < 0, on error.
+ virtual WebRtc_Word32 Delay() const = 0;
+
+ // Get the received frame counters. Keeps track of the number of each frame type
+ // received since the start of the call.
+ //
+ // Output:
+ // - frameCount : Struct to be filled with the number of frames received.
+ //
+ // Return value : VCM_OK, on success.
+ // <0, on error.
+ virtual WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const = 0;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_
diff --git a/src/modules/video_coding/main/interface/video_coding_defines.h b/src/modules/video_coding/main/interface/video_coding_defines.h
new file mode 100644
index 0000000..921fb90
--- /dev/null
+++ b/src/modules/video_coding/main/interface/video_coding_defines.h
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_
+#define WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_
+
+#include "typedefs.h"
+#include "module_common_types.h"
+
+namespace webrtc
+{
+
+// Error codes
+#define VCM_REQUEST_SLI 2
+#define VCM_MISSING_CALLBACK 1
+#define VCM_OK 0
+#define VCM_GENERAL_ERROR -1
+#define VCM_LEVEL_EXCEEDED -2
+#define VCM_MEMORY -3
+#define VCM_PARAMETER_ERROR -4
+#define VCM_UNKNOWN_PAYLOAD -5
+#define VCM_CODEC_ERROR -6
+#define VCM_UNINITIALIZED -7
+#define VCM_NO_CODEC_REGISTERED -8
+#define VCM_JITTER_BUFFER_ERROR -9
+#define VCM_OLD_PACKET_ERROR -10
+#define VCM_NO_FRAME_DECODED -11
+#define VCM_ERROR_REQUEST_SLI -12
+#define VCM_NOT_IMPLEMENTED -20
+
+#define VCM_H263_PAYLOAD_TYPE 34
+#define VCM_RED_PAYLOAD_TYPE 96
+#define VCM_ULPFEC_PAYLOAD_TYPE 97
+#define VCM_H263_1998_PAYLOAD_TYPE 121
+#define VCM_VP8_PAYLOAD_TYPE 120
+#define VCM_I420_PAYLOAD_TYPE 124
+
+enum VCMNackProperties
+{
+ kNackHistoryLength = 450
+};
+
+enum VCMH263FrameDrop
+{
+ kDecodePFrames,
+ kDropPFrames
+};
+
+enum VCMVideoProtection
+{
+ kProtectionNack, // Both send-side and receive-side
+ kProtectionNackSender, // Send-side only
+ kProtectionNackReceiver, // Receive-side only
+ kProtectionDualDecoder,
+ kProtectionFEC,
+ kProtectionNackFEC,
+ kProtectionKeyOnLoss,
+ kProtectionKeyOnKeyLoss,
+ kProtectionPeriodicKeyFrames
+};
+
+enum VCMTemporalDecimation
+{
+ kBitrateOverUseDecimation,
+};
+
+struct VCMFrameCount
+{
+ WebRtc_UWord32 numKeyFrames;
+ WebRtc_UWord32 numDeltaFrames;
+};
+
+
+// Callback class used for sending data ready to be packetized
+class VCMPacketizationCallback
+{
+public:
+ virtual WebRtc_Word32 SendData(
+ const FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const RTPFragmentationHeader& fragmentationHeader,
+ const RTPVideoTypeHeader* rtpTypeHdr) = 0;
+protected:
+ virtual ~VCMPacketizationCallback() {}
+};
+
+// Callback class used for passing decoded frames which are ready to be rendered.
+class VCMFrameStorageCallback
+{
+public:
+ virtual WebRtc_Word32 StoreReceivedFrame(const EncodedVideoData& frameToStore) = 0;
+
+protected:
+ virtual ~VCMFrameStorageCallback() {}
+};
+
+// Callback class used for passing decoded frames which are ready to be rendered.
+class VCMReceiveCallback
+{
+public:
+ virtual WebRtc_Word32 FrameToRender(VideoFrame& videoFrame) = 0;
+ virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) {return -1;}
+
+protected:
+ virtual ~VCMReceiveCallback() {}
+};
+
+// Callback class used for informing the user of the bit rate and frame rate produced by the
+// encoder.
+class VCMSendStatisticsCallback
+{
+public:
+ virtual WebRtc_Word32 SendStatistics(const WebRtc_UWord32 bitRate,
+ const WebRtc_UWord32 frameRate) = 0;
+
+protected:
+ virtual ~VCMSendStatisticsCallback() {}
+};
+
+// Callback class used for informing the user of the incoming bit rate and frame rate.
+class VCMReceiveStatisticsCallback
+{
+public:
+ virtual WebRtc_Word32 ReceiveStatistics(const WebRtc_UWord32 bitRate,
+ const WebRtc_UWord32 frameRate) = 0;
+
+protected:
+ virtual ~VCMReceiveStatisticsCallback() {}
+};
+
+// Callback class used for telling the user about the requested amount of bit stream protection
+// Key frame FEC rate, delta frame and whether NACK should be on or off.
+class VCMProtectionCallback
+{
+public:
+ virtual WebRtc_Word32 ProtectionRequest(const WebRtc_UWord8 deltaFECRate,
+ const WebRtc_UWord8 keyFECRate,
+ const bool nack) = 0;
+
+protected:
+ virtual ~VCMProtectionCallback() {}
+};
+
+// Callback class used for telling the user about what frame type needed to continue decoding.
+// Typically a key frame when the stream has been corrupted in some way.
+class VCMFrameTypeCallback
+{
+public:
+ virtual WebRtc_Word32 FrameTypeRequest(const FrameType frameType) = 0;
+ virtual WebRtc_Word32 SliceLossIndicationRequest(const WebRtc_UWord64 pictureId) {return -1;}
+
+protected:
+ virtual ~VCMFrameTypeCallback() {}
+};
+
+// Callback class used for telling the user about which packet sequence numbers are currently
+// missing and need to be resent.
+class VCMPacketRequestCallback
+{
+public:
+ virtual WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers,
+ WebRtc_UWord16 length) = 0;
+
+protected:
+ virtual ~VCMPacketRequestCallback() {}
+};
+
+// Callback used to inform the user of the the desired resolution
+// as subscribed by Media Optimization (Quality Modes)
+class VCMQMSettingsCallback
+{
+public:
+ virtual WebRtc_Word32 SetVideoQMSettings(const WebRtc_UWord32 frameRate,
+ const WebRtc_UWord32 width,
+ const WebRtc_UWord32 height) = 0;
+
+protected:
+ virtual ~VCMQMSettingsCallback() {}
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_
diff --git a/src/modules/video_coding/main/source/Android.mk b/src/modules/video_coding/main/source/Android.mk
new file mode 100644
index 0000000..9478957
--- /dev/null
+++ b/src/modules/video_coding/main/source/Android.mk
@@ -0,0 +1,79 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_video_coding
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+LOCAL_SRC_FILES := codec_database.cc \
+ codec_timer.cc \
+ content_metrics_processing.cc \
+ encoded_frame.cc \
+ exp_filter.cc \
+ frame_buffer.cc \
+ frame_dropper.cc \
+ frame_list.cc \
+ generic_decoder.cc \
+ generic_encoder.cc \
+ inter_frame_delay.cc \
+ jitter_buffer.cc \
+ jitter_estimator.cc \
+ media_opt_util.cc \
+ media_optimization.cc \
+ packet.cc \
+ qm_select.cc \
+ receiver.cc \
+ rtt_filter.cc \
+ session_info.cc \
+ timestamp_extrapolator.cc \
+ timestamp_map.cc \
+ timing.cc \
+ video_coding_impl.cc
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS :=
+MY_CFLAGS_C :=
+MY_DEFS := '-DNO_TCMALLOC' \
+ '-DNO_HEAPCHECKER' \
+ '-DWEBRTC_TARGET_PC' \
+ '-DWEBRTC_LINUX' \
+ '-DWEBRTC_THREAD_RR' \
+ '-DWEBRTC_ANDROID' \
+ '-DANDROID'
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \
+ $(LOCAL_PATH)/../interface \
+ $(LOCAL_PATH)/../../../interface \
+ $(LOCAL_PATH)/../../codecs/interface \
+ $(LOCAL_PATH)/../../codecs/i420/main/interface \
+ $(LOCAL_PATH)/../../codecs/vp8/main/interface \
+ $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \
+ $(LOCAL_PATH)/../../../../system_wrappers/interface
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS :=
+
+LOCAL_LDFLAGS :=
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_SHARED_LIBRARIES := libcutils \
+ libdl \
+ libstlport
+LOCAL_ADDITIONAL_DEPENDENCIES :=
+
+include external/stlport/libstlport.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/modules/video_coding/main/source/codec_database.cc b/src/modules/video_coding/main/source/codec_database.cc
new file mode 100644
index 0000000..612b88b
--- /dev/null
+++ b/src/modules/video_coding/main/source/codec_database.cc
@@ -0,0 +1,799 @@
+/*
+ * 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 "codec_database.h"
+#include "../../../../engine_configurations.h"
+#include "internal_defines.h"
+#include "trace.h"
+
+#if defined(_WIN32)
+ // VS 2005: Don't warn for default initialized arrays. See help for more info.
+ // Don't warn for strncpy being unsecure.
+ // switch statement contains 'default' but no 'case' labels
+#pragma warning(disable:4351; disable:4996; disable:4065)
+#endif
+
+// Supported codecs
+#ifdef VIDEOCODEC_VP8
+ #include "vp8.h"
+#endif
+#ifdef VIDEOCODEC_I420
+ #include "i420.h"
+#endif
+
+namespace webrtc
+{
+
+VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings,
+ WebRtc_UWord32 numberOfCores,
+ bool requireKeyFrame)
+:
+_settings(settings),
+_numberOfCores(numberOfCores),
+_requireKeyFrame(requireKeyFrame)
+{
+}
+
+VCMExtDecoderMapItem::VCMExtDecoderMapItem(VideoDecoder* externalDecoderInstance,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming)
+:
+_payloadType(payloadType),
+_externalDecoderInstance(externalDecoderInstance),
+_internalRenderTiming(internalRenderTiming)
+{
+}
+
+VCMCodecDataBase::VCMCodecDataBase(WebRtc_Word32 id):
+_id(id),
+_numberOfCores(0),
+_maxPayloadSize(kDefaultPayloadSize),
+_periodicKeyFrames(false),
+_currentEncIsExternal(false),
+_sendCodec(),
+_receiveCodec(),
+_externalPayloadType(0),
+_externalEncoder(NULL),
+_internalSource(false),
+_ptrEncoder(NULL),
+_ptrDecoder(NULL),
+_currentDecIsExternal(false),
+_decMap(),
+_decExternalMap()
+{
+ //
+}
+
+VCMCodecDataBase::~VCMCodecDataBase()
+{
+ Reset();
+}
+
+WebRtc_Word32
+VCMCodecDataBase::Version(WebRtc_Word8* version,
+ WebRtc_UWord32& remainingBufferInBytes,
+ WebRtc_UWord32& position) const
+{
+ VCMGenericEncoder* encoder = NULL;
+ VideoCodec settings;
+ WebRtc_Word32 ret;
+ for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++)
+ {
+ ret = VCMCodecDataBase::Codec(i, &settings);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ encoder = CreateEncoder(settings.codecType);
+ if (encoder == NULL)
+ {
+ return VCM_MEMORY;
+ }
+ ret = encoder->_encoder.Version(&version[position], remainingBufferInBytes);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ remainingBufferInBytes -= ret;
+ position += ret;
+ delete &encoder->_encoder;
+ delete encoder;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::Reset()
+{
+ WebRtc_Word32 ret = ResetReceiver();
+ if (ret < 0)
+ {
+ return ret;
+ }
+ ret = ResetSender();
+ if (ret < 0)
+ {
+ return ret;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::ResetSender()
+{
+ DeleteEncoder();
+ _periodicKeyFrames = false;
+ return VCM_OK;
+}
+
+VCMGenericEncoder*
+VCMCodecDataBase::CreateEncoder(VideoCodecType type) const
+{
+ switch(type)
+ {
+#ifdef VIDEOCODEC_VP8
+ case kVideoCodecVP8:
+ return new VCMGenericEncoder(*(new VP8Encoder));
+ break;
+#endif
+#ifdef VIDEOCODEC_I420
+ case kVideoCodecI420:
+ return new VCMGenericEncoder(*(new I420Encoder));
+ break;
+#endif
+ default:
+ return NULL;
+ break;
+ }
+}
+
+void
+VCMCodecDataBase::DeleteEncoder()
+{
+ if (_ptrEncoder)
+ {
+ _ptrEncoder->Release();
+ if (!_currentEncIsExternal)
+ {
+ delete &_ptrEncoder->_encoder;
+ }
+ delete _ptrEncoder;
+ _ptrEncoder = NULL;
+ }
+}
+
+WebRtc_UWord8
+VCMCodecDataBase::NumberOfCodecs()
+{
+ return VCM_NUM_VIDEO_CODECS_AVAILABLE;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::Codec(WebRtc_UWord8 listId, VideoCodec *settings)
+{
+ if (settings == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+
+ if (listId >= VCM_NUM_VIDEO_CODECS_AVAILABLE)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ memset(settings, 0, sizeof(VideoCodec));
+ switch (listId)
+ {
+#ifdef VIDEOCODEC_VP8
+ case VCM_VP8_IDX:
+ {
+ strncpy(settings->plName, "VP8", 3);
+ settings->codecType = kVideoCodecVP8;
+ // 96 to 127 dynamic payload types for video codecs
+ settings->plType = VCM_VP8_PAYLOAD_TYPE;
+ settings->startBitrate = 100;
+ settings->minBitrate = VCM_MIN_BITRATE;
+ settings->maxBitrate = 0;
+ settings->maxFramerate = VCM_DEFAULT_FRAME_RATE;
+ settings->width = VCM_DEFAULT_CODEC_WIDTH;
+ settings->height = VCM_DEFAULT_CODEC_HEIGHT;
+ break;
+ }
+#endif
+#ifdef VIDEOCODEC_I420
+ case VCM_I420_IDX:
+ {
+ strncpy(settings->plName, "I420", 4);
+ settings->codecType = kVideoCodecI420;
+ // 96 to 127 dynamic payload types for video codecs
+ settings->plType = VCM_I420_PAYLOAD_TYPE;
+ // Bitrate needed for this size and framerate
+ settings->startBitrate = 3*VCM_DEFAULT_CODEC_WIDTH*
+ VCM_DEFAULT_CODEC_HEIGHT*8*
+ VCM_DEFAULT_FRAME_RATE/1000/2;
+ settings->maxBitrate = settings->startBitrate;
+ settings->maxFramerate = VCM_DEFAULT_FRAME_RATE;
+ settings->width = VCM_DEFAULT_CODEC_WIDTH;
+ settings->height = VCM_DEFAULT_CODEC_HEIGHT;
+ settings->minBitrate = VCM_MIN_BITRATE;
+ break;
+ }
+#endif
+ default:
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ }
+
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::Codec(VideoCodecType codecType, VideoCodec* settings)
+{
+ for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++)
+ {
+ const WebRtc_Word32 ret = VCMCodecDataBase::Codec(i, settings);
+ if (ret != VCM_OK)
+ {
+ return ret;
+ }
+ if (codecType == settings->codecType)
+ {
+ return VCM_OK;
+ }
+ }
+ return VCM_PARAMETER_ERROR;
+}
+
+// assuming only one registered encoder - since only one used, no need for more
+WebRtc_Word32
+VCMCodecDataBase::RegisterSendCodec(const VideoCodec* sendCodec,
+ WebRtc_UWord32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize)
+ {
+ if (sendCodec == NULL)
+ {
+ return VCM_UNINITIALIZED;
+ }
+ if (maxPayloadSize == 0)
+ {
+ maxPayloadSize = kDefaultPayloadSize;
+ }
+ if (numberOfCores > 32)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ if (strcmp(sendCodec->plName, "H263") == 0 &&
+ (sendCodec->plType != 34))
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ if (sendCodec->plType <= 0)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ // Make sure the start bit rate is sane...
+ if (sendCodec->startBitrate > 1000000)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ if (sendCodec->codecType == kVideoCodecUnknown)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ _numberOfCores = numberOfCores;
+ _maxPayloadSize = maxPayloadSize;
+
+ memcpy(&_sendCodec, sendCodec, sizeof(VideoCodec));
+
+ if (_sendCodec.maxBitrate == 0)
+ {
+ // max is one bit per pixel
+ _sendCodec.maxBitrate = ((WebRtc_Word32)_sendCodec.height *
+ (WebRtc_Word32)_sendCodec.width *
+ (WebRtc_Word32)_sendCodec.maxFramerate) / 1000;
+ if (_sendCodec.startBitrate > _sendCodec.maxBitrate)
+ {
+ // but if the customer tries to set a higher start bit rate we will increase
+ // the max accordingly
+ _sendCodec.maxBitrate = _sendCodec.startBitrate;
+ }
+ }
+
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::SendCodec(VideoCodec* currentSendCodec) const
+{
+ WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec");
+
+ if(_ptrEncoder == NULL)
+ {
+ return VCM_UNINITIALIZED;
+ }
+ memcpy(currentSendCodec, &_sendCodec, sizeof(VideoCodec));
+ return VCM_OK;
+}
+
+VideoCodecType
+VCMCodecDataBase::SendCodec() const
+{
+ WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SendCodec type");
+ if (_ptrEncoder == NULL)
+ {
+ return kVideoCodecUnknown;
+ }
+ return _sendCodec.codecType;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::DeRegisterExternalEncoder(WebRtc_UWord8 payloadType, bool& wasSendCodec)
+{
+ wasSendCodec = false;
+ if (_externalPayloadType != payloadType)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ if (_sendCodec.plType == payloadType)
+ {
+ //De-register as send codec if needed
+ DeleteEncoder();
+ memset(&_sendCodec, 0, sizeof(VideoCodec));
+ _currentEncIsExternal = false;
+ wasSendCodec = true;
+ }
+ _externalPayloadType = 0;
+ _externalEncoder = NULL;
+ _internalSource = false;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::RegisterExternalEncoder(VideoEncoder* externalEncoder,
+ WebRtc_UWord8 payloadType,
+ bool internalSource)
+{
+ // since only one encoder can be used at a given time,
+ // only one external encoder can be registered/used
+ _externalEncoder = externalEncoder;
+ _externalPayloadType = payloadType;
+ _internalSource = internalSource;
+
+ return VCM_OK;
+}
+
+VCMGenericEncoder*
+VCMCodecDataBase::SetEncoder(const VideoCodec* settings,
+ VCMEncodedFrameCallback* VCMencodedFrameCallback)
+
+{
+ // if encoder exists, will destroy it and create new one
+ DeleteEncoder();
+
+ if (settings->plType == _externalPayloadType)
+ {
+ // External encoder
+ _ptrEncoder = new VCMGenericEncoder(*_externalEncoder, _internalSource);
+ _currentEncIsExternal = true;
+ }
+ else
+ {
+ _ptrEncoder = CreateEncoder(settings->codecType);
+ _currentEncIsExternal = false;
+ }
+
+ VCMencodedFrameCallback->SetPayloadType(settings->plType);
+
+ if (_ptrEncoder == NULL)
+ {
+ return NULL;
+ }
+
+ if (_ptrEncoder->InitEncode(settings, _numberOfCores, _maxPayloadSize) < 0)
+ {
+ DeleteEncoder();
+ return NULL;
+ }
+ else if (_ptrEncoder->RegisterEncodeCallback(VCMencodedFrameCallback) < 0)
+ {
+ DeleteEncoder();
+ return NULL;
+ }
+ // Intentionally don't check return value since the encoder registration
+ // shouldn't fail because the codec doesn't support changing the
+ // periodic key frame setting.
+ _ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames);
+ return _ptrEncoder;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::SetPeriodicKeyFrames(bool enable)
+{
+ _periodicKeyFrames = enable;
+ if (_ptrEncoder != NULL)
+ {
+ return _ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames);
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::RegisterReceiveCodec(const VideoCodec* receiveCodec,
+ WebRtc_UWord32 numberOfCores,
+ bool requireKeyFrame)
+{
+ WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Codec: %s, Payload type %d, Height %d, Width %d, Bitrate %d, Framerate %d.",
+ receiveCodec->plName, receiveCodec->plType,
+ receiveCodec->height, receiveCodec->width,
+ receiveCodec->startBitrate, receiveCodec->maxFramerate);
+
+ // check if payload value already exists, if so - erase old and insert new
+ DeRegisterReceiveCodec(receiveCodec->plType);
+ if (receiveCodec->codecType == kVideoCodecUnknown)
+ {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ VideoCodec* newReceiveCodec = new VideoCodec(*receiveCodec);
+ _decMap.Insert(receiveCodec->plType,
+ new VCMDecoderMapItem(newReceiveCodec, numberOfCores, requireKeyFrame));
+
+ return VCM_OK;
+}
+
+WebRtc_Word32 VCMCodecDataBase::DeRegisterReceiveCodec(WebRtc_UWord8 payloadType)
+{
+ MapItem* item = _decMap.Find(payloadType);
+ if (item == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ VCMDecoderMapItem* decItem = static_cast<VCMDecoderMapItem*>(item->GetItem());
+ delete decItem->_settings;
+ delete decItem;
+ _decMap.Erase(item);
+ if (_receiveCodec.plType == payloadType)
+ {
+ // This codec is currently in use.
+ memset(&_receiveCodec, 0, sizeof(VideoCodec));
+ _currentDecIsExternal = false;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::ResetReceiver()
+{
+ ReleaseDecoder(_ptrDecoder);
+ _ptrDecoder = NULL;
+ memset(&_receiveCodec, 0, sizeof(VideoCodec));
+ MapItem* item = _decMap.First();
+ while (item != NULL)
+ {
+ VCMDecoderMapItem* decItem = static_cast<VCMDecoderMapItem*>(item->GetItem());
+ if (decItem != NULL)
+ {
+ if (decItem->_settings != NULL)
+ {
+ delete decItem->_settings;
+ }
+ delete decItem;
+ }
+ _decMap.Erase(item);
+ item = _decMap.First();
+ }
+ item = _decExternalMap.First();
+ while (item != NULL)
+ {
+ VCMExtDecoderMapItem* decItem = static_cast<VCMExtDecoderMapItem*>(item->GetItem());
+ if (decItem != NULL)
+ {
+ delete decItem;
+ }
+ _decExternalMap.Erase(item);
+ item = _decExternalMap.First();
+ }
+ _currentDecIsExternal = false;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMCodecDataBase::DeRegisterExternalDecoder(WebRtc_UWord8 payloadType)
+{
+ MapItem* item = _decExternalMap.Find(payloadType);
+ if (item == NULL)
+ {
+ // Not found
+ return VCM_PARAMETER_ERROR;
+ }
+ if (_receiveCodec.plType == payloadType)
+ {
+ // Release it if it was registered and in use
+ ReleaseDecoder(_ptrDecoder);
+ _ptrDecoder = NULL;
+ }
+ DeRegisterReceiveCodec(payloadType);
+ VCMExtDecoderMapItem* decItem = static_cast<VCMExtDecoderMapItem*>(item->GetItem());
+ delete decItem;
+ _decExternalMap.Erase(item);
+ return VCM_OK;
+}
+
+// Add the external encoder object to the list of external decoders.
+// Won't be registered as a receive codec until RegisterReceiveCodec is called.
+WebRtc_Word32
+VCMCodecDataBase::RegisterExternalDecoder(VideoDecoder* externalDecoder,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming)
+{
+ // check if payload value already exists, if so - erase old and insert new
+ VCMExtDecoderMapItem* extDecoder = new VCMExtDecoderMapItem(externalDecoder,
+ payloadType,
+ internalRenderTiming);
+ if (extDecoder == NULL)
+ {
+ return VCM_MEMORY;
+ }
+ DeRegisterExternalDecoder(payloadType);
+ _decExternalMap.Insert(payloadType, extDecoder);
+
+ return VCM_OK;
+}
+
+bool
+VCMCodecDataBase::DecoderRegistered() const
+{
+ return (_decMap.Size() > 0);
+}
+
+WebRtc_Word32
+VCMCodecDataBase::ReceiveCodec(VideoCodec* currentReceiveCodec) const
+{
+ if (_ptrDecoder == NULL)
+ {
+ return VCM_NO_FRAME_DECODED;
+ }
+ memcpy(currentReceiveCodec, &_receiveCodec, sizeof(VideoCodec));
+ return VCM_OK;
+}
+
+VideoCodecType
+VCMCodecDataBase::ReceiveCodec() const
+{
+ if (_ptrDecoder == NULL)
+ {
+ return kVideoCodecUnknown;
+ }
+ return _receiveCodec.codecType;
+}
+
+VCMGenericDecoder*
+VCMCodecDataBase::SetDecoder(WebRtc_UWord8 payloadType, VCMDecodedFrameCallback& callback)
+{
+ if (payloadType == _receiveCodec.plType || payloadType == 0)
+ {
+ return _ptrDecoder;
+ }
+ // check for exisitng decoder, if exists - delete
+ if (_ptrDecoder)
+ {
+ ReleaseDecoder(_ptrDecoder);
+ _ptrDecoder = NULL;
+ memset(&_receiveCodec, 0, sizeof(VideoCodec));
+ }
+ _ptrDecoder = CreateAndInitDecoder(payloadType, _receiveCodec, _currentDecIsExternal);
+ if (_ptrDecoder == NULL)
+ {
+ return NULL;
+ }
+ if (_ptrDecoder->RegisterDecodeCompleteCallback(&callback) < 0)
+ {
+ ReleaseDecoder(_ptrDecoder);
+ _ptrDecoder = NULL;
+ memset(&_receiveCodec, 0, sizeof(VideoCodec));
+ return NULL;
+ }
+ return _ptrDecoder;
+}
+
+VCMGenericDecoder*
+VCMCodecDataBase::CreateAndInitDecoder(WebRtc_UWord8 payloadType,
+ VideoCodec& newCodec,
+ bool &external) const
+{
+ VCMDecoderMapItem* decoderItem = FindDecoderItem(payloadType);
+ if (decoderItem == NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Unknown payload type: %u", payloadType);
+ return NULL;
+ }
+ VCMGenericDecoder* ptrDecoder = NULL;
+ VCMExtDecoderMapItem* externalDecItem = FindExternalDecoderItem(payloadType);
+ if (externalDecItem != NULL)
+ {
+ // External codec
+ ptrDecoder = new VCMGenericDecoder(*externalDecItem->_externalDecoderInstance, _id,
+ true);
+ external = true;
+ }
+ else
+ {
+ // create decoder
+ ptrDecoder = CreateDecoder(decoderItem->_settings->codecType);
+ external = false;
+ }
+ if (ptrDecoder == NULL)
+ {
+ return NULL;
+ }
+
+ if (ptrDecoder->InitDecode(decoderItem->_settings,
+ decoderItem->_numberOfCores,
+ decoderItem->_requireKeyFrame) < 0)
+ {
+ ReleaseDecoder(ptrDecoder);
+ return NULL;
+ }
+
+ SetCodecConfigParameters(*ptrDecoder, *decoderItem->_settings);
+
+ memcpy(&newCodec, decoderItem->_settings, sizeof(VideoCodec));
+ return ptrDecoder;
+}
+
+VCMGenericDecoder*
+VCMCodecDataBase::CreateDecoderCopy() const
+{
+ if (_ptrDecoder == NULL)
+ {
+ return NULL;
+ }
+ VideoDecoder* decoderCopy = _ptrDecoder->_decoder.Copy();
+ if (decoderCopy == NULL)
+ {
+ return NULL;
+ }
+ return new VCMGenericDecoder(*decoderCopy, _id, _ptrDecoder->External());
+}
+
+void
+VCMCodecDataBase::CopyDecoder(const VCMGenericDecoder& decoder)
+{
+ VideoDecoder* decoderCopy = decoder._decoder.Copy();
+ if (decoderCopy != NULL)
+ {
+ ReleaseDecoder(_ptrDecoder);
+ _ptrDecoder = new VCMGenericDecoder(*decoderCopy, _id, decoder.External());
+ }
+}
+
+bool
+VCMCodecDataBase::RenderTiming() const
+{
+ bool renderTiming = true;
+ if (_currentDecIsExternal)
+ {
+ VCMExtDecoderMapItem* extItem = FindExternalDecoderItem(_receiveCodec.plType);
+ renderTiming = extItem->_internalRenderTiming;
+ }
+ return renderTiming;
+}
+
+void
+VCMCodecDataBase::ReleaseDecoder(VCMGenericDecoder* decoder) const
+{
+ if (decoder != NULL)
+ {
+ decoder->Release();
+ if (!decoder->External() && &decoder->_decoder != NULL)
+ {
+ delete &decoder->_decoder;
+ }
+ delete decoder;
+ }
+}
+
+WebRtc_Word32
+VCMCodecDataBase::SetCodecConfigParameters(WebRtc_UWord8 payloadType,
+ const WebRtc_UWord8* buffer,
+ WebRtc_Word32 length)
+{
+ VCMDecoderMapItem* decItem = FindDecoderItem(payloadType);
+ if (decItem == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ switch (decItem->_settings->codecType)
+ {
+ case kVideoCodecMPEG4:
+ {
+ memcpy(decItem->_settings->codecSpecific.MPEG4.configParameters, buffer, length);
+ decItem->_settings->codecSpecific.MPEG4.configParametersSize =
+ static_cast<WebRtc_UWord8>(length);
+ break;
+ }
+ default:
+ // This codec doesn't have codec config parameters
+ return VCM_GENERAL_ERROR;
+ }
+ if (_ptrDecoder != NULL && _receiveCodec.plType == decItem->_settings->plType)
+ {
+ return _ptrDecoder->SetCodecConfigParameters(buffer, length);
+ }
+ return VCM_OK;
+}
+
+VCMDecoderMapItem*
+VCMCodecDataBase::FindDecoderItem(WebRtc_UWord8 payloadType) const
+{
+ MapItem* item = _decMap.Find(payloadType);
+ if (item != NULL)
+ {
+ return static_cast<VCMDecoderMapItem*>(item->GetItem());
+ }
+ return NULL;
+}
+
+VCMExtDecoderMapItem*
+VCMCodecDataBase::FindExternalDecoderItem(WebRtc_UWord8 payloadType) const
+{
+ MapItem* item = _decExternalMap.Find(payloadType);
+ if (item != NULL)
+ {
+ return static_cast<VCMExtDecoderMapItem*>(item->GetItem());
+ }
+ return NULL;
+}
+
+VCMGenericDecoder*
+VCMCodecDataBase::CreateDecoder(VideoCodecType type) const
+{
+ switch(type)
+ {
+#ifdef VIDEOCODEC_VP8
+ case kVideoCodecVP8:
+ return new VCMGenericDecoder(*(new VP8Decoder), _id);
+#endif
+#ifdef VIDEOCODEC_I420
+ case kVideoCodecI420:
+ return new VCMGenericDecoder(*(new I420Decoder), _id);
+#endif
+ default:
+ return NULL;
+ }
+}
+
+void
+VCMCodecDataBase::SetCodecConfigParameters(VCMGenericDecoder& decoder,
+ const VideoCodec& settings)
+{
+ switch (settings.codecType)
+ {
+ case kVideoCodecMPEG4:
+ {
+ if (settings.codecSpecific.MPEG4.configParametersSize > 0)
+ {
+ decoder.SetCodecConfigParameters(
+ settings.codecSpecific.MPEG4.configParameters,
+ settings.codecSpecific.MPEG4.configParametersSize);
+ }
+ break;
+ }
+ default:
+ // No codec config parameters for this codec
+ return;
+ }
+ return;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/codec_database.h b/src/modules/video_coding/main/source/codec_database.h
new file mode 100644
index 0000000..37943e8
--- /dev/null
+++ b/src/modules/video_coding/main/source/codec_database.h
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_
+
+#include "video_coding.h"
+#include "video_codec_interface.h"
+#include "generic_decoder.h"
+#include "generic_encoder.h"
+#include "typedefs.h"
+#include "map_wrapper.h"
+
+namespace webrtc
+{
+
+enum VCMCodecDBProperties
+{
+ kDefaultPayloadSize = 1440
+};
+
+class VCMDecoderMapItem {
+public:
+ VCMDecoderMapItem(VideoCodec* settings,
+ WebRtc_UWord32 numberOfCores,
+ bool requireKeyFrame);
+
+ VideoCodec* _settings;
+ WebRtc_UWord32 _numberOfCores;
+ bool _requireKeyFrame;
+};
+
+class VCMExtDecoderMapItem {
+public:
+ VCMExtDecoderMapItem(VideoDecoder* externalDecoderInstance,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming);
+
+ WebRtc_UWord8 _payloadType;
+ VideoDecoder* _externalDecoderInstance;
+ bool _internalRenderTiming;
+};
+
+/*******************************/
+/* VCMCodecDataBase class */
+/*******************************/
+class VCMCodecDataBase
+{
+public:
+ VCMCodecDataBase(WebRtc_Word32 id);
+ ~VCMCodecDataBase();
+ /**
+ * Fills "version" with the version of all codecs supported.
+ */
+ WebRtc_Word32 Version(WebRtc_Word8* version,
+ WebRtc_UWord32& remainingBufferInBytes,
+ WebRtc_UWord32& position) const;
+ /**
+ * Release codecdatabase - release all memory for both send and receive side
+ */
+ WebRtc_Word32 Reset();
+ /**
+ * Sender Side
+ */
+ /**
+ * Returns the number of supported codecs (or -1 in case of error).
+ */
+ static WebRtc_UWord8 NumberOfCodecs();
+ /**
+ * Get supported codecs with ID
+ * Input Values:
+ * listnr : Requested codec id number
+ * codec_inst: Pointer to the struct in which the returned codec information is copied
+ * Return Values: 0 if successful, otherwise
+ */
+ static WebRtc_Word32 Codec(WebRtc_UWord8 listId, VideoCodec* settings);
+ static WebRtc_Word32 Codec(VideoCodecType codecType, VideoCodec* settings);
+ /**
+ * Reset Sender side
+ */
+ WebRtc_Word32 ResetSender();
+ /**
+ * Setting the sender side codec and initiaiting the desired codec given the VideoCodec
+ * struct.
+ * Return Value: 0 if the codec and the settings are supported, otherwise
+ */
+ WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec,
+ WebRtc_UWord32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize);
+ /**
+ * Get current send side codec. Relevant for internal codecs only.
+ */
+ WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const;
+ /**
+ * Get current send side codec type. Relevant for internal codecs only.
+ */
+ VideoCodecType SendCodec() const;
+ /**
+ * Register external encoder - current assumption - if one is registered then it will also
+ * be used, and therefore it is also initialized
+ * Return value: A pointer to the encoder on success, or null, in case of an error.
+ */
+ WebRtc_Word32 DeRegisterExternalEncoder(WebRtc_UWord8 payloadType, bool& wasSendCodec);
+ WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder,
+ WebRtc_UWord8 payloadType,
+ bool internalSource);
+ /**
+ * Returns a encoder given a payloadname - to be used with internal encoders only.
+ * Special cases:
+ * Encoder exists - If payload matches, returns existing one, otherwise,
+ * deletes existing one and creates new one.
+ * No match found / Error - returns NULL.
+ */
+ VCMGenericEncoder* SetEncoder(const VideoCodec* settings,
+ VCMEncodedFrameCallback* VCMencodedFrameCallback);
+
+ WebRtc_Word32 SetPeriodicKeyFrames(bool enable);
+
+ bool InternalSource() const;
+
+ /*
+ * Receiver Side
+ */
+ WebRtc_Word32 ResetReceiver();
+ /**
+ * Register external decoder/render object
+ */
+ WebRtc_Word32 DeRegisterExternalDecoder(WebRtc_UWord8 payloadType);
+ WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming);
+
+ bool DecoderRegistered() const;
+ /**
+ * Register recieve codec
+ */
+ WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec,
+ WebRtc_UWord32 numberOfCores,
+ bool requireKeyFrame);
+ WebRtc_Word32 DeRegisterReceiveCodec(WebRtc_UWord8 payloadType);
+ /**
+ * Get current receive side codec. Relevant for internal codecs only.
+ */
+ WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const;
+ /**
+ * Get current receive side codec type. Relevant for internal codecs only.
+ */
+ VideoCodecType ReceiveCodec() const;
+ /**
+ * Returns a decoder given which matches a payload type.
+ * Special cases:
+ * Decoder exists - If payload matches, returns existing one, otherwise, deletes
+ * existing one, and creates new one.
+ * No match found / Error - returns NULL.
+ */
+ VCMGenericDecoder* SetDecoder(WebRtc_UWord8 payloadType, VCMDecodedFrameCallback& callback);
+
+ VCMGenericDecoder* CreateAndInitDecoder(WebRtc_UWord8 payloadType,
+ VideoCodec& newCodec,
+ bool &external) const;
+
+ VCMGenericDecoder* CreateDecoderCopy() const;
+
+ void ReleaseDecoder(VCMGenericDecoder* decoder) const;
+
+ void CopyDecoder(const VCMGenericDecoder& decoder);
+
+ bool RenderTiming() const;
+
+ WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType,
+ const WebRtc_UWord8* buffer,
+ WebRtc_Word32 length);
+
+protected:
+ /**
+ * Create an internal Encoder given a codec type
+ */
+ VCMGenericEncoder* CreateEncoder(VideoCodecType type) const;
+
+ void DeleteEncoder();
+ /*
+ * Create an internal Decoder given a codec type
+ */
+ VCMGenericDecoder* CreateDecoder(VideoCodecType type) const;
+
+ static void SetCodecConfigParameters(VCMGenericDecoder& decoder,
+ const VideoCodec& settings);
+
+ VCMDecoderMapItem* FindDecoderItem(WebRtc_UWord8 payloadType) const;
+
+ VCMExtDecoderMapItem* FindExternalDecoderItem(WebRtc_UWord8 payloadType) const;
+
+private:
+ WebRtc_Word32 _id;
+ WebRtc_UWord32 _numberOfCores;
+ WebRtc_UWord32 _maxPayloadSize;
+ bool _periodicKeyFrames;
+ bool _currentEncIsExternal;
+ VideoCodec _sendCodec;
+ VideoCodec _receiveCodec;
+ WebRtc_UWord8 _externalPayloadType;
+ VideoEncoder* _externalEncoder;
+ bool _internalSource;
+ VCMGenericEncoder* _ptrEncoder;
+ VCMGenericDecoder* _ptrDecoder;
+ bool _currentDecIsExternal;
+ MapWrapper _decMap;
+ MapWrapper _decExternalMap;
+
+}; // end of VCMCodecDataBase class definition
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_
diff --git a/src/modules/video_coding/main/source/codec_timer.cc b/src/modules/video_coding/main/source/codec_timer.cc
new file mode 100644
index 0000000..1d112fa
--- /dev/null
+++ b/src/modules/video_coding/main/source/codec_timer.cc
@@ -0,0 +1,133 @@
+/*
+ * 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 "codec_timer.h"
+
+#include <assert.h>
+
+namespace webrtc
+{
+
+VCMCodecTimer::VCMCodecTimer()
+:
+_filteredMax(0),
+_firstDecodeTime(true),
+_shortMax(0),
+_history()
+{
+ Reset();
+}
+
+WebRtc_Word32 VCMCodecTimer::StopTimer(WebRtc_Word64 startTimeMs, WebRtc_Word64 nowMs)
+{
+ const WebRtc_Word32 timeDiff = static_cast<WebRtc_Word32>(nowMs - startTimeMs);
+ MaxFilter(timeDiff, nowMs);
+ return timeDiff;
+}
+
+void VCMCodecTimer::Reset()
+{
+ _filteredMax = 0;
+ _firstDecodeTime = true;
+ _shortMax = 0;
+ for (int i=0; i < MAX_HISTORY_SIZE; i++)
+ {
+ _history[i].shortMax = 0;
+ _history[i].timeMs = -1;
+ }
+}
+
+// Update the max-value filter
+void VCMCodecTimer::MaxFilter(WebRtc_Word32 decodeTime, WebRtc_Word64 nowMs)
+{
+ if (!_firstDecodeTime)
+ {
+ UpdateMaxHistory(decodeTime, nowMs);
+ ProcessHistory(nowMs);
+ }
+ else
+ {
+ _firstDecodeTime = false;
+ }
+}
+
+void
+VCMCodecTimer::UpdateMaxHistory(WebRtc_Word32 decodeTime, WebRtc_Word64 now)
+{
+ if (_history[0].timeMs >= 0 &&
+ now - _history[0].timeMs < SHORT_FILTER_MS)
+ {
+ if (decodeTime > _shortMax)
+ {
+ _shortMax = decodeTime;
+ }
+ }
+ else
+ {
+ // Only add a new value to the history once a second
+ if(_history[0].timeMs == -1)
+ {
+ // First, no shift
+ _shortMax = decodeTime;
+ }
+ else
+ {
+ // Shift
+ for(int i = (MAX_HISTORY_SIZE - 2); i >= 0 ; i--)
+ {
+ _history[i+1].shortMax = _history[i].shortMax;
+ _history[i+1].timeMs = _history[i].timeMs;
+ }
+ }
+ if (_shortMax == 0)
+ {
+ _shortMax = decodeTime;
+ }
+
+ _history[0].shortMax = _shortMax;
+ _history[0].timeMs = now;
+ _shortMax = 0;
+ }
+}
+
+void
+VCMCodecTimer::ProcessHistory(WebRtc_Word64 nowMs)
+{
+ _filteredMax = _shortMax;
+ if (_history[0].timeMs == -1)
+ {
+ return;
+ }
+ for (int i=0; i < MAX_HISTORY_SIZE; i++)
+ {
+ if (_history[i].timeMs == -1)
+ {
+ break;
+ }
+ if (nowMs - _history[i].timeMs > MAX_HISTORY_SIZE * SHORT_FILTER_MS)
+ {
+ // This sample (and all samples after this) is too old
+ break;
+ }
+ if (_history[i].shortMax > _filteredMax)
+ {
+ // This sample is the largest one this far into the history
+ _filteredMax = _history[i].shortMax;
+ }
+ }
+}
+
+// Get the maximum observed time within a time window
+WebRtc_Word32 VCMCodecTimer::RequiredDecodeTimeMs(FrameType /*frameType*/) const
+{
+ return _filteredMax;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/codec_timer.h b/src/modules/video_coding/main/source/codec_timer.h
new file mode 100644
index 0000000..e03c5bf
--- /dev/null
+++ b/src/modules/video_coding/main/source/codec_timer.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_
+
+#include "typedefs.h"
+#include "module_common_types.h"
+
+namespace webrtc
+{
+
+// MAX_HISTORY_SIZE * SHORT_FILTER_MS defines the window size in milliseconds
+#define MAX_HISTORY_SIZE 20
+#define SHORT_FILTER_MS 1000
+
+class VCMShortMaxSample
+{
+public:
+ VCMShortMaxSample() : shortMax(0), timeMs(-1) {};
+
+ WebRtc_Word32 shortMax;
+ WebRtc_Word64 timeMs;
+};
+
+class VCMCodecTimer
+{
+public:
+ VCMCodecTimer();
+
+ // Updates and returns the max filtered decode time.
+ WebRtc_Word32 StopTimer(WebRtc_Word64 startTimeMs, WebRtc_Word64 nowMs);
+
+ // Empty the list of timers.
+ void Reset();
+
+ // Get the required decode time in ms.
+ WebRtc_Word32 RequiredDecodeTimeMs(FrameType frameType) const;
+
+private:
+ void UpdateMaxHistory(WebRtc_Word32 decodeTime, WebRtc_Word64 now);
+ void MaxFilter(WebRtc_Word32 newTime, WebRtc_Word64 nowMs);
+ void ProcessHistory(WebRtc_Word64 nowMs);
+
+ WebRtc_Word32 _filteredMax;
+ bool _firstDecodeTime;
+ WebRtc_Word32 _shortMax;
+ VCMShortMaxSample _history[MAX_HISTORY_SIZE];
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_
diff --git a/src/modules/video_coding/main/source/content_metrics_processing.cc b/src/modules/video_coding/main/source/content_metrics_processing.cc
new file mode 100644
index 0000000..5206da0
--- /dev/null
+++ b/src/modules/video_coding/main/source/content_metrics_processing.cc
@@ -0,0 +1,213 @@
+/*
+ * 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 "content_metrics_processing.h"
+#include "tick_time.h"
+#include "module_common_types.h"
+#include "video_coding_defines.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+//////////////////////////////////
+/// VCMContentMetricsProcessing //
+//////////////////////////////////
+
+VCMContentMetricsProcessing::VCMContentMetricsProcessing():
+_frameRate(0),
+_recAvgFactor(1 / 150.0f), // matched to 30fps
+_frameCntRecursiveAvg(0),
+_frameCntUniformAvg(0),
+_avgMotionLevel(0.0f),
+_avgSpatialLevel(0.0f)
+{
+ _recursiveAvg = new VideoContentMetrics();
+ _uniformAvg = new VideoContentMetrics();
+}
+
+VCMContentMetricsProcessing::~VCMContentMetricsProcessing()
+{
+ delete _recursiveAvg;
+ delete _uniformAvg;
+}
+
+WebRtc_Word32
+VCMContentMetricsProcessing::Reset()
+{
+ _recursiveAvg->Reset();
+ _uniformAvg->Reset();
+ _frameRate = 0;
+ _frameCntRecursiveAvg = 0;
+ _frameCntUniformAvg = 0;
+ _avgMotionLevel = 0.0f;
+ _avgSpatialLevel = 0.0f;
+ return VCM_OK;
+}
+
+void
+VCMContentMetricsProcessing::UpdateFrameRate(WebRtc_UWord32 frameRate)
+{
+ _frameRate = frameRate;
+ // Update factor for recursive averaging.
+ _recAvgFactor = (float) 1000.0f / ((float)(_frameRate * kQmMinIntervalMs));
+
+}
+
+VideoContentMetrics*
+VCMContentMetricsProcessing::LongTermAvgData()
+{
+ if (_frameCntRecursiveAvg == 0)
+ {
+ return NULL;
+ }
+ return _recursiveAvg;
+}
+
+VideoContentMetrics*
+VCMContentMetricsProcessing::ShortTermAvgData()
+{
+ if (_frameCntUniformAvg == 0)
+ {
+ return NULL;
+ }
+
+ // Two metrics are used: motion and spatial level.
+ _uniformAvg->motionMagnitudeNZ = _avgMotionLevel /
+ (float)(_frameCntUniformAvg);
+ _uniformAvg->spatialPredErr = _avgSpatialLevel /
+ (float)(_frameCntUniformAvg);
+
+ return _uniformAvg;
+}
+
+void
+VCMContentMetricsProcessing::ResetShortTermAvgData()
+{
+ // Reset
+ _avgMotionLevel = 0.0f;
+ _avgSpatialLevel = 0.0f;
+ _frameCntUniformAvg = 0;
+}
+
+WebRtc_Word32
+VCMContentMetricsProcessing::UpdateContentData(const VideoContentMetrics *contentMetrics)
+{
+ if (contentMetrics == NULL)
+ {
+ return VCM_OK;
+ }
+ return ProcessContent(contentMetrics);
+
+}
+
+WebRtc_UWord32
+VCMContentMetricsProcessing::ProcessContent(const VideoContentMetrics *contentMetrics)
+{
+ // Update the recursive averaged metrics
+ // average is over longer window of time: over QmMinIntervalMs ms.
+ UpdateRecursiveAvg(contentMetrics);
+
+ // Update the uniform averaged metrics:
+ // average is over shorter window of time: based on ~RTCP reports.
+ UpdateUniformAvg(contentMetrics);
+
+ return VCM_OK;
+}
+
+void
+VCMContentMetricsProcessing::UpdateUniformAvg(const VideoContentMetrics *contentMetrics)
+{
+
+ // Update frame counter
+ _frameCntUniformAvg += 1;
+
+ // Update averaged metrics: motion and spatial level are used.
+ _avgMotionLevel += contentMetrics->motionMagnitudeNZ;
+ _avgSpatialLevel += contentMetrics->spatialPredErr;
+
+ return;
+
+}
+void
+VCMContentMetricsProcessing::UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics)
+{
+
+ // Threshold for size of zero motion cluster:
+ // Use for updating 3 motion vector derived metrics:
+ // motion magnitude, cluster distortion, and horizontalness.
+ float nonZeroMvThr = 0.1f;
+
+ float tmpRecAvgFactor = _recAvgFactor;
+
+ // Take value as is for first frame (no motion search in frame zero).
+ if (_frameCntRecursiveAvg < 1)
+ {
+ tmpRecAvgFactor = 1;
+ }
+
+ _recursiveAvg->motionPredErr = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->motionPredErr +
+ tmpRecAvgFactor * contentMetrics->motionPredErr;
+
+ _recursiveAvg->sizeZeroMotion = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->sizeZeroMotion +
+ tmpRecAvgFactor * contentMetrics->sizeZeroMotion;
+
+ _recursiveAvg->spatialPredErr = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->spatialPredErr +
+ tmpRecAvgFactor * contentMetrics->spatialPredErr;
+
+ _recursiveAvg->spatialPredErrH = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->spatialPredErrH +
+ tmpRecAvgFactor * contentMetrics->spatialPredErrH;
+
+ _recursiveAvg->spatialPredErrV = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->spatialPredErrV +
+ tmpRecAvgFactor * contentMetrics->spatialPredErrV;
+
+ // motionMag metric is derived from NFD (normalized frame difference).
+ if (kNfdMetric == 1)
+ {
+ _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->motionMagnitudeNZ +
+ tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ;
+ }
+
+ if (contentMetrics->sizeZeroMotion > nonZeroMvThr)
+ {
+ _recursiveAvg->motionClusterDistortion = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->motionClusterDistortion +
+ tmpRecAvgFactor *contentMetrics->motionClusterDistortion;
+
+ _recursiveAvg->motionHorizontalness = (1 - _recAvgFactor) *
+ _recursiveAvg->motionHorizontalness +
+ tmpRecAvgFactor * contentMetrics->motionHorizontalness;
+
+ // motionMag metric is derived from motion vectors.
+ if (kNfdMetric == 0)
+ {
+ _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) *
+ _recursiveAvg->motionMagnitudeNZ +
+ tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ;
+ }
+ }
+
+ // Update native values:
+ // TODO (marpan): we don't need to update this every frame.
+ _recursiveAvg->nativeHeight = contentMetrics->nativeHeight;
+ _recursiveAvg->nativeWidth = contentMetrics->nativeWidth;
+ _recursiveAvg->nativeFrameRate = contentMetrics->nativeFrameRate;
+
+ _frameCntRecursiveAvg++;
+
+ return;
+}
+} //end of namespace
diff --git a/src/modules/video_coding/main/source/content_metrics_processing.h b/src/modules/video_coding/main/source/content_metrics_processing.h
new file mode 100644
index 0000000..155c4ad
--- /dev/null
+++ b/src/modules/video_coding/main/source/content_metrics_processing.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+struct VideoContentMetrics;
+
+// QM interval time (in ms)
+enum { kQmMinIntervalMs = 10000 };
+
+// Flag for NFD metric vs motion metric
+enum { kNfdMetric = 1 };
+
+/**********************************/
+/* Content Metrics Processing */
+/**********************************/
+class VCMContentMetricsProcessing
+{
+public:
+ VCMContentMetricsProcessing();
+ ~VCMContentMetricsProcessing();
+
+ // Update class with latest metrics
+ WebRtc_Word32 UpdateContentData(const VideoContentMetrics *contentMetrics);
+
+ // Reset the short-term averaged content data
+ void ResetShortTermAvgData();
+
+ // Initialize to
+ WebRtc_Word32 Reset();
+
+ // Inform class of current frame rate
+ void UpdateFrameRate(WebRtc_UWord32 frameRate);
+
+ // Returns the long-term averaged content data:
+ // recursive average over longer time scale
+ VideoContentMetrics* LongTermAvgData();
+
+ // Returns the short-term averaged content data:
+ // uniform average over shorter time scale
+ VideoContentMetrics* ShortTermAvgData();
+private:
+
+ // Compute working avg
+ WebRtc_UWord32 ProcessContent(const VideoContentMetrics *contentMetrics);
+
+ // Update the recursive averaged metrics: longer time average (~5/10 secs).
+ void UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics);
+
+ // Update the uniform averaged metrics: shorter time average (~RTCP reports).
+ void UpdateUniformAvg(const VideoContentMetrics *contentMetrics);
+
+ VideoContentMetrics* _recursiveAvg;
+ VideoContentMetrics* _uniformAvg;
+ WebRtc_UWord32 _frameRate;
+ float _recAvgFactor;
+ WebRtc_UWord32 _frameCntRecursiveAvg;
+ WebRtc_UWord32 _frameCntUniformAvg;
+ float _avgMotionLevel;
+ float _avgSpatialLevel;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_
diff --git a/src/modules/video_coding/main/source/encoded_frame.cc b/src/modules/video_coding/main/source/encoded_frame.cc
new file mode 100644
index 0000000..4515ca0
--- /dev/null
+++ b/src/modules/video_coding/main/source/encoded_frame.cc
@@ -0,0 +1,201 @@
+/*
+ * 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 "encoded_frame.h"
+#include "generic_encoder.h"
+#include "jitter_buffer_common.h"
+#include "video_coding_defines.h"
+
+namespace webrtc {
+
+VCMEncodedFrame::VCMEncodedFrame()
+:
+webrtc::EncodedImage(),
+_renderTimeMs(-1),
+_payloadType(0),
+_missingFrame(false),
+_codecSpecificInfo(NULL),
+_codecSpecificInfoLength(0),
+_codec(kVideoCodecUnknown)
+{
+}
+
+VCMEncodedFrame::VCMEncodedFrame(const webrtc::EncodedImage& rhs)
+:
+webrtc::EncodedImage(rhs),
+_renderTimeMs(-1),
+_payloadType(0),
+_missingFrame(false),
+_codecSpecificInfo(NULL),
+_codecSpecificInfoLength(0),
+_codec(kVideoCodecUnknown)
+{
+ _buffer = NULL;
+ _size = NULL;
+ _length = NULL;
+ if (rhs._buffer != NULL)
+ {
+ VerifyAndAllocate(rhs._length);
+ memcpy(_buffer, rhs._buffer, rhs._length);
+ }
+}
+
+VCMEncodedFrame::VCMEncodedFrame(const VCMEncodedFrame& rhs)
+:
+webrtc::EncodedImage(rhs),
+_renderTimeMs(rhs._renderTimeMs),
+_payloadType(rhs._payloadType),
+_missingFrame(rhs._missingFrame),
+_codecSpecificInfo(NULL),
+_codecSpecificInfoLength(0),
+_codec(rhs._codec)
+{
+ _buffer = NULL;
+ _size = NULL;
+ _length = NULL;
+ if (rhs._buffer != NULL)
+ {
+ VerifyAndAllocate(rhs._size);
+ memcpy(_buffer, rhs._buffer, rhs._length);
+ }
+}
+
+VCMEncodedFrame::~VCMEncodedFrame()
+{
+ Free();
+}
+
+void VCMEncodedFrame::Free()
+{
+ Reset();
+ if (_buffer != NULL)
+ {
+ delete [] _buffer;
+ _buffer = NULL;
+ }
+}
+
+void VCMEncodedFrame::Reset()
+{
+ _renderTimeMs = -1;
+ _timeStamp = 0;
+ _payloadType = 0;
+ _codecSpecificInfo = NULL;
+ _codecSpecificInfoLength = 0;
+ _frameType = kDeltaFrame;
+ _encodedWidth = 0;
+ _encodedHeight = 0;
+ _completeFrame = false;
+ _missingFrame = false;
+ _length = 0;
+ _codec = kVideoCodecUnknown;
+}
+
+WebRtc_Word32
+VCMEncodedFrame::Store(VCMFrameStorageCallback& storeCallback) const
+{
+ EncodedVideoData frameToStore;
+ frameToStore.codec = _codec;
+ if (_buffer != NULL)
+ {
+ frameToStore.VerifyAndAllocate(_length);
+ memcpy(frameToStore.payloadData, _buffer, _length);
+ frameToStore.payloadSize = _length;
+ }
+ frameToStore.completeFrame = _completeFrame;
+ frameToStore.encodedWidth = _encodedWidth;
+ frameToStore.encodedHeight = _encodedHeight;
+ frameToStore.frameType = ConvertFrameType(_frameType);
+ frameToStore.missingFrame = _missingFrame;
+ frameToStore.payloadType = _payloadType;
+ frameToStore.renderTimeMs = _renderTimeMs;
+ frameToStore.timeStamp = _timeStamp;
+ storeCallback.StoreReceivedFrame(frameToStore);
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMEncodedFrame::VerifyAndAllocate(const WebRtc_UWord32 minimumSize)
+{
+ if(minimumSize > _size)
+ {
+ // create buffer of sufficient size
+ WebRtc_UWord8* newBuffer = new WebRtc_UWord8[minimumSize];
+ if (newBuffer == NULL)
+ {
+ return -1;
+ }
+ if(_buffer)
+ {
+ // copy old data
+ memcpy(newBuffer, _buffer, _size);
+ delete [] _buffer;
+ }
+ _buffer = newBuffer;
+ _size = minimumSize;
+ }
+ return 0;
+}
+
+webrtc::FrameType VCMEncodedFrame::ConvertFrameType(VideoFrameType frameType)
+{
+ switch(frameType)
+ {
+ case kKeyFrame:
+ {
+ return kVideoFrameKey;
+ }
+ case kDeltaFrame:
+ {
+ return kVideoFrameDelta;
+ }
+ case kGoldenFrame:
+ {
+ return kVideoFrameGolden;
+ }
+ case kAltRefFrame:
+ {
+ return kVideoFrameAltRef;
+ }
+ default:
+ {
+ return kVideoFrameDelta;
+ }
+ }
+}
+
+VideoFrameType VCMEncodedFrame::ConvertFrameType(webrtc::FrameType frameType)
+{
+ switch (frameType)
+ {
+ case kVideoFrameKey:
+ {
+ return kKeyFrame;
+ }
+ case kVideoFrameDelta:
+ {
+ return kDeltaFrame;
+ }
+ case kVideoFrameGolden:
+ {
+ return kGoldenFrame;
+ }
+ case kVideoFrameAltRef:
+ {
+ return kAltRefFrame;
+ }
+ default:
+ {
+ return kDeltaFrame;
+ }
+ }
+}
+
+}
diff --git a/src/modules/video_coding/main/source/encoded_frame.h b/src/modules/video_coding/main/source/encoded_frame.h
new file mode 100644
index 0000000..89cbd5e
--- /dev/null
+++ b/src/modules/video_coding/main/source/encoded_frame.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_
+#define WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_
+
+#include "module_common_types.h"
+#include "common_types.h"
+#include "video_coding_defines.h"
+#include "video_image.h"
+
+namespace webrtc
+{
+
+class VCMEncodedFrame : protected EncodedImage
+{
+public:
+ VCMEncodedFrame();
+ VCMEncodedFrame(const webrtc::EncodedImage& rhs);
+ VCMEncodedFrame(const VCMEncodedFrame& rhs);
+
+ ~VCMEncodedFrame();
+ /**
+ * Delete VideoFrame and resets members to zero
+ */
+ void Free();
+ /**
+ * Set render time in milliseconds
+ */
+ void SetRenderTime(const WebRtc_Word64 renderTimeMs) {_renderTimeMs = renderTimeMs;}
+
+ /**
+ * Set the encoded frame size
+ */
+ void SetEncodedSize(WebRtc_UWord32 width, WebRtc_UWord32 height)
+ { _encodedWidth = width; _encodedHeight = height; }
+ /**
+ * Get the encoded image
+ */
+ const webrtc::EncodedImage& EncodedImage() const
+ { return static_cast<const webrtc::EncodedImage&>(*this); }
+ /**
+ * Get pointer to frame buffer
+ */
+ const WebRtc_UWord8* Buffer() const {return _buffer;}
+ /**
+ * Get frame length
+ */
+ WebRtc_UWord32 Length() const {return _length;}
+ /**
+ * Get frame timestamp (90kHz)
+ */
+ WebRtc_UWord32 TimeStamp() const {return _timeStamp;}
+ /**
+ * Get render time in milliseconds
+ */
+ WebRtc_Word64 RenderTimeMs() const {return _renderTimeMs;}
+ /**
+ * Get frame type
+ */
+ webrtc::FrameType FrameType() const {return ConvertFrameType(_frameType);}
+ /**
+ * True if this frame is complete, false otherwise
+ */
+ bool Complete() const { return _completeFrame; }
+ /**
+ * True if there's a frame missing before this frame
+ */
+ bool MissingFrame() const { return _missingFrame; }
+ /**
+ * Payload type of the encoded payload
+ */
+ WebRtc_UWord8 PayloadType() const { return _payloadType; }
+ /**
+ * Get codec specific info
+ */
+ const void* CodecSpecificInfo() const {return _codecSpecificInfo;}
+
+ WebRtc_Word32 Store(VCMFrameStorageCallback& storeCallback) const;
+
+ static webrtc::FrameType ConvertFrameType(VideoFrameType frameType);
+ static VideoFrameType ConvertFrameType(webrtc::FrameType frameType);
+
+protected:
+ /**
+ * Verifies that current allocated buffer size is larger than or equal to the input size.
+ * If the current buffer size is smaller, a new allocation is made and the old buffer data
+ * is copied to the new buffer.
+ * Buffer size is updated to minimumSize.
+ */
+ WebRtc_Word32 VerifyAndAllocate(const WebRtc_UWord32 minimumSize);
+
+ void Reset();
+
+ WebRtc_Word64 _renderTimeMs;
+ WebRtc_UWord8 _payloadType;
+ bool _missingFrame;
+ void* _codecSpecificInfo;
+ WebRtc_UWord32 _codecSpecificInfoLength;
+ webrtc::VideoCodecType _codec;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_
diff --git a/src/modules/video_coding/main/source/er_tables_xor.h b/src/modules/video_coding/main/source/er_tables_xor.h
new file mode 100644
index 0000000..7dc5191
--- /dev/null
+++ b/src/modules/video_coding/main/source/er_tables_xor.h
@@ -0,0 +1,38728 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_
+
+namespace webrtc
+{
+
+// Table for average FEC recovery from packet loss, for XOR code.
+// From RPL model of random loss.
+// Input is the received packet loss (up to 50%), and FEC code parameters (up to 24x24):
+// i.e., averageFECRecoveryRS[k] where k= code_i*129 + loss_j;
+// code_i=1x1,2x1,2x2,..24x24, loss_j=0,1,..128.
+const unsigned char VCMAvgFECRecoveryXOR[38700] = {
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+31,
+31,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+64,
+63,
+63,
+63,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+24,
+24,
+24,
+24,
+25,
+25,
+25,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+42,
+42,
+43,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+61,
+61,
+61,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+64,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+64,
+63,
+63,
+63,
+63,
+62,
+62,
+62,
+61,
+61,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+19,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+25,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+47,
+46,
+46,
+45,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+55,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+61,
+61,
+62,
+62,
+62,
+63,
+63,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+68,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+63,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+46,
+46,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+34,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+53,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+55,
+55,
+55,
+54,
+54,
+54,
+53,
+53,
+53,
+52,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+61,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+64,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+67,
+67,
+67,
+67,
+67,
+67,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+67,
+67,
+67,
+67,
+67,
+67,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+7,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+24,
+24,
+24,
+25,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+25,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+49,
+49,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+46,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+53,
+53,
+54,
+55,
+55,
+56,
+57,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+63,
+64,
+64,
+64,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+66,
+66,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+66,
+66,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+63,
+62,
+62,
+61,
+61,
+60,
+60,
+59,
+59,
+58,
+57,
+57,
+56,
+55,
+55,
+54,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+19,
+20,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+61,
+61,
+61,
+61,
+60,
+60,
+60,
+59,
+59,
+58,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+51,
+50,
+49,
+49,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+57,
+58,
+59,
+59,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+65,
+65,
+65,
+66,
+66,
+66,
+67,
+67,
+67,
+68,
+68,
+68,
+68,
+68,
+68,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+68,
+68,
+68,
+68,
+68,
+68,
+67,
+67,
+67,
+67,
+66,
+66,
+65,
+65,
+65,
+64,
+64,
+63,
+63,
+62,
+62,
+61,
+61,
+60,
+59,
+59,
+58,
+57,
+57,
+56,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+9,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+20,
+20,
+20,
+20,
+20,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+43,
+43,
+43,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+55,
+55,
+54,
+54,
+54,
+54,
+53,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+41,
+41,
+40,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+62,
+62,
+62,
+62,
+61,
+61,
+61,
+60,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+55,
+56,
+56,
+57,
+58,
+59,
+59,
+60,
+61,
+61,
+62,
+63,
+63,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+68,
+69,
+69,
+69,
+70,
+70,
+70,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+67,
+67,
+66,
+66,
+65,
+65,
+64,
+64,
+63,
+62,
+62,
+61,
+60,
+59,
+59,
+58,
+57,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+24,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+63,
+64,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+63,
+62,
+62,
+61,
+61,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+56,
+56,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+61,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+68,
+69,
+69,
+69,
+70,
+70,
+70,
+70,
+70,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+71,
+70,
+70,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+67,
+67,
+66,
+66,
+65,
+65,
+64,
+64,
+63,
+63,
+62,
+61,
+61,
+60,
+59,
+58,
+58,
+57,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+24,
+24,
+24,
+24,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+39,
+39,
+39,
+39,
+40,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+54,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+53,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+61,
+60,
+60,
+60,
+60,
+59,
+59,
+59,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+44,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+63,
+63,
+64,
+64,
+64,
+64,
+64,
+64,
+63,
+63,
+63,
+63,
+63,
+62,
+62,
+62,
+62,
+61,
+61,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+57,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+51,
+50,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+39,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+58,
+59,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+65,
+66,
+67,
+67,
+68,
+68,
+69,
+69,
+70,
+70,
+71,
+71,
+71,
+72,
+72,
+72,
+72,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+73,
+72,
+72,
+72,
+72,
+71,
+71,
+70,
+70,
+70,
+69,
+69,
+68,
+68,
+67,
+66,
+66,
+65,
+64,
+64,
+63,
+62,
+61,
+61,
+60,
+59,
+58,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+5,
+6,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+55,
+54,
+54,
+54,
+53,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+62,
+61,
+61,
+61,
+61,
+61,
+61,
+60,
+60,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+57,
+57,
+56,
+55,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+39,
+39,
+38,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+57,
+58,
+59,
+59,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+65,
+65,
+65,
+66,
+66,
+66,
+67,
+67,
+67,
+67,
+67,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+67,
+67,
+67,
+67,
+67,
+67,
+66,
+66,
+66,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+62,
+61,
+61,
+60,
+60,
+59,
+58,
+57,
+57,
+56,
+55,
+54,
+54,
+53,
+52,
+51,
+50,
+49,
+49,
+48,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+58,
+59,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+69,
+69,
+70,
+70,
+70,
+71,
+71,
+71,
+72,
+72,
+72,
+72,
+72,
+72,
+73,
+73,
+73,
+73,
+73,
+72,
+72,
+72,
+72,
+72,
+72,
+71,
+71,
+71,
+70,
+70,
+70,
+69,
+69,
+68,
+68,
+67,
+66,
+66,
+65,
+65,
+64,
+63,
+62,
+62,
+61,
+60,
+59,
+58,
+58,
+57,
+56,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+5,
+5,
+5,
+6,
+6,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+20,
+20,
+20,
+20,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+23,
+22,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+34,
+33,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+43,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+55,
+55,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+62,
+62,
+62,
+62,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+62,
+62,
+62,
+61,
+61,
+61,
+60,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+56,
+56,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+49,
+48,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+55,
+56,
+56,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+67,
+68,
+68,
+68,
+68,
+68,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+68,
+68,
+68,
+68,
+67,
+67,
+67,
+66,
+66,
+65,
+65,
+64,
+64,
+63,
+63,
+62,
+61,
+61,
+60,
+59,
+59,
+58,
+57,
+56,
+56,
+55,
+54,
+53,
+52,
+51,
+50,
+50,
+49,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+69,
+69,
+70,
+70,
+71,
+71,
+72,
+72,
+72,
+73,
+73,
+73,
+73,
+73,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+73,
+73,
+73,
+73,
+73,
+72,
+72,
+72,
+71,
+71,
+70,
+70,
+69,
+69,
+68,
+67,
+67,
+66,
+66,
+65,
+64,
+63,
+63,
+62,
+61,
+60,
+59,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+4,
+5,
+5,
+5,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+9,
+10,
+10,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+19,
+19,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+23,
+24,
+24,
+25,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+27,
+27,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+49,
+48,
+48,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+53,
+53,
+53,
+54,
+54,
+54,
+54,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+54,
+54,
+54,
+54,
+53,
+53,
+53,
+52,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+51,
+51,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+43,
+43,
+42,
+41,
+40,
+39,
+39,
+38,
+37,
+36,
+35,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+61,
+62,
+62,
+62,
+63,
+63,
+63,
+64,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+64,
+63,
+63,
+63,
+62,
+62,
+61,
+61,
+60,
+60,
+59,
+59,
+58,
+57,
+57,
+56,
+55,
+55,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+43,
+42,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+61,
+61,
+62,
+63,
+63,
+64,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+69,
+69,
+69,
+69,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+68,
+67,
+67,
+66,
+66,
+65,
+65,
+64,
+63,
+63,
+62,
+61,
+60,
+60,
+59,
+58,
+57,
+56,
+55,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+66,
+66,
+67,
+68,
+68,
+69,
+69,
+70,
+70,
+71,
+71,
+72,
+72,
+72,
+73,
+73,
+73,
+73,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+73,
+73,
+73,
+73,
+72,
+72,
+72,
+71,
+71,
+70,
+70,
+69,
+69,
+68,
+67,
+67,
+66,
+65,
+64,
+64,
+63,
+62,
+61,
+60,
+59,
+58,
+57,
+56,
+55,
+54,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+4,
+5,
+5,
+5,
+5,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+53,
+54,
+54,
+55,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+55,
+55,
+54,
+54,
+54,
+53,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+39,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+57,
+58,
+59,
+59,
+60,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+65,
+65,
+66,
+66,
+66,
+66,
+66,
+65,
+65,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+62,
+62,
+61,
+61,
+60,
+59,
+59,
+58,
+57,
+57,
+56,
+55,
+54,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+53,
+54,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+62,
+63,
+63,
+64,
+64,
+65,
+66,
+66,
+67,
+67,
+67,
+68,
+68,
+69,
+69,
+69,
+70,
+70,
+70,
+70,
+70,
+70,
+71,
+71,
+71,
+71,
+70,
+70,
+70,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+67,
+67,
+66,
+66,
+65,
+65,
+64,
+63,
+63,
+62,
+61,
+60,
+60,
+59,
+58,
+57,
+56,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+65,
+65,
+66,
+67,
+67,
+68,
+68,
+69,
+70,
+70,
+71,
+71,
+72,
+72,
+72,
+73,
+73,
+73,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+73,
+73,
+73,
+72,
+72,
+72,
+71,
+71,
+70,
+69,
+69,
+68,
+67,
+67,
+66,
+65,
+64,
+63,
+63,
+62,
+61,
+60,
+59,
+58,
+57,
+56,
+55,
+54,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+4,
+4,
+5,
+5,
+5,
+5,
+5,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+7,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+51,
+50,
+49,
+48,
+48,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+30,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+49,
+48,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+57,
+58,
+59,
+59,
+60,
+61,
+61,
+62,
+62,
+63,
+63,
+63,
+64,
+64,
+64,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+65,
+64,
+64,
+64,
+63,
+63,
+63,
+62,
+62,
+61,
+60,
+60,
+59,
+58,
+58,
+57,
+56,
+55,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+58,
+59,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+69,
+69,
+69,
+69,
+70,
+70,
+70,
+70,
+70,
+71,
+71,
+71,
+71,
+71,
+70,
+70,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+68,
+67,
+67,
+66,
+65,
+65,
+64,
+63,
+63,
+62,
+61,
+61,
+60,
+59,
+58,
+57,
+56,
+55,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+48,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+57,
+58,
+59,
+60,
+60,
+61,
+62,
+63,
+64,
+64,
+65,
+66,
+66,
+67,
+68,
+68,
+69,
+69,
+70,
+70,
+71,
+71,
+72,
+72,
+73,
+73,
+73,
+73,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+73,
+73,
+73,
+72,
+72,
+71,
+71,
+70,
+70,
+69,
+69,
+68,
+67,
+67,
+66,
+65,
+64,
+63,
+62,
+61,
+61,
+60,
+59,
+58,
+57,
+56,
+54,
+53,
+52,
+51,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+4,
+4,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+7,
+7,
+8,
+8,
+9,
+9,
+9,
+10,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+37,
+37,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+47,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+46,
+45,
+44,
+43,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+21,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+50,
+51,
+51,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+60,
+60,
+60,
+61,
+61,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+62,
+62,
+61,
+61,
+61,
+61,
+61,
+60,
+60,
+60,
+59,
+59,
+58,
+58,
+57,
+56,
+56,
+55,
+54,
+54,
+53,
+52,
+51,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+55,
+56,
+57,
+58,
+58,
+59,
+60,
+60,
+61,
+62,
+62,
+63,
+63,
+64,
+64,
+65,
+65,
+65,
+66,
+66,
+66,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+67,
+66,
+66,
+66,
+65,
+65,
+65,
+64,
+64,
+63,
+62,
+62,
+61,
+61,
+60,
+59,
+58,
+58,
+57,
+56,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+0,
+1,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+55,
+56,
+57,
+57,
+58,
+59,
+60,
+61,
+61,
+62,
+63,
+63,
+64,
+65,
+65,
+66,
+66,
+67,
+67,
+68,
+68,
+68,
+69,
+69,
+69,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+69,
+69,
+69,
+68,
+68,
+68,
+67,
+67,
+66,
+66,
+65,
+64,
+64,
+63,
+62,
+61,
+60,
+60,
+59,
+58,
+57,
+56,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+45,
+44,
+43,
+42,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+56,
+57,
+58,
+59,
+60,
+61,
+62,
+62,
+63,
+64,
+65,
+65,
+66,
+67,
+67,
+68,
+69,
+69,
+70,
+70,
+71,
+71,
+72,
+72,
+73,
+73,
+73,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+74,
+73,
+73,
+73,
+72,
+72,
+71,
+71,
+70,
+69,
+69,
+68,
+67,
+66,
+66,
+65,
+64,
+63,
+62,
+61,
+60,
+59,
+58,
+57,
+56,
+54,
+53,
+52,
+51,
+50,
+49,
+0,
+0,
+1,
+2,
+3,
+3,
+4,
+4,
+4,
+4,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+9,
+9,
+9,
+9,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+23,
+24,
+24,
+24,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+27,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+44,
+44,
+44,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+0,
+0,
+2,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+55,
+55,
+56,
+56,
+56,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+51,
+51,
+50,
+49,
+48,
+47,
+46,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+17,
+0,
+0,
+2,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+55,
+56,
+57,
+57,
+57,
+58,
+58,
+59,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+59,
+59,
+59,
+59,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+0,
+1,
+2,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+59,
+59,
+59,
+59,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+0,
+0,
+1,
+2,
+3,
+3,
+3,
+4,
+4,
+4,
+4,
+4,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+10,
+10,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+36,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+53,
+53,
+53,
+53,
+53,
+53,
+53,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+46,
+45,
+44,
+43,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+49,
+49,
+49,
+49,
+49,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+43,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+48,
+48,
+48,
+49,
+49,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+49,
+49,
+48,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+54,
+54,
+54,
+54,
+53,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+0,
+1,
+2,
+3,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+56,
+56,
+56,
+56,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+54,
+54,
+53,
+53,
+52,
+51,
+50,
+50,
+49,
+48,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+33,
+34,
+35,
+36,
+37,
+38,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+50,
+50,
+51,
+52,
+52,
+52,
+53,
+53,
+53,
+54,
+54,
+54,
+54,
+54,
+54,
+53,
+53,
+53,
+53,
+52,
+52,
+51,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+39,
+40,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+52,
+53,
+53,
+54,
+54,
+54,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+55,
+54,
+54,
+53,
+53,
+53,
+52,
+51,
+51,
+50,
+49,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+14,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+17,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+29,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+40,
+41,
+42,
+43,
+44,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+56,
+56,
+57,
+57,
+57,
+57,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+51,
+50,
+49,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+7,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+25,
+26,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+45,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+44,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+3,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+6,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+12,
+13,
+13,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+19,
+18,
+18,
+18,
+18,
+17,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+24,
+24,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+41,
+41,
+41,
+41,
+41,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+41,
+41,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+36,
+37,
+37,
+38,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+10,
+10,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+47,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+17,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+30,
+31,
+32,
+33,
+34,
+35,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+49,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+19,
+20,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+51,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+52,
+51,
+51,
+51,
+50,
+50,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+29,
+30,
+31,
+32,
+34,
+35,
+36,
+37,
+39,
+40,
+41,
+42,
+43,
+45,
+46,
+47,
+48,
+49,
+49,
+50,
+51,
+52,
+53,
+53,
+54,
+54,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+55,
+54,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+13,
+13,
+12,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+20,
+21,
+23,
+24,
+26,
+27,
+29,
+30,
+32,
+33,
+35,
+36,
+38,
+39,
+40,
+42,
+43,
+44,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+52,
+53,
+54,
+54,
+55,
+55,
+55,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+55,
+55,
+54,
+54,
+54,
+53,
+52,
+52,
+51,
+50,
+49,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+15,
+14,
+13,
+13,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+15,
+17,
+18,
+19,
+21,
+22,
+24,
+25,
+27,
+28,
+30,
+31,
+33,
+35,
+36,
+38,
+39,
+41,
+42,
+43,
+45,
+46,
+47,
+48,
+49,
+51,
+52,
+52,
+53,
+54,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+58,
+58,
+58,
+58,
+57,
+57,
+56,
+55,
+55,
+54,
+53,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+3,
+3,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+24,
+24,
+24,
+24,
+24,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+25,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+26,
+26,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+27,
+26,
+26,
+26,
+26,
+25,
+25,
+24,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+36,
+36,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+36,
+36,
+37,
+37,
+37,
+36,
+36,
+36,
+36,
+36,
+36,
+35,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+32,
+33,
+33,
+34,
+35,
+35,
+36,
+37,
+37,
+38,
+38,
+39,
+39,
+39,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+39,
+39,
+39,
+38,
+38,
+37,
+37,
+36,
+36,
+35,
+35,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+28,
+29,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+43,
+44,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+18,
+20,
+21,
+22,
+23,
+24,
+25,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+44,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+32,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+21,
+22,
+23,
+24,
+26,
+27,
+28,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+44,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+10,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+23,
+24,
+25,
+26,
+27,
+28,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+18,
+19,
+20,
+22,
+23,
+25,
+26,
+28,
+29,
+30,
+32,
+33,
+35,
+36,
+37,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+50,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+52,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+11,
+12,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+16,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+15,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+20,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+23,
+23,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+14,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+27,
+28,
+28,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+28,
+28,
+28,
+28,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+23,
+24,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+28,
+29,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+34,
+33,
+32,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+27,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+29,
+29,
+30,
+31,
+31,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+34,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+35,
+34,
+34,
+34,
+33,
+33,
+33,
+32,
+31,
+31,
+30,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+25,
+24,
+23,
+22,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+18,
+19,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+20,
+21,
+22,
+24,
+25,
+26,
+27,
+28,
+30,
+31,
+32,
+34,
+35,
+36,
+37,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+45,
+46,
+47,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+47,
+46,
+45,
+45,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+9,
+9,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+21,
+22,
+24,
+25,
+26,
+28,
+29,
+31,
+32,
+34,
+35,
+36,
+38,
+39,
+40,
+41,
+42,
+44,
+44,
+45,
+46,
+47,
+48,
+48,
+49,
+49,
+50,
+50,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+47,
+47,
+46,
+45,
+44,
+44,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+18,
+19,
+21,
+22,
+23,
+25,
+26,
+28,
+29,
+31,
+32,
+34,
+35,
+37,
+38,
+39,
+40,
+42,
+43,
+44,
+45,
+46,
+46,
+47,
+48,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+43,
+43,
+42,
+41,
+40,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+9,
+9,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+9,
+10,
+10,
+10,
+10,
+10,
+11,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+26,
+27,
+28,
+29,
+31,
+32,
+33,
+34,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+47,
+48,
+49,
+49,
+49,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+50,
+49,
+49,
+49,
+48,
+48,
+47,
+46,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+40,
+39,
+38,
+37,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+13,
+12,
+12,
+11,
+10,
+10,
+9,
+9,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+16,
+17,
+18,
+20,
+21,
+23,
+24,
+26,
+27,
+29,
+31,
+32,
+34,
+35,
+37,
+39,
+40,
+42,
+43,
+44,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+59,
+58,
+58,
+57,
+57,
+56,
+56,
+55,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+44,
+43,
+42,
+41,
+40,
+38,
+37,
+36,
+35,
+34,
+32,
+31,
+30,
+29,
+28,
+27,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+7,
+7,
+8,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+16,
+17,
+18,
+20,
+21,
+23,
+25,
+26,
+28,
+30,
+31,
+33,
+35,
+36,
+38,
+39,
+41,
+42,
+44,
+45,
+46,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+55,
+56,
+56,
+57,
+57,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+57,
+57,
+57,
+56,
+56,
+55,
+54,
+54,
+53,
+52,
+51,
+51,
+50,
+49,
+48,
+47,
+46,
+45,
+43,
+42,
+41,
+40,
+39,
+38,
+36,
+35,
+34,
+33,
+32,
+31,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+17,
+16,
+15,
+14,
+13,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+9,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+19,
+20,
+21,
+23,
+24,
+26,
+27,
+29,
+30,
+32,
+34,
+35,
+37,
+38,
+40,
+41,
+43,
+44,
+46,
+47,
+48,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+59,
+60,
+61,
+61,
+61,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+62,
+61,
+61,
+60,
+60,
+59,
+58,
+58,
+57,
+56,
+55,
+54,
+53,
+52,
+51,
+50,
+49,
+48,
+47,
+46,
+44,
+43,
+42,
+41,
+39,
+38,
+37,
+36,
+34,
+33,
+32,
+31,
+29,
+28,
+27,
+26,
+25,
+24,
+22,
+21,
+20,
+19,
+18,
+0,
+0,
+1,
+2,
+2,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+4,
+5,
+5,
+6,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+9,
+9,
+10,
+10,
+10,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+11,
+10,
+10,
+10,
+10,
+10,
+9,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+15,
+14,
+14,
+14,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+12,
+12,
+13,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+16,
+17,
+17,
+17,
+17,
+17,
+16,
+16,
+16,
+16,
+16,
+15,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+19,
+20,
+20,
+20,
+20,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+22,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+22,
+22,
+22,
+21,
+21,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+13,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+18,
+18,
+19,
+19,
+19,
+19,
+20,
+20,
+20,
+20,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+21,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+22,
+21,
+21,
+21,
+21,
+21,
+21,
+20,
+20,
+20,
+20,
+19,
+19,
+19,
+18,
+18,
+18,
+17,
+17,
+17,
+16,
+16,
+15,
+15,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+20,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+3,
+2,
+2,
+2,
+2,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+16,
+17,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+23,
+24,
+25,
+26,
+26,
+27,
+27,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+34,
+33,
+33,
+33,
+33,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+5,
+4,
+4,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+11,
+12,
+13,
+14,
+14,
+15,
+15,
+16,
+16,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+33,
+32,
+32,
+32,
+32,
+31,
+31,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+14,
+15,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+24,
+25,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+31,
+32,
+32,
+33,
+33,
+34,
+34,
+35,
+35,
+35,
+36,
+36,
+36,
+37,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+32,
+31,
+30,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+16,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+20,
+21,
+21,
+22,
+23,
+23,
+24,
+25,
+25,
+26,
+26,
+27,
+27,
+28,
+28,
+29,
+29,
+29,
+30,
+30,
+30,
+30,
+30,
+31,
+31,
+31,
+30,
+30,
+30,
+30,
+30,
+29,
+29,
+29,
+28,
+28,
+27,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+15,
+16,
+16,
+16,
+17,
+17,
+17,
+18,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+22,
+23,
+24,
+24,
+25,
+26,
+26,
+27,
+28,
+28,
+29,
+29,
+30,
+30,
+30,
+31,
+31,
+31,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+32,
+31,
+31,
+31,
+30,
+30,
+30,
+29,
+29,
+28,
+28,
+27,
+27,
+26,
+25,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+3,
+3,
+3,
+3,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+9,
+10,
+11,
+11,
+12,
+12,
+12,
+13,
+13,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+14,
+15,
+15,
+15,
+16,
+16,
+17,
+17,
+18,
+19,
+19,
+20,
+21,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+7,
+8,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+13,
+13,
+14,
+14,
+14,
+14,
+15,
+15,
+16,
+16,
+16,
+17,
+18,
+18,
+19,
+20,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+38,
+38,
+39,
+39,
+40,
+40,
+41,
+41,
+41,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+41,
+41,
+41,
+41,
+40,
+40,
+39,
+39,
+38,
+38,
+37,
+36,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+7,
+6,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+14,
+15,
+15,
+16,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+27,
+28,
+29,
+30,
+31,
+32,
+32,
+33,
+34,
+34,
+35,
+35,
+36,
+36,
+37,
+37,
+37,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+38,
+37,
+37,
+37,
+36,
+36,
+36,
+35,
+35,
+34,
+34,
+33,
+33,
+32,
+31,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+13,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+8,
+8,
+9,
+9,
+9,
+9,
+9,
+9,
+10,
+10,
+11,
+11,
+12,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+20,
+22,
+23,
+24,
+25,
+27,
+28,
+29,
+30,
+31,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+42,
+42,
+43,
+43,
+44,
+44,
+44,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+45,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+29,
+28,
+27,
+26,
+25,
+24,
+23,
+23,
+22,
+21,
+20,
+19,
+19,
+18,
+17,
+16,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+6,
+7,
+7,
+8,
+8,
+9,
+10,
+10,
+11,
+12,
+13,
+14,
+16,
+17,
+18,
+19,
+20,
+22,
+23,
+24,
+25,
+26,
+28,
+29,
+30,
+31,
+32,
+33,
+34,
+34,
+35,
+36,
+37,
+37,
+38,
+38,
+38,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+39,
+38,
+38,
+38,
+37,
+37,
+36,
+35,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+24,
+24,
+23,
+22,
+21,
+20,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+8,
+7,
+7,
+6,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+8,
+8,
+8,
+8,
+8,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+8,
+8,
+8,
+9,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+19,
+21,
+22,
+23,
+25,
+26,
+27,
+29,
+30,
+31,
+32,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+39,
+38,
+37,
+37,
+36,
+35,
+34,
+33,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+10,
+9,
+9,
+8,
+8,
+7,
+7,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+6,
+6,
+6,
+7,
+7,
+8,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+19,
+20,
+21,
+22,
+24,
+25,
+26,
+27,
+29,
+30,
+31,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+39,
+40,
+41,
+41,
+42,
+42,
+42,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+43,
+42,
+42,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+31,
+30,
+29,
+28,
+27,
+26,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+15,
+15,
+14,
+13,
+13,
+12,
+12,
+11,
+10,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+5,
+5,
+5,
+6,
+7,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+16,
+17,
+18,
+20,
+21,
+22,
+24,
+25,
+26,
+28,
+29,
+30,
+32,
+33,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+41,
+42,
+43,
+43,
+44,
+44,
+44,
+44,
+44,
+45,
+45,
+44,
+44,
+44,
+44,
+44,
+43,
+43,
+42,
+42,
+41,
+41,
+40,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+14,
+14,
+13,
+12,
+12,
+11,
+11,
+10,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+5,
+5,
+6,
+6,
+7,
+8,
+8,
+9,
+10,
+11,
+12,
+13,
+15,
+16,
+17,
+18,
+20,
+21,
+23,
+24,
+25,
+27,
+28,
+29,
+31,
+32,
+33,
+34,
+35,
+37,
+38,
+39,
+40,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+45,
+46,
+46,
+46,
+47,
+47,
+47,
+47,
+47,
+47,
+47,
+46,
+46,
+46,
+46,
+45,
+45,
+44,
+44,
+44,
+43,
+42,
+42,
+41,
+41,
+40,
+39,
+38,
+38,
+37,
+36,
+35,
+34,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+28,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+21,
+20,
+19,
+18,
+17,
+17,
+16,
+15,
+0,
+0,
+1,
+2,
+3,
+4,
+5,
+6,
+6,
+6,
+7,
+7,
+7,
+7,
+7,
+7,
+6,
+6,
+6,
+5,
+5,
+5,
+5,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+4,
+5,
+5,
+6,
+6,
+7,
+7,
+8,
+9,
+10,
+11,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+20,
+21,
+22,
+23,
+25,
+26,
+27,
+29,
+30,
+31,
+32,
+33,
+35,
+36,
+37,
+38,
+39,
+40,
+41,
+42,
+43,
+43,
+44,
+45,
+45,
+46,
+46,
+47,
+47,
+47,
+47,
+48,
+48,
+48,
+48,
+48,
+47,
+47,
+47,
+47,
+46,
+46,
+45,
+45,
+44,
+44,
+43,
+42,
+42,
+41,
+40,
+39,
+39,
+38,
+37,
+36,
+35,
+34,
+33,
+32,
+31,
+30,
+29,
+28,
+27,
+27,
+26,
+25,
+24,
+23,
+22,
+21,
+20,
+19,
+18,
+18,
+17,
+16,
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_
diff --git a/src/modules/video_coding/main/source/event.h b/src/modules/video_coding/main/source/event.h
new file mode 100644
index 0000000..39fd494
--- /dev/null
+++ b/src/modules/video_coding/main/source/event.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_EVENT_H_
+#define WEBRTC_MODULES_VIDEO_CODING_EVENT_H_
+
+#include "event_wrapper.h"
+
+namespace webrtc
+{
+
+//#define EVENT_DEBUG
+
+class VCMEvent : public EventWrapper
+{
+public:
+ VCMEvent() : _event(*EventWrapper::Create()) {};
+
+ virtual ~VCMEvent() { delete &_event; };
+
+ /**
+ * Release waiting threads
+ */
+ bool Set() { return _event.Set(); };
+
+ bool Reset() { return _event.Reset(); };
+
+ /**
+ * Wait for this event
+ */
+ EventTypeWrapper Wait(unsigned long maxTime)
+ {
+#ifdef EVENT_DEBUG
+ return kEventTimeout;
+#else
+ return _event.Wait(maxTime);
+#endif
+ };
+
+ /**
+ * Start a timer
+ */
+ bool StartTimer(bool periodic, unsigned long time)
+ { return _event.StartTimer(periodic, time); };
+ /**
+ * Stop the timer
+ */
+ bool StopTimer() { return _event.StopTimer(); };
+
+private:
+ EventWrapper& _event;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_EVENT_H_
diff --git a/src/modules/video_coding/main/source/exp_filter.cc b/src/modules/video_coding/main/source/exp_filter.cc
new file mode 100644
index 0000000..1d6f9a7
--- /dev/null
+++ b/src/modules/video_coding/main/source/exp_filter.cc
@@ -0,0 +1,60 @@
+/*
+ * 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 "exp_filter.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+void
+VCMExpFilter::Reset(float alpha)
+{
+ _alpha = alpha;
+ _filtered = -1.0;
+}
+
+float
+VCMExpFilter::Apply(float exp, float sample)
+{
+ if (_filtered == -1.0)
+ {
+ // Initialize filtered bit rates
+ _filtered = sample;
+ }
+ else if (exp == 1.0)
+ {
+ _filtered = _alpha * _filtered + (1 - _alpha) * sample;
+ }
+ else
+ {
+ float alpha = pow(_alpha, exp);
+ _filtered = alpha * _filtered + (1 - alpha) * sample;
+ }
+ if (_max != -1 && _filtered > _max)
+ {
+ _filtered = _max;
+ }
+ return _filtered;
+}
+
+void
+VCMExpFilter::UpdateBase(float alpha)
+{
+ _alpha = alpha;
+}
+
+float
+VCMExpFilter::Value() const
+{
+ return _filtered;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/exp_filter.h b/src/modules/video_coding/main/source/exp_filter.h
new file mode 100644
index 0000000..46d206a
--- /dev/null
+++ b/src/modules/video_coding/main/source/exp_filter.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_
+
+namespace webrtc
+{
+
+/**********************/
+/* ExpFilter class */
+/**********************/
+
+class VCMExpFilter
+{
+public:
+ VCMExpFilter(float alpha, float max = -1.0) : _alpha(alpha), _filtered(-1.0), _max(max) {}
+
+ // Resets the filter to its initial state, and resets alpha to the given value
+ //
+ // Input:
+ // - alpha : the new value of the filter factor base.
+ void Reset(float alpha);
+
+ // Applies the filter with the given exponent on the provided sample
+ //
+ // Input:
+ // - exp : Exponent T in y(k) = alpha^T * y(k-1) + (1 - alpha^T) * x(k)
+ // - sample : x(k) in the above filter equation
+ float Apply(float exp, float sample);
+
+ // Return current filtered value: y(k)
+ //
+ // Return value : The current filter output
+ float Value() const;
+
+ // Change the filter factor base
+ //
+ // Input:
+ // - alpha : The new filter factor base.
+ void UpdateBase(float alpha);
+
+private:
+ float _alpha; // Filter factor base
+ float _filtered; // Current filter output
+ const float _max;
+}; // end of ExpFilter class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_
diff --git a/src/modules/video_coding/main/source/fec_tables_xor.h b/src/modules/video_coding/main/source/fec_tables_xor.h
new file mode 100644
index 0000000..3c9eaeb
--- /dev/null
+++ b/src/modules/video_coding/main/source/fec_tables_xor.h
@@ -0,0 +1,6478 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_
+
+namespace webrtc
+{
+
+// Table for Protection factor (code rate) of delta frames, for the XOR FEC.
+// Input is the packet loss and average bits/frame (bitRate/frame_rate):
+// i.e., codeRateXORTable[k] where k = rate_i*129 + loss_j; loss_j=0,1,..128,
+// and rate_i varies over some range
+const unsigned char VCMCodeRateXORTable[6450] = {
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+25,
+25,
+25,
+25,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+0,
+0,
+0,
+0,
+25,
+25,
+25,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+0,
+0,
+0,
+0,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+42,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+0,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+0,
+0,
+0,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+36,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+31,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+0,
+0,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+28,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+56,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+0,
+0,
+25,
+25,
+25,
+25,
+25,
+25,
+25,
+51,
+51,
+51,
+51,
+51,
+51,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+0,
+25,
+25,
+25,
+25,
+25,
+25,
+51,
+51,
+51,
+51,
+51,
+51,
+51,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+46,
+46,
+46,
+46,
+46,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+0,
+23,
+23,
+23,
+23,
+23,
+23,
+23,
+46,
+46,
+46,
+46,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+0,
+23,
+23,
+23,
+23,
+23,
+23,
+46,
+46,
+46,
+46,
+46,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+69,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+0,
+21,
+21,
+21,
+21,
+21,
+42,
+42,
+42,
+42,
+42,
+42,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+21,
+21,
+21,
+21,
+21,
+42,
+42,
+42,
+42,
+42,
+42,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+106,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+19,
+19,
+19,
+19,
+19,
+39,
+39,
+39,
+39,
+58,
+58,
+58,
+58,
+58,
+58,
+58,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+0,
+19,
+19,
+19,
+19,
+39,
+39,
+39,
+39,
+39,
+58,
+58,
+58,
+58,
+58,
+58,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+78,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+98,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+117,
+0,
+18,
+18,
+18,
+18,
+36,
+36,
+36,
+54,
+54,
+54,
+54,
+54,
+54,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+18,
+18,
+18,
+18,
+36,
+36,
+36,
+54,
+54,
+54,
+54,
+54,
+54,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+18,
+18,
+18,
+18,
+36,
+36,
+36,
+54,
+54,
+54,
+54,
+54,
+54,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+91,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+17,
+17,
+17,
+17,
+34,
+34,
+51,
+51,
+51,
+51,
+51,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+0,
+17,
+17,
+17,
+17,
+34,
+34,
+51,
+51,
+51,
+51,
+51,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+68,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+119,
+0,
+15,
+15,
+15,
+31,
+31,
+31,
+47,
+47,
+47,
+47,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+15,
+15,
+15,
+31,
+31,
+31,
+47,
+47,
+47,
+47,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+63,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+79,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+95,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+111,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+15,
+15,
+15,
+30,
+30,
+30,
+45,
+45,
+45,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+0,
+15,
+15,
+15,
+30,
+30,
+30,
+45,
+45,
+45,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+0,
+15,
+15,
+15,
+30,
+30,
+45,
+45,
+45,
+45,
+60,
+60,
+60,
+60,
+60,
+60,
+60,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+75,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+90,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+105,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+0,
+14,
+14,
+28,
+28,
+28,
+42,
+42,
+56,
+56,
+56,
+56,
+56,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+14,
+14,
+28,
+28,
+28,
+42,
+42,
+56,
+56,
+56,
+56,
+56,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+70,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+113,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+13,
+13,
+26,
+26,
+40,
+40,
+40,
+40,
+53,
+53,
+67,
+67,
+67,
+67,
+67,
+67,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+0,
+13,
+13,
+26,
+26,
+40,
+40,
+40,
+40,
+53,
+53,
+67,
+67,
+67,
+67,
+67,
+67,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+80,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+93,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+107,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+120,
+0,
+12,
+12,
+25,
+25,
+38,
+38,
+38,
+38,
+63,
+63,
+63,
+63,
+63,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+12,
+12,
+25,
+25,
+38,
+38,
+38,
+38,
+63,
+63,
+63,
+63,
+63,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+76,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+89,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+102,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+114,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+12,
+12,
+24,
+24,
+36,
+36,
+48,
+48,
+48,
+48,
+60,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+0,
+12,
+12,
+24,
+24,
+36,
+36,
+48,
+48,
+48,
+48,
+60,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+0,
+12,
+12,
+24,
+24,
+36,
+48,
+48,
+48,
+48,
+48,
+60,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+72,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+85,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+97,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+109,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+0,
+11,
+11,
+23,
+34,
+34,
+34,
+46,
+46,
+57,
+57,
+57,
+57,
+57,
+57,
+69,
+69,
+69,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+11,
+11,
+23,
+34,
+34,
+34,
+46,
+46,
+57,
+57,
+57,
+57,
+57,
+57,
+69,
+69,
+69,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+81,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+92,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+104,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+115,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+127,
+0,
+11,
+11,
+22,
+33,
+33,
+44,
+44,
+44,
+55,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+66,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+88,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+99,
+110,
+110,
+110,
+110,
+110,
+110,
+110,
+110,
+110,
+110,
+110,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+121,
+
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_
diff --git a/src/modules/video_coding/main/source/frame_buffer.cc b/src/modules/video_coding/main/source/frame_buffer.cc
new file mode 100644
index 0000000..df7b37b
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_buffer.cc
@@ -0,0 +1,402 @@
+/*
+ * 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 "../../../../engine_configurations.h"
+#include "frame_buffer.h"
+#include "packet.h"
+
+#include <cassert>
+#include <string.h>
+
+#if defined(_WIN32)
+ // VS 2005: Don't warn for default initialized arrays. See help for more info.
+ #pragma warning(disable:4351)
+#endif
+
+namespace webrtc {
+
+// Constructor
+VCMFrameBuffer::VCMFrameBuffer() :
+ _state(kStateFree),
+ _frameCounted(false),
+ _nackCount(0),
+ _latestPacketTimeMs(-1)
+{
+}
+
+// Destructor
+VCMFrameBuffer::~VCMFrameBuffer()
+{
+ Reset();
+}
+
+VCMFrameBuffer::VCMFrameBuffer(VCMFrameBuffer& rhs)
+:
+VCMEncodedFrame(rhs),
+_state(rhs._state),
+_frameCounted(rhs._frameCounted),
+_sessionInfo(),
+_nackCount(rhs._nackCount),
+_latestPacketTimeMs(rhs._latestPacketTimeMs)
+{
+ _sessionInfo = rhs._sessionInfo;
+}
+
+webrtc::FrameType
+VCMFrameBuffer::FrameType() const
+{
+ return _sessionInfo.FrameType();
+}
+
+void
+VCMFrameBuffer::SetPreviousFrameLoss()
+{
+ _sessionInfo.SetPreviousFrameLoss();
+}
+
+WebRtc_Word32
+VCMFrameBuffer::GetLowSeqNum()
+{
+ return _sessionInfo.GetLowSeqNum();
+}
+
+// Get highest sequence number for complete sessions
+WebRtc_Word32
+VCMFrameBuffer::GetHighSeqNumComplete()
+{
+ if (_sessionInfo.IsSessionComplete())
+ {
+ return _sessionInfo.GetHighSeqNum();
+ }
+ return -1;
+}
+
+WebRtc_Word32
+VCMFrameBuffer::GetHighSeqNum()
+{
+ return _sessionInfo.GetHighSeqNum();
+}
+
+bool
+VCMFrameBuffer::IsSessionComplete()
+{
+ return _sessionInfo.IsSessionComplete();
+}
+
+// Insert packet
+VCMFrameBufferEnum
+VCMFrameBuffer::InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs)
+{
+ if (_state == kStateDecoding)
+ {
+ // Do not insert packet
+ return kIncomplete;
+ }
+
+ // Sanity to check if the frame has been freed. (Too old for example)
+ if(_state == kStateFree)
+ {
+ return kStateError;
+ }
+
+ // is this packet part of this frame
+ if (TimeStamp() && (TimeStamp() != packet.timestamp))
+ {
+ return kTimeStampError;
+ }
+
+ // sanity checks
+ if (_size + packet.sizeBytes +
+ (packet.insertStartCode ? kH264StartCodeLengthBytes : 0 )
+ > kMaxJBFrameSizeBytes)
+ {
+ return kSizeError;
+ }
+ if (NULL == packet.dataPtr && packet.sizeBytes > 0)
+ {
+ return kSizeError;
+ }
+ if ((packet.frameType != kFrameEmpty) &&
+ (!_sessionInfo.HaveStartSeqNumber()))
+ {
+ _sessionInfo.SetStartSeqNumber(packet.seqNum);
+ }
+ if (packet.dataPtr != NULL)
+ {
+ _payloadType = packet.payloadType;
+ }
+
+ if (kStateEmpty == _state)
+ {
+ // First packet (empty and/or media) inserted into this frame.
+ // store some info and set some initial values.
+ _timeStamp = packet.timestamp;
+ _codec = packet.codec;
+ if (packet.frameType != kFrameEmpty)
+ {
+ // first media packet
+ SetState(kStateIncomplete);
+ }
+ }
+
+ WebRtc_UWord32 requiredSizeBytes = Length() + packet.sizeBytes +
+ (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
+ if (requiredSizeBytes >= _size)
+ {
+ const WebRtc_UWord32 increments = requiredSizeBytes /
+ kBufferIncStepSizeBytes +
+ (requiredSizeBytes %
+ kBufferIncStepSizeBytes > 0);
+ const WebRtc_UWord32 newSize = _size +
+ increments * kBufferIncStepSizeBytes;
+ if (newSize > kMaxJBFrameSizeBytes)
+ {
+ return kSizeError;
+ }
+ if (VerifyAndAllocate(newSize) == -1)
+ {
+ return kSizeError;
+ }
+ }
+ WebRtc_Word64 retVal = _sessionInfo.InsertPacket(packet, _buffer);
+ if (retVal == -1)
+ {
+ return kSizeError;
+ }
+ else if (retVal == -2)
+ {
+ return kDuplicatePacket;
+ }
+ // update length
+ _length = Length() + static_cast<WebRtc_UWord32>(retVal);
+
+ _latestPacketTimeMs = timeInMs;
+
+ if(_sessionInfo.IsSessionComplete())
+ {
+ return kCompleteSession;
+ }
+ else
+ {
+ // this layer is not complete
+ if (_state == kStateComplete)
+ {
+ // we already have a complete layer
+ // wait for all independent layers belonging to the same frame
+ _state = kStateIncomplete;
+ }
+ }
+ return kIncomplete;
+}
+
+WebRtc_Word64 VCMFrameBuffer::LatestPacketTimeMs()
+{
+ return _latestPacketTimeMs;
+}
+
+// Zero out all entries in list up to and including the (first) entry equal to _lowSeqNum
+WebRtc_Word32 VCMFrameBuffer::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num)
+{
+ if(_sessionInfo.ZeroOutSeqNum(list, num) != 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+// Zero out all entries in list up to and including the (first) entry equal to
+// _lowSeqNum. Hybrid mode: 1. Don't NACK FEC packets 2. Make a smart decision
+// on whether to NACK or not
+
+WebRtc_Word32 VCMFrameBuffer::ZeroOutSeqNumHybrid(WebRtc_Word32* list,
+ WebRtc_Word32 num,
+ float rttScore)
+{
+ return _sessionInfo.ZeroOutSeqNumHybrid(list, num, rttScore);
+}
+
+void VCMFrameBuffer::IncrementNackCount()
+{
+ _nackCount++;
+}
+
+WebRtc_Word16 VCMFrameBuffer::GetNackCount() const
+{
+ return _nackCount;
+}
+
+bool VCMFrameBuffer::HaveLastPacket()
+{
+ return _sessionInfo.HaveLastPacket();
+}
+
+bool
+VCMFrameBuffer::ForceSetHaveLastPacket()
+{
+ _sessionInfo.ForceSetHaveLastPacket();
+ return _sessionInfo.IsSessionComplete();
+}
+
+void VCMFrameBuffer::Reset()
+{
+ _length = 0;
+ _timeStamp = 0;
+ _sessionInfo.Reset();
+ _frameCounted = false;
+ _payloadType = 0;
+ _nackCount = 0;
+ _latestPacketTimeMs = -1;
+ _state = kStateFree;
+ VCMEncodedFrame::Reset();
+}
+
+// Makes sure the session contains a decodable stream.
+void
+VCMFrameBuffer::MakeSessionDecodable()
+{
+ WebRtc_Word32 retVal = _sessionInfo.MakeSessionDecodable(_buffer);
+ // update length
+ _length -= retVal;
+}
+
+// Set state of frame
+void
+VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state)
+{
+ if(_state == state)
+ {
+ return;
+ }
+ switch (state)
+ {
+ case kStateFree:
+ // Reset everything
+ // We can go to this state from all other states.
+ // The one setting the state to free must ensure
+ // that the frame is removed from the timestamp
+ // ordered frame list in the jb.
+ Reset();
+ break;
+
+ case kStateIncomplete:
+ // we can go to this state from state kStateEmpty
+ assert(_state == kStateEmpty ||
+ _state == kStateDecoding);
+
+ // Do nothing, we received a packet
+ break;
+
+ case kStateComplete:
+ assert(_state == kStateEmpty ||
+ _state == kStateIncomplete ||
+ _state == kStateDecodable);
+
+ break;
+
+ case kStateEmpty:
+ assert(_state == kStateFree);
+ // Do nothing
+ break;
+
+ case kStateDecoding:
+ // we can go to this state from state kStateComplete kStateIncomplete
+ assert(_state == kStateComplete || _state == kStateIncomplete ||
+ _state == kStateDecodable);
+ // Transfer frame information to EncodedFrame and create any codec specific information
+ RestructureFrameInformation();
+ break;
+
+ case kStateDecodable:
+ if (_state == kStateComplete)
+ {
+ // if complete, obviously decodable, keep as is.
+ return;
+ }
+ assert(_state == kStateEmpty ||
+ _state == kStateIncomplete);
+ break;
+
+ default:
+ // Should never happen
+ assert(!"FrameBuffer::SetState Incorrect frame buffer state as input");
+ return;
+ }
+ _state = state;
+}
+
+void
+VCMFrameBuffer::RestructureFrameInformation()
+{
+ PrepareForDecode();
+ _frameType = ConvertFrameType(_sessionInfo.FrameType());
+ _completeFrame = _sessionInfo.IsSessionComplete();
+ _missingFrame = _sessionInfo.PreviousFrameLoss();
+}
+
+WebRtc_Word32
+VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage)
+{
+ _frameType = ConvertFrameType(frameFromStorage.frameType);
+ _timeStamp = frameFromStorage.timeStamp;
+ _payloadType = frameFromStorage.payloadType;
+ _encodedWidth = frameFromStorage.encodedWidth;
+ _encodedHeight = frameFromStorage.encodedHeight;
+ _missingFrame = frameFromStorage.missingFrame;
+ _completeFrame = frameFromStorage.completeFrame;
+ _renderTimeMs = frameFromStorage.renderTimeMs;
+ _codec = frameFromStorage.codec;
+ if (VerifyAndAllocate(frameFromStorage.payloadSize) < 0)
+ {
+ return VCM_MEMORY;
+ }
+ memcpy(_buffer, frameFromStorage.payloadData, frameFromStorage.payloadSize);
+ _length = frameFromStorage.payloadSize;
+ return VCM_OK;
+}
+
+// Set counted status (as counted by JB or not)
+void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
+{
+ _frameCounted = frameCounted;
+}
+
+bool VCMFrameBuffer::GetCountedFrame()
+{
+ return _frameCounted;
+}
+
+// Get current state of frame
+VCMFrameBufferStateEnum
+VCMFrameBuffer::GetState() const
+{
+ return _state;
+}
+
+// Get current state of frame
+VCMFrameBufferStateEnum
+VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const
+{
+ timeStamp = TimeStamp();
+ return GetState();
+}
+
+bool
+VCMFrameBuffer::IsRetransmitted()
+{
+ return _sessionInfo.IsRetransmitted();
+}
+
+void
+VCMFrameBuffer::PrepareForDecode()
+{
+ _length = _sessionInfo.PrepareForDecode(_buffer, _codec);
+}
+
+}
diff --git a/src/modules/video_coding/main/source/frame_buffer.h b/src/modules/video_coding/main/source/frame_buffer.h
new file mode 100644
index 0000000..65c56a3
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_buffer.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_
+
+#include "typedefs.h"
+#include "module_common_types.h"
+
+#include "encoded_frame.h"
+#include "frame_list.h"
+#include "jitter_buffer_common.h"
+#include "session_info.h"
+
+namespace webrtc
+{
+
+class VCMFrameBuffer : public VCMEncodedFrame
+{
+public:
+ VCMFrameBuffer();
+ virtual ~VCMFrameBuffer();
+
+ VCMFrameBuffer(VCMFrameBuffer& rhs);
+
+ virtual void Reset();
+
+ VCMFrameBufferEnum InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs);
+
+ // State
+ // Get current state of frame
+ VCMFrameBufferStateEnum GetState() const;
+ // Get current state and timestamp of frame
+ VCMFrameBufferStateEnum GetState(WebRtc_UWord32& timeStamp) const;
+ void SetState(VCMFrameBufferStateEnum state); // Set state of frame
+
+ bool IsRetransmitted();
+ bool IsSessionComplete();
+ bool HaveLastPacket();
+ bool ForceSetHaveLastPacket();
+ // Makes sure the session contain a decodable stream.
+ void MakeSessionDecodable();
+
+ // Sequence numbers
+ // Get lowest packet sequence number in frame
+ WebRtc_Word32 GetLowSeqNum();
+ // Get highest packet sequence number in frame
+ WebRtc_Word32 GetHighSeqNum();
+
+ // Get highest sequence number of complete session
+ WebRtc_Word32 GetHighSeqNumComplete();
+
+ // Set counted status (as counted by JB or not)
+ void SetCountedFrame(bool frameCounted);
+ bool GetCountedFrame();
+
+ // NACK
+ // Zero out all entries in list up to and including the entry equal to _lowSeqNum
+ WebRtc_Word32 ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num);
+ // Hybrid extension: only NACK important packets, discard FEC packets
+ WebRtc_Word32 ZeroOutSeqNumHybrid(WebRtc_Word32* list,
+ WebRtc_Word32 num,
+ float rttScore);
+ void IncrementNackCount();
+ WebRtc_Word16 GetNackCount() const;
+
+ WebRtc_Word64 LatestPacketTimeMs();
+
+ webrtc::FrameType FrameType() const;
+ void SetPreviousFrameLoss();
+
+ WebRtc_Word32 ExtractFromStorage(const EncodedVideoData& frameFromStorage);
+
+protected:
+ void RestructureFrameInformation();
+ void PrepareForDecode();
+
+private:
+ VCMFrameBufferStateEnum _state; // Current state of the frame
+ bool _frameCounted; // If this frame has been counted by JB
+ VCMSessionInfo _sessionInfo;
+ WebRtc_UWord16 _nackCount;
+ WebRtc_Word64 _latestPacketTimeMs;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_
diff --git a/src/modules/video_coding/main/source/frame_dropper.cc b/src/modules/video_coding/main/source/frame_dropper.cc
new file mode 100644
index 0000000..065e452
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_dropper.cc
@@ -0,0 +1,331 @@
+/*
+ * 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 "frame_dropper.h"
+#include "internal_defines.h"
+#include "trace.h"
+
+namespace webrtc
+{
+
+VCMFrameDropper::VCMFrameDropper(WebRtc_Word32 vcmId)
+:
+_vcmId(vcmId),
+_keyFrameSizeAvgKbits(0.9f),
+_keyFrameRatio(0.99f),
+_dropRatio(0.9f, 0.96f)
+{
+ Reset();
+}
+
+void
+VCMFrameDropper::Reset()
+{
+ _keyFrameRatio.Reset(0.99f);
+ _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps
+ _keyFrameSizeAvgKbits.Reset(0.9f);
+ _keyFrameCount = 0;
+ _accumulator = 0.0f;
+ _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window
+ _targetBitRate = 300.0f;
+ _userFrameRate = 30;
+ _keyFrameSpreadFrames = 0.5f * _userFrameRate;
+ _dropNext = false;
+ _dropRatio.Reset(0.9f);
+ _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0
+ _dropCount = 0;
+ _windowSize = 0.5f;
+ _wasBelowMax = true;
+ _enabled = true;
+ _fastMode = false; // start with normal (non-aggressive) mode
+}
+
+void
+VCMFrameDropper::Enable(bool enable)
+{
+ _enabled = enable;
+}
+
+void
+VCMFrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame)
+{
+ if (!_enabled)
+ {
+ return;
+ }
+ float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f;
+ if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different
+ {
+ _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits);
+ _keyFrameRatio.Apply(1.0, 1.0);
+ if (frameSizeKbits > _keyFrameSizeAvgKbits.Value())
+ {
+ // Remove the average key frame size since we
+ // compensate for key frames when adding delta
+ // frames.
+ frameSizeKbits -= _keyFrameSizeAvgKbits.Value();
+ }
+ else
+ {
+ // Shouldn't be negative, so zero is the lower bound.
+ frameSizeKbits = 0;
+ }
+ if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
+ {
+ // We are sending key frames more often than our upper bound for
+ // how much we allow the key frame compensation to be spread
+ // out in time. Therefor we must use the key frame ratio rather
+ // than keyFrameSpreadFrames.
+ _keyFrameCount = static_cast<WebRtc_Word32>(1 / _keyFrameRatio.Value() + 0.5);
+ }
+ else
+ {
+ // Compensate for the key frame the following frames
+ _keyFrameCount = static_cast<WebRtc_Word32>(_keyFrameSpreadFrames + 0.5);
+ }
+ }
+ else
+ {
+ // Decrease the keyFrameRatio
+ _keyFrameRatio.Apply(1.0, 0.0);
+ }
+ // Change the level of the accumulator (bucket)
+ _accumulator += frameSizeKbits;
+}
+
+void
+VCMFrameDropper::Leak(WebRtc_UWord32 inputFrameRate)
+{
+ if (!_enabled)
+ {
+ return;
+ }
+ if (inputFrameRate < 1)
+ {
+ return;
+ }
+ if (_targetBitRate < 0.0f)
+ {
+ return;
+ }
+ _keyFrameSpreadFrames = 0.5f * inputFrameRate;
+ // T is the expected bits per frame (target). If all frames were the same size,
+ // we would get T bits per frame. Notice that T is also weighted to be able to
+ // force a lower frame rate if wanted.
+ float T = _targetBitRate / inputFrameRate;
+ if (_keyFrameCount > 0)
+ {
+ // Perform the key frame compensation
+ if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
+ {
+ T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value();
+ }
+ else
+ {
+ T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames;
+ }
+ _keyFrameCount--;
+ }
+ _accumulator -= T;
+ UpdateRatio();
+
+}
+
+void
+VCMFrameDropper::UpdateNack(WebRtc_UWord32 nackBytes)
+{
+ if (!_enabled)
+ {
+ return;
+ }
+ _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
+}
+
+void
+VCMFrameDropper::FillBucket(float inKbits, float outKbits)
+{
+ _accumulator += (inKbits - outKbits);
+}
+
+void
+VCMFrameDropper::UpdateRatio()
+{
+ if (_accumulator > 1.3f * _accumulatorMax)
+ {
+ // Too far above accumulator max, react faster
+ _dropRatio.UpdateBase(0.8f);
+ }
+ else
+ {
+ // Go back to normal reaction
+ _dropRatio.UpdateBase(0.9f);
+ }
+ if (_accumulator > _accumulatorMax)
+ {
+ // We are above accumulator max, and should ideally
+ // drop a frame. Increase the dropRatio and drop
+ // the frame later.
+ if (_wasBelowMax)
+ {
+ _dropNext = true;
+ }
+ if (_fastMode)
+ {
+ // always drop in aggressive mode
+ _dropNext = true;
+ }
+
+ _dropRatio.Apply(1.0f, 1.0f);
+ _dropRatio.UpdateBase(0.9f);
+ }
+ else
+ {
+ _dropRatio.Apply(1.0f, 0.0f);
+ }
+ if (_accumulator < 0.0f)
+ {
+ _accumulator = 0.0f;
+ }
+ _wasBelowMax = _accumulator < _accumulatorMax;
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId), "FrameDropper: dropRatio = %f accumulator = %f, accumulatorMax = %f", _dropRatio.Value(), _accumulator, _accumulatorMax);
+}
+
+// This function signals when to drop frames to the caller. It makes use of the dropRatio
+// to smooth out the drops over time.
+bool
+VCMFrameDropper::DropFrame()
+{
+ if (!_enabled)
+ {
+ return false;
+ }
+ if (_dropNext)
+ {
+ _dropNext = false;
+ _dropCount = 0;
+ }
+
+ if (_dropRatio.Value() >= 0.5f) // Drops per keep
+ {
+ // limit is the number of frames we should drop between each kept frame
+ // to keep our drop ratio. limit is positive in this case.
+ float denom = 1.0f - _dropRatio.Value();
+ if (denom < 1e-5)
+ {
+ denom = (float)1e-5;
+ }
+ WebRtc_Word32 limit = static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
+ if (_dropCount < 0)
+ {
+ // Reset the _dropCount since it was negative and should be positive.
+ if (_dropRatio.Value() > 0.4f)
+ {
+ _dropCount = -_dropCount;
+ }
+ else
+ {
+ _dropCount = 0;
+ }
+ }
+ if (_dropCount < limit)
+ {
+ // As long we are below the limit we should drop frames.
+ _dropCount++;
+ return true;
+ }
+ else
+ {
+ // Only when we reset _dropCount a frame should be kept.
+ _dropCount = 0;
+ return false;
+ }
+ }
+ else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop
+ {
+ // limit is the number of frames we should keep between each drop
+ // in order to keep the drop ratio. limit is negative in this case,
+ // and the _dropCount is also negative.
+ float denom = _dropRatio.Value();
+ if (denom < 1e-5)
+ {
+ denom = (float)1e-5;
+ }
+ WebRtc_Word32 limit = -static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
+ if (_dropCount > 0)
+ {
+ // Reset the _dropCount since we have a positive
+ // _dropCount, and it should be negative.
+ if (_dropRatio.Value() < 0.6f)
+ {
+ _dropCount = -_dropCount;
+ }
+ else
+ {
+ _dropCount = 0;
+ }
+ }
+ if (_dropCount > limit)
+ {
+ if (_dropCount == 0)
+ {
+ // Drop frames when we reset _dropCount.
+ _dropCount--;
+ return true;
+ }
+ else
+ {
+ // Keep frames as long as we haven't reached limit.
+ _dropCount--;
+ return false;
+ }
+ }
+ else
+ {
+ _dropCount = 0;
+ return false;
+ }
+ }
+ _dropCount = 0;
+ return false;
+
+ // A simpler version, unfiltered and quicker
+ //bool dropNext = _dropNext;
+ //_dropNext = false;
+ //return dropNext;
+}
+
+void
+VCMFrameDropper::SetRates(float bitRate, float userFrameRate)
+{
+ // Bit rate of -1 means infinite bandwidth.
+ _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds)
+ if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax)
+ {
+ // Rescale the accumulator level if the accumulator max decreases
+ _accumulator = bitRate / _targetBitRate * _accumulator;
+ }
+ _targetBitRate = bitRate;
+ if (userFrameRate > 0.0f)
+ {
+ _userFrameRate = userFrameRate;
+ }
+}
+
+float
+VCMFrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const
+{
+ if (!_enabled)
+ {
+ return static_cast<float>(inputFrameRate);
+ }
+ return inputFrameRate * (1.0f - _dropRatio.Value());
+}
+
+}
diff --git a/src/modules/video_coding/main/source/frame_dropper.h b/src/modules/video_coding/main/source/frame_dropper.h
new file mode 100644
index 0000000..5e7e8a1
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_dropper.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_
+
+#include "exp_filter.h"
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+/******************************/
+/* VCMFrameDropper class */
+/****************************/
+// The Frame Dropper implements a variant of the leaky bucket algorithm
+// for keeping track of when to drop frames to avoid bit rate
+// over use when the encoder can't keep its bit rate.
+class VCMFrameDropper
+{
+public:
+ VCMFrameDropper(WebRtc_Word32 vcmId = 0);
+ // Resets the FrameDropper to its initial state.
+ // This means that the frameRateWeight is set to its
+ // default value as well.
+ void Reset();
+
+ void Enable(bool enable);
+ // Answers the question if it's time to drop a frame
+ // if we want to reach a given frame rate. Must be
+ // called for every frame.
+ //
+ // Return value : True if we should drop the current frame
+ bool DropFrame();
+ // Updates the FrameDropper with the size of the latest encoded
+ // frame. The FrameDropper calculates a new drop ratio (can be
+ // seen as the probability to drop a frame) and updates its
+ // internal statistics.
+ //
+ // Input:
+ // - frameSizeBytes : The size of the latest frame
+ // returned from the encoder.
+ // - deltaFrame : True if the encoder returned
+ // a key frame.
+ void Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame);
+
+ void Leak(WebRtc_UWord32 inputFrameRate);
+
+ void UpdateNack(WebRtc_UWord32 nackBytes);
+
+ // Sets the target bit rate and the frame rate produced by
+ // the camera.
+ //
+ // Input:
+ // - bitRate : The target bit rate
+ void SetRates(float bitRate, float userFrameRate);
+
+ // Return value : The current average frame rate produced
+ // if the DropFrame() function is used as
+ // instruction of when to drop frames.
+ float ActualFrameRate(WebRtc_UWord32 inputFrameRate) const;
+
+private:
+ void FillBucket(float inKbits, float outKbits);
+ void UpdateRatio();
+
+ WebRtc_Word32 _vcmId;
+ VCMExpFilter _keyFrameSizeAvgKbits;
+ VCMExpFilter _keyFrameRatio;
+ float _keyFrameSpreadFrames;
+ WebRtc_Word32 _keyFrameCount;
+ float _accumulator;
+ float _accumulatorMax;
+ float _targetBitRate;
+ bool _dropNext;
+ VCMExpFilter _dropRatio;
+ WebRtc_Word32 _dropCount;
+ float _windowSize;
+ float _userFrameRate;
+ bool _wasBelowMax;
+ bool _enabled;
+ bool _fastMode;
+}; // end of VCMFrameDropper class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_
diff --git a/src/modules/video_coding/main/source/frame_list.cc b/src/modules/video_coding/main/source/frame_list.cc
new file mode 100644
index 0000000..e79dc91
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_list.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 "frame_list.h"
+#include "frame_buffer.h"
+#include "jitter_buffer.h"
+#include <cstdlib>
+
+namespace webrtc {
+
+VCMFrameListTimestampOrderAsc::~VCMFrameListTimestampOrderAsc()
+{
+ Flush();
+}
+
+void
+VCMFrameListTimestampOrderAsc::Flush()
+{
+ while(Erase(First()) != -1) { }
+}
+
+// Inserts frame in timestamp order, with the oldest timestamp first. Takes wrap arounds into account
+WebRtc_Word32
+VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame)
+{
+ VCMFrameListItem* item = static_cast<VCMFrameListItem*>(First());
+ VCMFrameListItem* newItem = new VCMFrameListItem(frame);
+ bool inserted = false;
+ if (newItem == NULL)
+ {
+ return -1;
+ }
+ while (item != NULL)
+ {
+ const WebRtc_UWord32 itemTimestamp = item->GetItem()->TimeStamp();
+ if (VCMJitterBuffer::LatestTimestamp(itemTimestamp, frame->TimeStamp()) == itemTimestamp)
+ {
+ if (InsertBefore(item, newItem) < 0)
+ {
+ delete newItem;
+ return -1;
+ }
+ inserted = true;
+ break;
+ }
+ item = Next(item);
+ }
+ if (!inserted && ListWrapper::Insert(ListWrapper::Last(), newItem) < 0)
+ {
+ delete newItem;
+ return -1;
+ }
+ return 0;
+}
+
+VCMFrameBuffer*
+VCMFrameListTimestampOrderAsc::FirstFrame() const
+{
+ VCMFrameListItem* item = First();
+ if (item != NULL)
+ {
+ return item->GetItem();
+ }
+ return NULL;
+}
+
+VCMFrameListItem*
+VCMFrameListTimestampOrderAsc::FindFrameListItem(FindFrameCriteria criteria,
+ const void* compareWith,
+ VCMFrameListItem* startItem) const
+{
+ if (startItem == NULL)
+ {
+ startItem = First();
+ }
+ if (criteria == NULL)
+ {
+ return NULL;
+ }
+ while (startItem != NULL)
+ {
+ if (criteria(startItem->GetItem(), compareWith))
+ {
+ return startItem;
+ }
+ startItem = Next(startItem);
+ }
+ // No frame found
+ return NULL;
+}
+
+VCMFrameBuffer*
+VCMFrameListTimestampOrderAsc::FindFrame(FindFrameCriteria criteria,
+ const void* compareWith,
+ VCMFrameListItem* startItem) const
+{
+ const VCMFrameListItem* frameListItem = FindFrameListItem(criteria, compareWith, startItem);
+ if (frameListItem == NULL)
+ {
+ return NULL;
+ }
+ return frameListItem->GetItem();
+}
+
+}
+
diff --git a/src/modules/video_coding/main/source/frame_list.h b/src/modules/video_coding/main/source/frame_list.h
new file mode 100644
index 0000000..cc5d053
--- /dev/null
+++ b/src/modules/video_coding/main/source/frame_list.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_
+
+#include "list_wrapper.h"
+#include "typedefs.h"
+#include <stdlib.h>
+
+namespace webrtc
+{
+
+class VCMFrameBuffer;
+
+typedef bool (*FindFrameCriteria)(VCMFrameBuffer*, const void*);
+
+class VCMFrameListItem : public ListItem
+{
+ friend class VCMFrameListTimestampOrderAsc;
+public:
+ VCMFrameListItem(const VCMFrameBuffer* ptr) : ListItem(ptr) {}
+ ~VCMFrameListItem() {};
+
+ VCMFrameBuffer* GetItem() const
+ { return static_cast<VCMFrameBuffer*>(ListItem::GetItem()); }
+};
+
+class VCMFrameListTimestampOrderAsc : public ListWrapper
+{
+public:
+ VCMFrameListTimestampOrderAsc() : ListWrapper() {};
+ ~VCMFrameListTimestampOrderAsc();
+
+ void Flush();
+
+ // Inserts frame in timestamp order, with the oldest timestamp first.
+ // Takes wrap arounds into account.
+ WebRtc_Word32 Insert(VCMFrameBuffer* frame);
+ VCMFrameBuffer* FirstFrame() const;
+ VCMFrameListItem* Next(VCMFrameListItem* item) const
+ { return static_cast<VCMFrameListItem*>(ListWrapper::Next(item)); }
+ VCMFrameListItem* Previous(VCMFrameListItem* item) const
+ { return static_cast<VCMFrameListItem*>(ListWrapper::Previous(item)); }
+ VCMFrameListItem* First() const
+ { return static_cast<VCMFrameListItem*>(ListWrapper::First()); }
+ VCMFrameListItem* Last() const
+ { return static_cast<VCMFrameListItem*>(ListWrapper::Last()); }
+ VCMFrameListItem* FindFrameListItem(FindFrameCriteria criteria,
+ const void* compareWith = NULL,
+ VCMFrameListItem* startItem = NULL) const;
+ VCMFrameBuffer* FindFrame(FindFrameCriteria criteria,
+ const void* compareWith = NULL,
+ VCMFrameListItem* startItem = NULL) const;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_
diff --git a/src/modules/video_coding/main/source/generic_decoder.cc b/src/modules/video_coding/main/source/generic_decoder.cc
new file mode 100644
index 0000000..1fddb52
--- /dev/null
+++ b/src/modules/video_coding/main/source/generic_decoder.cc
@@ -0,0 +1,203 @@
+/*
+ * 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 "video_coding.h"
+#include "trace.h"
+#include "generic_decoder.h"
+#include "internal_defines.h"
+#include "tick_time.h"
+
+namespace webrtc {
+
+VCMDecodedFrameCallback::VCMDecodedFrameCallback(VCMTiming& timing)
+:
+_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_receiveCallback(NULL),
+_timing(timing),
+_timestampMap(kDecoderFrameMemoryLength)
+{
+}
+
+VCMDecodedFrameCallback::~VCMDecodedFrameCallback()
+{
+ delete &_critSect;
+}
+
+void VCMDecodedFrameCallback::SetUserReceiveCallback(VCMReceiveCallback* receiveCallback)
+{
+ CriticalSectionScoped cs(_critSect);
+ _receiveCallback = receiveCallback;
+}
+
+WebRtc_Word32 VCMDecodedFrameCallback::Decoded(RawImage& decodedImage)
+{
+ CriticalSectionScoped cs(_critSect);
+ VCMFrameInformation* frameInfo = static_cast<VCMFrameInformation*>(_timestampMap.Pop(decodedImage._timeStamp));
+ if (frameInfo == NULL)
+ {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ WebRtc_Word32 ret = _timing.StopDecodeTimer(decodedImage._timeStamp, frameInfo->decodeStartTimeMs, VCMTickTime::MillisecondTimestamp());
+
+ if (_receiveCallback != NULL)
+ {
+ _frame.Swap(decodedImage._buffer, decodedImage._length, decodedImage._size);
+ _frame.SetWidth(decodedImage._width);
+ _frame.SetHeight(decodedImage._height);
+ _frame.SetTimeStamp(decodedImage._timeStamp);
+ _frame.SetRenderTime(frameInfo->renderTimeMs);
+ // Convert raw image to video frame
+ WebRtc_Word32 callbackReturn = _receiveCallback->FrameToRender(_frame);
+ if (callbackReturn < 0)
+ {
+ return callbackReturn;
+ }
+ }
+ if (ret < 0)
+ {
+ return ret;
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebRtc_Word32
+VCMDecodedFrameCallback::ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId)
+{
+ CriticalSectionScoped cs(_critSect);
+ if (_receiveCallback != NULL)
+ {
+ return _receiveCallback->ReceivedDecodedReferenceFrame(pictureId);
+ }
+ return -1;
+}
+
+WebRtc_Word32
+VCMDecodedFrameCallback::ReceivedDecodedFrame(const WebRtc_UWord64 pictureId)
+{
+ _lastReceivedPictureID = pictureId;
+ return 0;
+}
+
+WebRtc_UWord64 VCMDecodedFrameCallback::LastReceivedPictureID() const
+{
+ return _lastReceivedPictureID;
+}
+
+WebRtc_Word32 VCMDecodedFrameCallback::Map(WebRtc_UWord32 timestamp, VCMFrameInformation* frameInfo)
+{
+ CriticalSectionScoped cs(_critSect);
+ return _timestampMap.Add(timestamp, frameInfo);
+}
+
+WebRtc_Word32 VCMDecodedFrameCallback::Pop(WebRtc_UWord32 timestamp)
+{
+ CriticalSectionScoped cs(_critSect);
+ if (_timestampMap.Pop(timestamp) == NULL)
+ {
+ return VCM_GENERAL_ERROR;
+ }
+ return VCM_OK;
+}
+
+VCMGenericDecoder::VCMGenericDecoder(VideoDecoder& decoder, WebRtc_Word32 id, bool isExternal)
+:
+_id(id),
+_callback(NULL),
+_frameInfos(),
+_nextFrameInfoIdx(0),
+_decoder(decoder),
+_codecType(kVideoCodecUnknown),
+_isExternal(isExternal),
+_requireKeyFrame(false),
+_keyFrameDecoded(false)
+{
+}
+
+VCMGenericDecoder::~VCMGenericDecoder()
+{
+}
+
+WebRtc_Word32 VCMGenericDecoder::InitDecode(const VideoCodec* settings, WebRtc_Word32 numberOfCores, bool requireKeyFrame)
+{
+ _requireKeyFrame = requireKeyFrame;
+ _keyFrameDecoded = false;
+ _codecType = settings->codecType;
+
+ return _decoder.InitDecode(settings, numberOfCores);
+}
+
+WebRtc_Word32 VCMGenericDecoder::Decode(const VCMEncodedFrame& frame)
+{
+ if (_requireKeyFrame &&
+ !_keyFrameDecoded &&
+ frame.FrameType() != kVideoFrameKey &&
+ frame.FrameType() != kVideoFrameGolden)
+ {
+ // Require key frame is enabled, meaning that one key frame must be decoded
+ // before we can decode delta frames.
+ return VCM_CODEC_ERROR;
+ }
+ _frameInfos[_nextFrameInfoIdx].decodeStartTimeMs = VCMTickTime::MillisecondTimestamp();
+ _frameInfos[_nextFrameInfoIdx].renderTimeMs = frame.RenderTimeMs();
+ _callback->Map(frame.TimeStamp(), &_frameInfos[_nextFrameInfoIdx]);
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Decoding timestamp %u",
+ frame.TimeStamp());
+
+ _nextFrameInfoIdx = (_nextFrameInfoIdx + 1) % kDecoderFrameMemoryLength;
+
+ WebRtc_Word32 ret = _decoder.Decode(frame.EncodedImage(),
+ frame.MissingFrame(),
+ frame.CodecSpecificInfo(),
+ frame.RenderTimeMs());
+
+ if (ret < WEBRTC_VIDEO_CODEC_OK)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), "Decoder error: %d\n", ret);
+ _callback->Pop(frame.TimeStamp());
+ return ret;
+ }
+ // Update the key frame decoded variable so that we know whether or not we've decoded a key frame since reset.
+ _keyFrameDecoded = (frame.FrameType() == kVideoFrameKey || frame.FrameType() == kVideoFrameGolden);
+ return ret;
+}
+
+WebRtc_Word32
+VCMGenericDecoder::Release()
+{
+ _keyFrameDecoded = false;
+ return _decoder.Release();
+}
+
+WebRtc_Word32 VCMGenericDecoder::Reset()
+{
+ _keyFrameDecoded = false;
+ return _decoder.Reset();
+}
+
+WebRtc_Word32 VCMGenericDecoder::SetCodecConfigParameters(const WebRtc_UWord8* buffer, WebRtc_Word32 size)
+{
+ return _decoder.SetCodecConfigParameters(buffer, size);
+}
+
+WebRtc_Word32 VCMGenericDecoder::RegisterDecodeCompleteCallback(VCMDecodedFrameCallback* callback)
+{
+ _callback = callback;
+ return _decoder.RegisterDecodeCompleteCallback(callback);
+}
+
+bool VCMGenericDecoder::External() const
+{
+ return _isExternal;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/generic_decoder.h b/src/modules/video_coding/main/source/generic_decoder.h
new file mode 100644
index 0000000..9569055
--- /dev/null
+++ b/src/modules/video_coding/main/source/generic_decoder.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_
+
+#include "timing.h"
+#include "timestamp_map.h"
+#include "video_codec_interface.h"
+#include "encoded_frame.h"
+#include "module_common_types.h"
+
+namespace webrtc
+{
+
+class VCMReceiveCallback;
+
+enum { kDecoderFrameMemoryLength = 10 };
+
+struct VCMFrameInformation
+{
+ WebRtc_Word64 renderTimeMs;
+ WebRtc_Word64 decodeStartTimeMs;
+ void* userData;
+};
+
+class VCMDecodedFrameCallback : public DecodedImageCallback
+{
+public:
+ VCMDecodedFrameCallback(VCMTiming& timing);
+ virtual ~VCMDecodedFrameCallback();
+ void SetUserReceiveCallback(VCMReceiveCallback* receiveCallback);
+
+ virtual WebRtc_Word32 Decoded(RawImage& decodedImage);
+ virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId);
+ virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId);
+
+ WebRtc_UWord64 LastReceivedPictureID() const;
+
+ WebRtc_Word32 Map(WebRtc_UWord32 timestamp, VCMFrameInformation* frameInfo);
+ WebRtc_Word32 Pop(WebRtc_UWord32 timestamp);
+
+private:
+ CriticalSectionWrapper& _critSect;
+ VideoFrame _frame;
+ VCMReceiveCallback* _receiveCallback;
+ VCMTiming& _timing;
+ VCMTimestampMap _timestampMap;
+ WebRtc_UWord64 _lastReceivedPictureID;
+};
+
+
+class VCMGenericDecoder
+{
+ friend class VCMCodecDataBase;
+public:
+ VCMGenericDecoder(VideoDecoder& decoder, WebRtc_Word32 id = 0, bool isExternal = false);
+ ~VCMGenericDecoder();
+
+ /**
+ * Initialize the decoder with the information from the VideoCodec
+ */
+ WebRtc_Word32 InitDecode(const VideoCodec* settings,
+ WebRtc_Word32 numberOfCores,
+ bool requireKeyFrame);
+
+ /**
+ * Decode to a raw I420 frame,
+ *
+ * inputVideoBuffer reference to encoded video frame
+ */
+ WebRtc_Word32 Decode(const VCMEncodedFrame& inputFrame);
+
+ /**
+ * Free the decoder memory
+ */
+ WebRtc_Word32 Release();
+
+ /**
+ * Reset the decoder state, prepare for a new call
+ */
+ WebRtc_Word32 Reset();
+
+ /**
+ * Codec configuration data sent out-of-band, i.e. in SIP call setup
+ *
+ * buffer pointer to the configuration data
+ * size the size of the configuration data in bytes
+ */
+ WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/,
+ WebRtc_Word32 /*size*/);
+
+ WebRtc_Word32 RegisterDecodeCompleteCallback(VCMDecodedFrameCallback* callback);
+
+ bool External() const;
+
+protected:
+
+ WebRtc_Word32 _id;
+ VCMDecodedFrameCallback* _callback;
+ VCMFrameInformation _frameInfos[kDecoderFrameMemoryLength];
+ WebRtc_UWord32 _nextFrameInfoIdx;
+ VideoDecoder& _decoder;
+ VideoCodecType _codecType;
+ bool _isExternal;
+ bool _requireKeyFrame;
+ bool _keyFrameDecoded;
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_
diff --git a/src/modules/video_coding/main/source/generic_encoder.cc b/src/modules/video_coding/main/source/generic_encoder.cc
new file mode 100644
index 0000000..eeb3f32
--- /dev/null
+++ b/src/modules/video_coding/main/source/generic_encoder.cc
@@ -0,0 +1,260 @@
+/*
+ * 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 "encoded_frame.h"
+#include "generic_encoder.h"
+#include "media_optimization.h"
+#include "../../../../engine_configurations.h"
+
+namespace webrtc {
+
+//#define DEBUG_ENCODER_BIT_STREAM
+
+VCMGenericEncoder::VCMGenericEncoder(VideoEncoder& encoder, bool internalSource /*= false*/)
+:
+_encoder(encoder),
+_codecType(kVideoCodecUnknown),
+_VCMencodedFrameCallback(NULL),
+_bitRate(0),
+_frameRate(0),
+_internalSource(false)
+{
+}
+
+
+VCMGenericEncoder::~VCMGenericEncoder()
+{
+}
+
+WebRtc_Word32
+VCMGenericEncoder::Reset()
+{
+ _bitRate = 0;
+ _frameRate = 0;
+ _VCMencodedFrameCallback = NULL;
+ return _encoder.Reset();
+}
+
+WebRtc_Word32 VCMGenericEncoder::Release()
+{
+ _bitRate = 0;
+ _frameRate = 0;
+ _VCMencodedFrameCallback = NULL;
+ return _encoder.Release();
+}
+
+WebRtc_Word32
+VCMGenericEncoder::InitEncode(const VideoCodec* settings, WebRtc_Word32 numberOfCores, WebRtc_UWord32 maxPayloadSize)
+{
+ _bitRate = settings->startBitrate;
+ _frameRate = settings->maxFramerate;
+ _codecType = settings->codecType;
+ if (_VCMencodedFrameCallback != NULL)
+ {
+ _VCMencodedFrameCallback->SetCodecType(_codecType);
+ }
+ return _encoder.InitEncode(settings, numberOfCores, maxPayloadSize);
+}
+
+WebRtc_Word32
+VCMGenericEncoder::Encode(const VideoFrame& inputFrame,
+ const CodecSpecificInfo* codecSpecificInfo,
+ FrameType frameType)
+{
+ RawImage rawImage(inputFrame.Buffer(), inputFrame.Length(), inputFrame.Size());
+ rawImage._width = inputFrame.Width();
+ rawImage._height = inputFrame.Height();
+ rawImage._timeStamp = inputFrame.TimeStamp();
+
+ WebRtc_Word32 ret = _encoder.Encode(rawImage, codecSpecificInfo, VCMEncodedFrame::ConvertFrameType(frameType));
+
+ return ret;
+}
+
+WebRtc_Word32
+VCMGenericEncoder::SetPacketLoss(WebRtc_Word32 packetLoss)
+{
+ return _encoder.SetPacketLoss(packetLoss);
+}
+
+WebRtc_Word32
+VCMGenericEncoder::SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate)
+{
+ WebRtc_Word32 ret = _encoder.SetRates(newBitRate, frameRate);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ _bitRate = newBitRate;
+ _frameRate = frameRate;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMGenericEncoder::CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size)
+{
+ WebRtc_Word32 ret = _encoder.CodecConfigParameters(buffer, size);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ return ret;
+}
+
+WebRtc_UWord32 VCMGenericEncoder::BitRate() const
+{
+ return _bitRate;
+}
+
+WebRtc_UWord32 VCMGenericEncoder::FrameRate() const
+{
+ return _frameRate;
+}
+
+WebRtc_Word32
+VCMGenericEncoder::SetPeriodicKeyFrames(bool enable)
+{
+ return _encoder.SetPeriodicKeyFrames(enable);
+}
+
+WebRtc_Word32
+VCMGenericEncoder::RequestFrame(FrameType frameType)
+{
+ RawImage image;
+ return _encoder.Encode(image, NULL, VCMEncodedFrame::ConvertFrameType(frameType));
+}
+
+WebRtc_Word32
+VCMGenericEncoder::RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback)
+{
+ _VCMencodedFrameCallback = VCMencodedFrameCallback;
+
+ _VCMencodedFrameCallback->SetCodecType(_codecType);
+ _VCMencodedFrameCallback->SetInternalSource(_internalSource);
+ return _encoder.RegisterEncodeCompleteCallback(_VCMencodedFrameCallback);
+}
+
+bool
+VCMGenericEncoder::InternalSource() const
+{
+ return _internalSource;
+}
+
+ /***************************
+ * Callback Implementation
+ ***************************/
+VCMEncodedFrameCallback::VCMEncodedFrameCallback():
+_sendCallback(),
+_encodedBytes(0),
+_payloadType(0),
+_bitStreamAfterEncoder(NULL)
+{
+#ifdef DEBUG_ENCODER_BIT_STREAM
+ _bitStreamAfterEncoder = fopen("encoderBitStream.bit", "wb");
+#endif
+}
+
+VCMEncodedFrameCallback::~VCMEncodedFrameCallback()
+{
+#ifdef DEBUG_ENCODER_BIT_STREAM
+ fclose(_bitStreamAfterEncoder);
+#endif
+}
+
+WebRtc_Word32
+VCMEncodedFrameCallback::SetTransportCallback(VCMPacketizationCallback* transport)
+{
+ _sendCallback = transport;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMEncodedFrameCallback::Encoded(
+ EncodedImage &encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragmentationHeader)
+{
+ FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
+
+ WebRtc_UWord32 encodedBytes = 0;
+ if (_sendCallback != NULL)
+ {
+ encodedBytes = encodedImage._length;
+
+ if (_bitStreamAfterEncoder != NULL)
+ {
+ fwrite(encodedImage._buffer, 1, encodedImage._length, _bitStreamAfterEncoder);
+ }
+
+ RTPVideoTypeHeader rtpTypeHeader;
+ RTPVideoTypeHeader* rtpTypeHeaderPtr = &rtpTypeHeader;
+ if (codecSpecificInfo)
+ {
+ CopyCodecSpecific(*codecSpecificInfo, &rtpTypeHeaderPtr);
+ }
+ else
+ {
+ rtpTypeHeaderPtr = NULL;
+ }
+
+ WebRtc_Word32 callbackReturn = _sendCallback->SendData(
+ frameType,
+ _payloadType,
+ encodedImage._timeStamp,
+ encodedImage._buffer,
+ encodedBytes,
+ *fragmentationHeader,
+ rtpTypeHeaderPtr);
+ if (callbackReturn < 0)
+ {
+ return callbackReturn;
+ }
+ }
+ else
+ {
+ return VCM_UNINITIALIZED;
+ }
+ _encodedBytes = encodedBytes;
+ _mediaOpt->UpdateWithEncodedData(_encodedBytes, frameType);
+ if (_internalSource)
+ {
+ return _mediaOpt->DropFrame(); // Signal to encoder to drop next frame
+ }
+
+ return VCM_OK;
+}
+
+WebRtc_UWord32
+VCMEncodedFrameCallback::EncodedBytes()
+{
+ return _encodedBytes;
+}
+
+void
+VCMEncodedFrameCallback::SetMediaOpt(VCMMediaOptimization *mediaOpt)
+{
+ _mediaOpt = mediaOpt;
+}
+
+void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
+ RTPVideoTypeHeader** rtp) {
+ switch (info.codecType)
+ {
+ //TODO(hlundin): Implement case for kVideoCodecVP8.
+ default: {
+ // No codec specific info. Change RTP header pointer to NULL.
+ *rtp = NULL;
+ return;
+ }
+
+ }
+}
+
+} // namespace webrtc
diff --git a/src/modules/video_coding/main/source/generic_encoder.h b/src/modules/video_coding/main/source/generic_encoder.h
new file mode 100644
index 0000000..26d19df
--- /dev/null
+++ b/src/modules/video_coding/main/source/generic_encoder.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_
+
+#include "video_codec_interface.h"
+
+#include <stdio.h>
+
+namespace webrtc
+{
+
+class VCMMediaOptimization;
+
+/*************************************/
+/* VCMEncodeFrameCallback class */
+/***********************************/
+class VCMEncodedFrameCallback : public EncodedImageCallback
+{
+public:
+ VCMEncodedFrameCallback();
+ virtual ~VCMEncodedFrameCallback();
+
+ /*
+ * Callback implementation - codec encode complete
+ */
+ WebRtc_Word32 Encoded(
+ EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo = NULL,
+ const RTPFragmentationHeader* fragmentationHeader = NULL);
+ /*
+ * Get number of encoded bytes
+ */
+ WebRtc_UWord32 EncodedBytes();
+ /*
+ * Callback implementation - generic encoder encode complete
+ */
+ WebRtc_Word32 SetTransportCallback(VCMPacketizationCallback* transport);
+ /**
+ * Set media Optimization
+ */
+ void SetMediaOpt (VCMMediaOptimization* mediaOpt);
+
+ void SetPayloadType(WebRtc_UWord8 payloadType) { _payloadType = payloadType; };
+ void SetCodecType(VideoCodecType codecType) {_codecType = codecType;};
+ void SetInternalSource(bool internalSource) { _internalSource = internalSource; };
+
+private:
+ /*
+ * Map information from info into rtp. If no relevant information is found
+ * in info, rtp is set to NULL.
+ */
+ static void CopyCodecSpecific(const CodecSpecificInfo& info,
+ RTPVideoTypeHeader** rtp);
+
+ VCMPacketizationCallback* _sendCallback;
+ VCMMediaOptimization* _mediaOpt;
+ WebRtc_UWord32 _encodedBytes;
+ WebRtc_UWord8 _payloadType;
+ VideoCodecType _codecType;
+ bool _internalSource;
+ FILE* _bitStreamAfterEncoder;
+};// end of VCMEncodeFrameCallback class
+
+
+/******************************/
+/* VCMGenericEncoder class */
+/******************************/
+class VCMGenericEncoder
+{
+ friend class VCMCodecDataBase;
+public:
+ VCMGenericEncoder(VideoEncoder& encoder, bool internalSource = false);
+ ~VCMGenericEncoder();
+ /**
+ * Reset the encoder state, prepare for a new call
+ */
+ WebRtc_Word32 Reset();
+ /**
+ * Free encoder memory
+ */
+ WebRtc_Word32 Release();
+ /**
+ * Initialize the encoder with the information from the VideoCodec
+ */
+ WebRtc_Word32 InitEncode(const VideoCodec* settings,
+ WebRtc_Word32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize);
+ /**
+ * Encode raw image
+ * inputFrame : Frame containing raw image
+ * codecSpecificInfo : Specific codec data
+ * cameraFrameRate : request or information from the remote side
+ * frameType : The requested frame type to encode
+ */
+ WebRtc_Word32 Encode(const VideoFrame& inputFrame,
+ const CodecSpecificInfo* codecSpecificInfo,
+ FrameType frameType);
+ /**
+ * Set new target bit rate and frame rate
+ * Return Value: new bit rate if OK, otherwise <0s
+ */
+ WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate);
+ /**
+ * Set a new packet loss rate
+ */
+ WebRtc_Word32 SetPacketLoss(WebRtc_Word32 packetLoss);
+ WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size);
+ /**
+ * Register a transport callback which will be called to deliver the encoded buffers
+ */
+ WebRtc_Word32 RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback);
+ /**
+ * Get encoder bit rate
+ */
+ WebRtc_UWord32 BitRate() const;
+ /**
+ * Get encoder frame rate
+ */
+ WebRtc_UWord32 FrameRate() const;
+
+ WebRtc_Word32 SetPeriodicKeyFrames(bool enable);
+
+ WebRtc_Word32 RequestFrame(FrameType frameType);
+
+ bool InternalSource() const;
+
+private:
+ VideoEncoder& _encoder;
+ VideoCodecType _codecType;
+ VCMEncodedFrameCallback* _VCMencodedFrameCallback;
+ WebRtc_UWord32 _bitRate;
+ WebRtc_UWord32 _frameRate;
+ bool _internalSource;
+}; // end of VCMGenericEncoder class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_
diff --git a/src/modules/video_coding/main/source/inter_frame_delay.cc b/src/modules/video_coding/main/source/inter_frame_delay.cc
new file mode 100644
index 0000000..f3bc013
--- /dev/null
+++ b/src/modules/video_coding/main/source/inter_frame_delay.cc
@@ -0,0 +1,120 @@
+/*
+ * 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 "inter_frame_delay.h"
+#include "tick_time.h"
+
+namespace webrtc {
+
+VCMInterFrameDelay::VCMInterFrameDelay()
+{
+ Reset();
+}
+
+// Resets the delay estimate
+void
+VCMInterFrameDelay::Reset()
+{
+ _zeroWallClock = VCMTickTime::MillisecondTimestamp();
+ _wrapArounds = 0;
+ _prevWallClock = 0;
+ _prevTimestamp = 0;
+ _dTS = 0;
+}
+
+// Calculates the delay of a frame with the given timestamp.
+// This method is called when the frame is complete.
+bool
+VCMInterFrameDelay::CalculateDelay(WebRtc_UWord32 timestamp,
+ WebRtc_Word64 *delay,
+ WebRtc_Word64 currentWallClock /* = -1 */)
+{
+ if (currentWallClock <= -1)
+ {
+ currentWallClock = VCMTickTime::MillisecondTimestamp();
+ }
+
+ if (_prevWallClock == 0)
+ {
+ // First set of data, initialization, wait for next frame
+ _prevWallClock = currentWallClock;
+ _prevTimestamp = timestamp;
+ *delay = 0;
+ return true;
+ }
+
+ WebRtc_Word32 prevWrapArounds = _wrapArounds;
+ CheckForWrapArounds(timestamp);
+
+ // This will be -1 for backward wrap arounds and +1 for forward wrap arounds
+ WebRtc_Word32 wrapAroundsSincePrev = _wrapArounds - prevWrapArounds;
+
+ // Account for reordering in jitter variance estimate in the future?
+ // Note that this also captures incomplete frames which are grabbed
+ // for decoding after a later frame has been complete, i.e. real
+ // packet losses.
+ if ((wrapAroundsSincePrev == 0 && timestamp < _prevTimestamp) || wrapAroundsSincePrev < 0)
+ {
+ *delay = 0;
+ return false;
+ }
+
+ // Compute the compensated timestamp difference and convert it to ms and
+ // round it to closest integer.
+ _dTS = static_cast<WebRtc_Word64>((timestamp + wrapAroundsSincePrev *
+ (static_cast<WebRtc_Word64>(1)<<32) - _prevTimestamp) / 90.0 + 0.5);
+
+ // frameDelay is the difference of dT and dTS -- i.e. the difference of
+ // the wall clock time difference and the timestamp difference between
+ // two following frames.
+ *delay = static_cast<WebRtc_Word64>(currentWallClock - _prevWallClock - _dTS);
+
+ _prevTimestamp = timestamp;
+ _prevWallClock = currentWallClock;
+
+ return true;
+}
+
+// Returns the current difference between incoming timestamps
+WebRtc_UWord32 VCMInterFrameDelay::CurrentTimeStampDiffMs() const
+{
+ if (_dTS < 0)
+ {
+ return 0;
+ }
+ return static_cast<WebRtc_UWord32>(_dTS);
+}
+
+// Investigates if the timestamp clock has overflowed since the last timestamp and
+// keeps track of the number of wrap arounds since reset.
+void
+VCMInterFrameDelay::CheckForWrapArounds(WebRtc_UWord32 timestamp)
+{
+ if (timestamp < _prevTimestamp)
+ {
+ // This difference will probably be less than -2^31 if we have had a wrap around
+ // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is cast to a Word32,
+ // it should be positive.
+ if (static_cast<WebRtc_Word32>(timestamp - _prevTimestamp) > 0)
+ {
+ // Forward wrap around
+ _wrapArounds++;
+ }
+ }
+ // This difference will probably be less than -2^31 if we have had a backward wrap around.
+ // Since it is cast to a Word32, it should be positive.
+ else if (static_cast<WebRtc_Word32>(_prevTimestamp - timestamp) > 0)
+ {
+ // Backward wrap around
+ _wrapArounds--;
+ }
+}
+
+}
diff --git a/src/modules/video_coding/main/source/inter_frame_delay.h b/src/modules/video_coding/main/source/inter_frame_delay.h
new file mode 100644
index 0000000..7a976a4
--- /dev/null
+++ b/src/modules/video_coding/main/source/inter_frame_delay.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_
+#define WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+class VCMInterFrameDelay
+{
+public:
+ VCMInterFrameDelay();
+
+ // Resets the estimate. Zeros are given as parameters.
+ void Reset();
+
+ // Calculates the delay of a frame with the given timestamp.
+ // This method is called when the frame is complete.
+ //
+ // Input:
+ // - timestamp : RTP timestamp of a received frame
+ // - *delay : Pointer to memory where the result should be stored
+ // - currentWallClock : The current time in milliseconds.
+ // Should be -1 for normal operation, only used for testing.
+ // Return value : true if OK, false when reordered timestamps
+ bool CalculateDelay(WebRtc_UWord32 timestamp,
+ WebRtc_Word64 *delay,
+ WebRtc_Word64 currentWallClock = -1);
+
+ // Returns the current difference between incoming timestamps
+ //
+ // Return value : Wrap-around compensated difference between incoming
+ // timestamps.
+ WebRtc_UWord32 CurrentTimeStampDiffMs() const;
+
+private:
+ // Controls if the RTP timestamp counter has had a wrap around
+ // between the current and the previously received frame.
+ //
+ // Input:
+ // - timestmap : RTP timestamp of the current frame.
+ void CheckForWrapArounds(WebRtc_UWord32 timestamp);
+
+ WebRtc_Word64 _zeroWallClock; // Local timestamp of the first video packet received
+ WebRtc_Word32 _wrapArounds; // Number of wrapArounds detected
+ // The previous timestamp passed to the delay estimate
+ WebRtc_UWord32 _prevTimestamp;
+ // The previous wall clock timestamp used by the delay estimate
+ WebRtc_Word64 _prevWallClock;
+ // Wrap-around compensated difference between incoming timestamps
+ WebRtc_Word64 _dTS;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_
diff --git a/src/modules/video_coding/main/source/internal_defines.h b/src/modules/video_coding/main/source/internal_defines.h
new file mode 100644
index 0000000..781e668
--- /dev/null
+++ b/src/modules/video_coding/main/source/internal_defines.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+#define MASK_32_BITS(x) (0xFFFFFFFF & (x))
+
+inline WebRtc_UWord32 MaskWord64ToUWord32(WebRtc_Word64 w64)
+{
+ return static_cast<WebRtc_UWord32>(MASK_32_BITS(w64));
+}
+
+#define VCM_MAX(a, b) ((a) > (b)) ? (a) : (b)
+#define VCM_MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+#define VCM_DEFAULT_CODEC_WIDTH 352
+#define VCM_DEFAULT_CODEC_HEIGHT 288
+#define VCM_DEFAULT_FRAME_RATE 30
+#define VCM_MIN_BITRATE 30
+
+// Helper macros for creating the static codec list
+#define VCM_NO_CODEC_IDX -1
+#ifdef VIDEOCODEC_VP8
+ #define VCM_VP8_IDX VCM_NO_CODEC_IDX + 1
+#else
+ #define VCM_VP8_IDX VCM_NO_CODEC_IDX
+#endif
+#ifdef VIDEOCODEC_I420
+ #define VCM_I420_IDX VCM_VP8_IDX + 1
+#else
+ #define VCM_I420_IDX VCM_VP8_IDX
+#endif
+#define VCM_NUM_VIDEO_CODECS_AVAILABLE VCM_I420_IDX + 1
+
+#define VCM_NO_RECEIVER_ID 0
+
+inline WebRtc_Word32 VCMId(const WebRtc_Word32 vcmId, const WebRtc_Word32 receiverId = 0)
+{
+ return static_cast<WebRtc_Word32>((vcmId << 16) + receiverId);
+}
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_
diff --git a/src/modules/video_coding/main/source/jitter_buffer.cc b/src/modules/video_coding/main/source/jitter_buffer.cc
new file mode 100644
index 0000000..d4d7ab9
--- /dev/null
+++ b/src/modules/video_coding/main/source/jitter_buffer.cc
@@ -0,0 +1,2015 @@
+/*
+ * 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 "critical_section_wrapper.h"
+
+#include "frame_buffer.h"
+#include "inter_frame_delay.h"
+#include "internal_defines.h"
+#include "jitter_buffer.h"
+#include "jitter_buffer_common.h"
+#include "jitter_estimator.h"
+#include "media_optimization.h" // hybrid NACK/FEC thresholds.
+#include "packet.h"
+
+#include "event.h"
+#include "trace.h"
+#include "tick_time.h"
+#include "list_wrapper.h"
+
+#include <cassert>
+#include <string.h>
+#include <cmath>
+
+#if defined(_WIN32)
+ // VS 2005: Don't warn for default initialized arrays. See help for more info.
+ #pragma warning(disable:4351)
+#endif
+
+namespace webrtc {
+
+// Criteria used when searching for frames in the frame buffer list
+bool
+VCMJitterBuffer::FrameEqualTimestamp(VCMFrameBuffer* frame, const void* timestamp)
+{
+ if (timestamp == NULL)
+ {
+ return false;
+ }
+ return (*static_cast<const WebRtc_UWord32*>(timestamp)) == frame->TimeStamp();
+}
+
+bool
+VCMJitterBuffer::CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
+ const void* /*notUsed*/)
+{
+ const VCMFrameBufferStateEnum state = frame->GetState();
+ // We can decode key frame or decodable/complete frames.
+ return (frame->FrameType() == kVideoFrameKey) &&
+ ((state == kStateComplete)
+ || (state == kStateDecodable));
+}
+
+// Constructor
+VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
+ bool master) :
+ _vcmId(vcmId),
+ _receiverId(receiverId),
+ _running(false),
+ _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
+ _master(master),
+ _frameEvent(),
+ _packetEvent(),
+ _maxNumberOfFrames(kStartNumberOfFrames),
+ _frameBuffers(),
+ _frameBuffersTSOrder(),
+ _lastDecodedSeqNum(),
+ _lastDecodedTimeStamp(-1),
+ _receiveStatistics(),
+ _incomingFrameRate(0),
+ _incomingFrameCount(0),
+ _timeLastIncomingFrameCount(0),
+ _incomingBitCount(0),
+ _dropCount(0),
+ _numConsecutiveOldFrames(0),
+ _numConsecutiveOldPackets(0),
+ _jitterEstimate(vcmId, receiverId),
+ _rttMs(0),
+ _nackMode(kNoNack),
+ _NACKSeqNum(),
+ _NACKSeqNumLength(0),
+ _missingMarkerBits(false),
+ _firstPacket(true)
+{
+ memset(_frameBuffers, 0, sizeof(_frameBuffers));
+ memset(_receiveStatistics, 0, sizeof(_receiveStatistics));
+ _lastDecodedSeqNum = -1;
+ memset(_NACKSeqNumInternal, -1, sizeof(_NACKSeqNumInternal));
+
+ for (int i = 0; i< kStartNumberOfFrames; i++)
+ {
+ _frameBuffers[i] = new VCMFrameBuffer();
+ }
+}
+
+// Destructor
+VCMJitterBuffer::~VCMJitterBuffer()
+{
+ Stop();
+ for (int i = 0; i< kMaxNumberOfFrames; i++)
+ {
+ if (_frameBuffers[i])
+ {
+ delete _frameBuffers[i];
+ }
+ }
+ delete &_critSect;
+}
+
+VCMJitterBuffer&
+VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
+{
+ if (this != &rhs)
+ {
+ _critSect.Enter();
+ rhs._critSect.Enter();
+ _vcmId = rhs._vcmId;
+ _receiverId = rhs._receiverId;
+ _running = rhs._running;
+ _master = !rhs._master;
+ _maxNumberOfFrames = rhs._maxNumberOfFrames;
+ _lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
+ _incomingFrameRate = rhs._incomingFrameRate;
+ _incomingFrameCount = rhs._incomingFrameCount;
+ _timeLastIncomingFrameCount = rhs._timeLastIncomingFrameCount;
+ _incomingBitCount = rhs._incomingBitCount;
+ _dropCount = rhs._dropCount;
+ _numConsecutiveOldFrames = rhs._numConsecutiveOldFrames;
+ _numConsecutiveOldPackets = rhs._numConsecutiveOldPackets;
+ _jitterEstimate = rhs._jitterEstimate;
+ _delayEstimate = rhs._delayEstimate;
+ _waitingForCompletion = rhs._waitingForCompletion;
+ _nackMode = rhs._nackMode;
+ _rttMs = rhs._rttMs;
+ _NACKSeqNumLength = rhs._NACKSeqNumLength;
+ _missingMarkerBits = rhs._missingMarkerBits;
+ _firstPacket = rhs._firstPacket;
+ _lastDecodedSeqNum = rhs._lastDecodedSeqNum;
+ memcpy(_receiveStatistics, rhs._receiveStatistics, sizeof(_receiveStatistics));
+ memcpy(_NACKSeqNumInternal, rhs._NACKSeqNumInternal, sizeof(_NACKSeqNumInternal));
+ memcpy(_NACKSeqNum, rhs._NACKSeqNum, sizeof(_NACKSeqNum));
+ for (int i = 0; i < kMaxNumberOfFrames; i++)
+ {
+ if (_frameBuffers[i] != NULL)
+ {
+ delete _frameBuffers[i];
+ _frameBuffers[i] = NULL;
+ }
+ }
+ while(_frameBuffersTSOrder.Erase(_frameBuffersTSOrder.First()) != -1) { }
+ for (int i = 0; i < _maxNumberOfFrames; i++)
+ {
+ _frameBuffers[i] = new VCMFrameBuffer(*(rhs._frameBuffers[i]));
+ if (_frameBuffers[i]->Length() > 0)
+ {
+ _frameBuffersTSOrder.Insert(_frameBuffers[i]);
+ }
+ }
+ rhs._critSect.Leave();
+ _critSect.Leave();
+ }
+ return *this;
+}
+
+WebRtc_UWord32
+VCMJitterBuffer::LatestTimestamp(const WebRtc_UWord32 existingTimestamp,
+ const WebRtc_UWord32 newTimestamp)
+{
+ bool wrap = (newTimestamp < 0x0000ffff && existingTimestamp > 0xffff0000) ||
+ (newTimestamp > 0xffff0000 && existingTimestamp < 0x0000ffff);
+ if (existingTimestamp > newTimestamp && !wrap)
+ {
+ return existingTimestamp;
+ }
+ else if (existingTimestamp <= newTimestamp && !wrap)
+ {
+ return newTimestamp;
+ }
+ else if (existingTimestamp < newTimestamp && wrap)
+ {
+ return existingTimestamp;
+ }
+ else
+ {
+ return newTimestamp;
+ }
+}
+
+// Start jitter buffer
+void
+VCMJitterBuffer::Start()
+{
+ CriticalSectionScoped cs(_critSect);
+ _running = true;
+ _incomingFrameCount = 0;
+ _incomingFrameRate = 0;
+ _incomingBitCount = 0;
+ _timeLastIncomingFrameCount = VCMTickTime::MillisecondTimestamp();
+ memset(_receiveStatistics, 0, sizeof(_receiveStatistics));
+
+ _numConsecutiveOldFrames = 0;
+ _numConsecutiveOldPackets = 0;
+
+ _frameEvent.Reset(); // start in a non-signaled state
+ _packetEvent.Reset(); // start in a non-signaled state
+ _waitingForCompletion.frameSize = 0;
+ _waitingForCompletion.timestamp = 0;
+ _waitingForCompletion.latestPacketTime = -1;
+ _missingMarkerBits = false;
+ _firstPacket = true;
+ _NACKSeqNumLength = 0;
+ _rttMs = 0;
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId,
+ _receiverId), "JB(0x%x): Jitter buffer: start", this);
+}
+
+
+// Stop jitter buffer
+void
+VCMJitterBuffer::Stop()
+{
+ _critSect.Enter();
+ _running = false;
+ _lastDecodedTimeStamp = -1;
+ _lastDecodedSeqNum = -1;
+ _frameBuffersTSOrder.Flush();
+ for (int i = 0; i < kMaxNumberOfFrames; i++)
+ {
+ if (_frameBuffers[i] != NULL)
+ {
+ static_cast<VCMFrameBuffer*>(_frameBuffers[i])->SetState(kStateFree);
+ }
+ }
+
+ _critSect.Leave();
+ _frameEvent.Set(); // Make sure we exit from trying to get a frame to decoder
+ _packetEvent.Set(); // Make sure we exit from trying to get a sequence number
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId,
+ _receiverId), "JB(0x%x): Jitter buffer: stop", this);
+}
+
+bool
+VCMJitterBuffer::Running() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return _running;
+}
+
+// Flush jitter buffer
+void
+VCMJitterBuffer::Flush()
+{
+ CriticalSectionScoped cs(_critSect);
+ FlushInternal();
+}
+
+// Must be called under the critical section _critSect
+void
+VCMJitterBuffer::FlushInternal()
+{
+ // Erase all frames from the sorted list and set their state to free.
+ _frameBuffersTSOrder.Flush();
+ for (int i = 0; i < _maxNumberOfFrames; i++)
+ {
+ ReleaseFrameInternal(_frameBuffers[i]);
+ }
+ _lastDecodedSeqNum = -1;
+ _lastDecodedTimeStamp = -1;
+
+ _frameEvent.Reset();
+ _packetEvent.Reset();
+
+ _numConsecutiveOldFrames = 0;
+ _numConsecutiveOldPackets = 0;
+
+ // Also reset the jitter and delay estimates
+ _jitterEstimate.Reset();
+ _delayEstimate.Reset();
+
+ _waitingForCompletion.frameSize = 0;
+ _waitingForCompletion.timestamp = 0;
+ _waitingForCompletion.latestPacketTime = -1;
+
+ _missingMarkerBits = false;
+ _firstPacket = true;
+
+ _NACKSeqNumLength = 0;
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId,
+ _receiverId), "JB(0x%x): Jitter buffer: flush", this);
+}
+
+// Set the frame state to free and remove it from the sorted
+// frame list. Must be called from inside the critical section _critSect.
+void
+VCMJitterBuffer::ReleaseFrameInternal(VCMFrameBuffer* frame)
+{
+ if (frame != NULL)
+ {
+ frame->SetState(kStateFree);
+ }
+}
+
+// Update frame state (set as complete if conditions are met)
+// Doing it here increases the degree of freedom for e.g. future
+// reconstructability of separate layers. Must be called under the
+// critical section _critSect.
+void
+VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame)
+{
+ if (frame == NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
+ VCMId(_vcmId, _receiverId), "JB(0x%x) FB(0x%x): "
+ "UpdateFrameState NULL frame pointer", this, frame);
+ return;
+ }
+
+ int length = frame->Length();
+ if (_master)
+ {
+ // Only trace the primary jitter buffer to make it possible to parse and plot the trace file.
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "JB(0x%x) FB(0x%x): Complete frame added to jitter buffer, size:%d type %d",
+ this, frame,length,frame->FrameType());
+ }
+
+ if (length != 0 && !frame->GetCountedFrame())
+ {
+ // ignore Ack frames
+ _incomingFrameCount++;
+ frame->SetCountedFrame(true);
+ }
+
+ // Check if we should drop frame
+ // an old complete frame can arrive too late
+ if(_lastDecodedTimeStamp > 0 &&
+ LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
+ frame->TimeStamp()) == _lastDecodedTimeStamp)
+ {
+ // Frame is older than the latest decoded frame, drop it.
+ // This will trigger a release in CleanUpSizeZeroFrames later.
+ frame->Reset();
+ frame->SetState(kStateEmpty);
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "JB(0x%x) FB(0x%x): Dropping old frame in Jitter buffer", this, frame);
+ _dropCount++;
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Jitter buffer drop count: %d, consecutive drops: %u", _dropCount, _numConsecutiveOldFrames);
+ // Flush() if this happens consistently.
+ _numConsecutiveOldFrames++;
+ if (_numConsecutiveOldFrames > kMaxConsecutiveOldFrames)
+ {
+ FlushInternal();
+ }
+ return;
+ }
+ _numConsecutiveOldFrames = 0;
+ frame->SetState(kStateComplete);
+
+
+ // Update receive statistics. We count all layers, thus when you use layers
+ // adding all key and delta frames might differ from frame count
+ if (frame->IsSessionComplete())
+ {
+ switch (frame->FrameType())
+ {
+ case kVideoFrameKey:
+ {
+ _receiveStatistics[0]++;
+ break;
+ }
+ case kVideoFrameDelta:
+ {
+ _receiveStatistics[1]++;
+ break;
+ }
+ case kVideoFrameGolden:
+ {
+ _receiveStatistics[2]++;
+ break;
+ }
+ case kVideoFrameAltRef:
+ {
+ _receiveStatistics[3]++;
+ break;
+ }
+ default:
+ assert(false);
+
+ }
+ }
+ const VCMFrameListItem* oldFrameListItem = FindOldestCompleteContinuousFrame();
+ VCMFrameBuffer* oldFrame = NULL;
+ if (oldFrameListItem != NULL)
+ {
+ oldFrame = oldFrameListItem->GetItem();
+ }
+
+ // Only signal if this is the oldest frame.
+ // Not necessary the case due to packet reordering or NACK.
+ if (!WaitForNack() || (oldFrame != NULL && oldFrame == frame))
+ {
+ _frameEvent.Set();
+ }
+}
+
+
+// Get received key and delta frames
+WebRtc_Word32
+VCMJitterBuffer::GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
+ WebRtc_UWord32& receivedKeyFrames) const
+{
+ {
+ CriticalSectionScoped cs(_critSect);
+ receivedDeltaFrames = _receiveStatistics[1] + _receiveStatistics[3];
+ receivedKeyFrames = _receiveStatistics[0] + _receiveStatistics[2];
+ }
+ return 0;
+}
+
+// Gets frame to use for this timestamp. If no match, get empty frame.
+WebRtc_Word32
+VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMEncodedFrame*& frame)
+{
+ if (!_running) // don't accept incoming packets until we are started
+ {
+ return VCM_UNINITIALIZED;
+ }
+
+ _critSect.Enter();
+ if (LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp), packet.timestamp)
+ == _lastDecodedTimeStamp
+ && packet.sizeBytes > 0) // Make sure that old filler packets are inserted.
+ {
+ // Trying to get an old frame.
+ _numConsecutiveOldPackets++;
+ if (_numConsecutiveOldPackets > kMaxConsecutiveOldPackets)
+ {
+ FlushInternal();
+ }
+ _critSect.Leave();
+ return VCM_OLD_PACKET_ERROR;
+ }
+ _numConsecutiveOldPackets = 0;
+
+ frame = _frameBuffersTSOrder.FindFrame(FrameEqualTimestamp, &packet.timestamp);
+ _critSect.Leave();
+
+ if (frame != NULL)
+ {
+ return VCM_OK;
+ }
+
+ // No match, return empty frame
+ frame = GetEmptyFrame();
+ if (frame != NULL)
+ {
+ return VCM_OK;
+ }
+ // No free frame! Try to reclaim some...
+ _critSect.Enter();
+ RecycleFramesUntilKeyFrame();
+ _critSect.Leave();
+
+ frame = GetEmptyFrame();
+ if (frame != NULL)
+ {
+ return VCM_OK;
+ }
+ return VCM_JITTER_BUFFER_ERROR;
+}
+
+// Deprecated! Kept for testing purposes.
+VCMEncodedFrame*
+VCMJitterBuffer::GetFrame(const VCMPacket& packet)
+{
+ VCMEncodedFrame* frame = NULL;
+ if (GetFrame(packet, frame) < 0)
+ {
+ return NULL;
+ }
+ return frame;
+}
+
+// Get empty frame, creates new (i.e. increases JB size) if necessary
+VCMFrameBuffer*
+VCMJitterBuffer::GetEmptyFrame()
+{
+ if (!_running) // don't accept incoming packets until we are started
+ {
+ return NULL;
+ }
+
+ _critSect.Enter();
+
+ for (int i = 0; i <_maxNumberOfFrames; ++i)
+ {
+ if (kStateFree == _frameBuffers[i]->GetState())
+ {
+ // found a free buffer
+ _frameBuffers[i]->SetState(kStateEmpty);
+ _critSect.Leave();
+ return _frameBuffers[i];
+ }
+ }
+
+ // Check if we can increase JB size
+ if (_maxNumberOfFrames < kMaxNumberOfFrames)
+ {
+ VCMFrameBuffer* ptrNewBuffer = new VCMFrameBuffer();
+ ptrNewBuffer->SetState(kStateEmpty);
+ _frameBuffers[_maxNumberOfFrames] = ptrNewBuffer;
+ _maxNumberOfFrames++;
+
+ _critSect.Leave();
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding,
+ VCMId(_vcmId, _receiverId), "JB(0x%x) FB(0x%x): Jitter buffer "
+ "increased to:%d frames", this, ptrNewBuffer, _maxNumberOfFrames);
+ return ptrNewBuffer;
+ }
+ _critSect.Leave();
+
+ // We have reached max size, cannot increase JB size
+ return NULL;
+}
+
+// Must be called under the critical section _critSect.
+VCMFrameListItem*
+VCMJitterBuffer::FindOldestSequenceNum() const
+{
+ WebRtc_UWord16 currentLow = 0xffff;
+ VCMFrameBufferStateEnum state = kStateFree;
+ WebRtc_UWord16 sequenceNumber = 0;
+ bool first = true;
+ VCMFrameListItem* frameListItem = _frameBuffersTSOrder.First();
+ VCMFrameListItem* oldestFrameListItem = NULL;
+
+ while (frameListItem != NULL)
+ {
+ // if we have more than one frame done since last time,
+ // pick oldest
+ VCMFrameBuffer* ptrFrame = NULL;
+ ptrFrame = frameListItem->GetItem();
+ state = ptrFrame->GetState();
+ sequenceNumber = static_cast<WebRtc_UWord16>(ptrFrame->GetLowSeqNum());
+
+ // Find the oldest, hence lowest, using sequence numbers
+ if (first)
+ {
+ currentLow = sequenceNumber;
+ oldestFrameListItem = frameListItem;
+ first = false;
+ }
+ else if ((currentLow < 0x0fff) && (sequenceNumber > 0xf000))
+ {
+ // We have a wrap and this one is older
+ currentLow = sequenceNumber;
+ oldestFrameListItem = frameListItem;
+ }
+ else if ((sequenceNumber < 0x0fff) && (currentLow > 0xf000))
+ {
+ // This one is after a wrap, leave as is
+ }
+ else if (currentLow > sequenceNumber)
+ {
+ // Normal case, this one is lower.
+ currentLow = sequenceNumber;
+ oldestFrameListItem = frameListItem;
+ }
+ frameListItem = _frameBuffersTSOrder.Next(frameListItem);
+ }
+ return oldestFrameListItem;
+}
+
+// Find oldest complete frame used for getting next frame to decode
+// Must be called under critical section
+// Based on sequence number
+// Return NULL for lost packets
+VCMFrameListItem*
+VCMJitterBuffer::FindOldestCompleteContinuousFrame()
+{
+ // if we have more than one frame done since last time, pick oldest
+ VCMFrameBuffer* oldestFrame = NULL;
+ int currentLow = -1;
+
+ VCMFrameListItem* oldestFrameItem = _frameBuffersTSOrder.First();
+ if (oldestFrameItem != NULL)
+ {
+ oldestFrame = oldestFrameItem->GetItem();
+ }
+ // is the frame complete?
+ if (oldestFrame != NULL)
+ {
+ if (kStateComplete != oldestFrame->GetState())
+ {
+ // Try to see if the frame is complete even though the state is not
+ // complete. Can happen if markerbit is not set.
+ if (!CheckForCompleteFrame(oldestFrameItem))
+ {
+ oldestFrame = NULL;
+ }
+ }
+ else
+ {
+ // we have a complete frame
+ currentLow = oldestFrame->GetLowSeqNum();
+ }
+ }
+ if (oldestFrame == NULL)
+ {
+ // no complete frame no point to continue
+ return NULL;
+ }
+
+ // we have a complete frame
+ // check if it's continuous, otherwise we are missing a full frame
+ // Use seqNum not timestamp since a full frame might be lost
+ if (_lastDecodedSeqNum != -1)
+ {
+ // it's not enough that we have complete frame we need the seq numbers
+ // to be continuous too for layers it's not enough that we have complete
+ // frame we need the layers to be continuous too
+ currentLow = oldestFrame->GetLowSeqNum();
+
+ WebRtc_UWord16 lastDecodedSeqNum = (WebRtc_UWord16)_lastDecodedSeqNum;
+
+ // we could have received the first packet of the last frame before a
+ // long period if drop, that case is handled by GetNackList
+ if (((WebRtc_UWord16)(lastDecodedSeqNum + 1)) != currentLow)
+ {
+ // wait since we want a complete continuous frame
+ return NULL;
+ }
+ }
+ return oldestFrameItem;
+}
+
+// Check if the oldest frame is complete even though it isn't complete.
+// This can happen when makerbit is not set
+// Must be called under the critical section _critSect.
+// Return false for lost packets
+bool
+VCMJitterBuffer::CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem)
+{
+ const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameItem);
+ VCMFrameBuffer* oldestFrame = NULL;
+ if (oldestFrameItem != NULL)
+ {
+ oldestFrame = oldestFrameItem->GetItem();
+ }
+ if (nextFrameItem != NULL)
+ {
+ // We have received at least one packet from a later frame.
+ if(!oldestFrame->HaveLastPacket()) // If we don't have the markerbit
+ {
+ VCMFrameBuffer* nextFrame = nextFrameItem->GetItem();
+ // Verify that we have received the first packet of the next frame.
+ // This is the only way we can be sure we're not missing the last packet.
+ if (nextFrame != NULL && nextFrame->GetLowSeqNum() ==
+ static_cast<WebRtc_UWord16>(oldestFrame->GetHighSeqNum() + 1))
+ {
+ _missingMarkerBits = true;
+ bool completeSession = oldestFrame->ForceSetHaveLastPacket();
+ if (completeSession)
+ {
+ UpdateFrameState(oldestFrame);
+ }
+ const VCMFrameBufferStateEnum state = oldestFrame->GetState();
+ if (state == kStateComplete)
+ {
+ if(oldestFrame->Length() > 0)
+ {
+ UpdateJitterAndDelayEstimates(*oldestFrame, false);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// Call from inside the critical section _critSect
+void
+VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
+{
+ if (frame == NULL)
+ {
+ return;
+ }
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "JB(0x%x) FB(0x%x): RecycleFrame, size:%d",
+ this, frame, frame->Length());
+
+ ReleaseFrameInternal(frame);
+}
+
+
+// Calculate frame and bit rates
+WebRtc_Word32
+VCMJitterBuffer::GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate)
+{
+ CriticalSectionScoped cs(_critSect);
+ const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ WebRtc_Word64 diff = now - _timeLastIncomingFrameCount;
+ if (diff < 1000 && _incomingFrameRate > 0 && _incomingBitRate > 0)
+ {
+ // Make sure we report something even though less than
+ // 1 second has passed since last update.
+ frameRate = _incomingFrameRate;
+ bitRate = _incomingBitRate;
+ }
+ else if (_incomingFrameCount != 0)
+ {
+ // We have received frame(s) since last call to this function
+
+ // Prepare calculations
+ if (diff <= 0)
+ {
+ diff = 1;
+ }
+ // we add 0.5f for rounding
+ float rate = 0.5f + ((_incomingFrameCount * 1000.0f) / diff);
+ if (rate < 1.0f) // don't go below 1, can crash
+ {
+ rate = 1.0f;
+ }
+
+ // Calculate frame rate
+ // Let r be rate.
+ // r(0) = 1000*framecount/delta_time. (I.e. frames per second since last calculation.)
+ // frameRate = r(0)/2 + r(-1)/2 (I.e. fr/s average this and the previous calculation.)
+ frameRate = (_incomingFrameRate + (WebRtc_Word32)rate) >> 1;
+ _incomingFrameRate = (WebRtc_UWord8)rate;
+
+ // Calculate bit rate
+ if (_incomingBitCount == 0)
+ {
+ bitRate = 0;
+ }
+ else
+ {
+ bitRate = 10 * ((100 * _incomingBitCount) / static_cast<WebRtc_UWord32>(diff));
+ }
+ _incomingBitRate = bitRate;
+
+ // Reset count
+ _incomingFrameCount = 0;
+ _incomingBitCount = 0;
+ _timeLastIncomingFrameCount = now;
+
+ }
+ else
+ {
+ // No frames since last call
+ _timeLastIncomingFrameCount = VCMTickTime::MillisecondTimestamp();
+ frameRate = 0;
+ bitRate = 0;
+ _incomingBitRate = 0;
+ }
+
+ return 0;
+}
+
+// Returns immediately or a X ms event hang waiting for a decodable frame, X decided by caller
+VCMEncodedFrame*
+VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
+{
+ if (!_running)
+ {
+ return NULL;
+ }
+
+ _critSect.Enter();
+
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ VCMFrameListItem* oldestFrameListItem = FindOldestCompleteContinuousFrame();
+ VCMFrameBuffer* oldestFrame = NULL;
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+
+ if (oldestFrame == NULL)
+ {
+ if (maxWaitTimeMS == 0)
+ {
+ _critSect.Leave();
+ return NULL;
+ }
+ const WebRtc_Word64 endWaitTimeMs = VCMTickTime::MillisecondTimestamp()
+ + maxWaitTimeMS;
+ WebRtc_Word64 waitTimeMs = maxWaitTimeMS;
+ while (waitTimeMs > 0)
+ {
+ _critSect.Leave();
+ const EventTypeWrapper ret = _frameEvent.Wait(static_cast<WebRtc_UWord32>(waitTimeMs));
+ _critSect.Enter();
+ if (ret == kEventSignaled)
+ {
+ // are we closing down the Jitter buffer
+ if (!_running)
+ {
+ _critSect.Leave();
+ return NULL;
+ }
+
+ // Finding oldest frame ready for decoder, but check sequence number and size
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+ oldestFrameListItem = FindOldestCompleteContinuousFrame();
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+ if (oldestFrame == NULL)
+ {
+ waitTimeMs = endWaitTimeMs - VCMTickTime::MillisecondTimestamp();
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ _critSect.Leave();
+ return NULL;
+ }
+ }
+ // Inside critSect
+ }
+ else
+ {
+ // we already have a frame reset the event
+ _frameEvent.Reset();
+ }
+
+ if (oldestFrame == NULL)
+ {
+ // Even after signaling we're still missing a complete _continuous_ frame
+ _critSect.Leave();
+ return NULL;
+ }
+
+ // we have a frame
+ // store seqnum
+ _lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
+ // store current timestamp
+ _lastDecodedTimeStamp = oldestFrame->TimeStamp();
+
+ // Update jitter estimate
+ const bool retransmitted = (oldestFrame->GetNackCount() > 0);
+ if (retransmitted)
+ {
+ _jitterEstimate.FrameNacked();
+ }
+ else if (oldestFrame->Length() > 0)
+ {
+ // Ignore retransmitted and empty frames.
+ UpdateJitterAndDelayEstimates(*oldestFrame, false);
+ }
+
+ // This needs to be done before we clean up old frames,
+ // otherwise we'll remove ourselves...
+ oldestFrame->SetState(kStateDecoding);
+ _frameBuffersTSOrder.Erase(oldestFrameListItem);
+ oldestFrameListItem = NULL;
+
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ _critSect.Leave();
+
+ return oldestFrame;
+}
+
+WebRtc_UWord32
+VCMJitterBuffer::GetEstimatedJitterMS()
+{
+ CriticalSectionScoped cs(_critSect);
+ return GetEstimatedJitterMsInternal();
+}
+
+WebRtc_UWord32
+VCMJitterBuffer::GetEstimatedJitterMsInternal()
+{
+ WebRtc_UWord32 estimate = VCMJitterEstimator::OPERATING_SYSTEM_JITTER;
+
+ // compute RTT multiplier for estimation
+ double rttMult = 1.0f;
+ if (_nackMode == kNackHybrid && _rttMs > kLowRttNackMs)
+ {
+ // from here we count on FEC
+ rttMult = 0.0f;
+ }
+ estimate += static_cast<WebRtc_UWord32>
+ (_jitterEstimate.GetJitterEstimate(rttMult) + 0.5);
+ if (_missingMarkerBits)
+ {
+ // Since the incoming packets are all missing marker bits we have to
+ // wait until the first packet of the next frame arrives, before we can
+ // safely say that the frame is complete. Therefore we have to compensate
+ // the jitter buffer level with one frame period.
+ // TODO(holmer): The timestamp diff should probably be filtered
+ // (max filter) since the diff can alternate between e.g. 3000 and 6000
+ // if we have a frame rate between 15 and 30 frames per seconds.
+ estimate += _delayEstimate.CurrentTimeStampDiffMs();
+ }
+ return estimate;
+}
+
+void
+VCMJitterBuffer::UpdateRtt(WebRtc_UWord32 rttMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ _rttMs = rttMs;
+ _jitterEstimate.UpdateRtt(rttMs);
+}
+
+// wait for the first packet in the next frame to arrive
+WebRtc_Word64
+VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
+ FrameType& incomingFrameType,
+ WebRtc_Word64& renderTimeMs)
+{
+ if (!_running)
+ {
+ return -1;
+ }
+
+ _critSect.Enter();
+
+ // Finding oldest frame ready for decoder, but check sequence number and size
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ VCMFrameBuffer* oldestFrame = _frameBuffersTSOrder.FirstFrame();
+
+ if (oldestFrame == NULL)
+ {
+ _critSect.Leave();
+ if(_packetEvent.Wait(maxWaitTimeMS) == kEventSignaled)
+ {
+ // are we closing down the Jitter buffer
+ if (!_running)
+ {
+ return -1;
+ }
+ _critSect.Enter();
+
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+ oldestFrame = _frameBuffersTSOrder.FirstFrame();
+ }else
+ {
+ _critSect.Enter();
+ }
+ }
+ _packetEvent.Reset();
+
+ if (oldestFrame == NULL)
+ {
+ _critSect.Leave();
+ return -1;
+ }
+ // we have a frame
+
+ // return frame type
+ incomingFrameType = oldestFrame->FrameType(); // All layers are assumed to have the same type
+
+ renderTimeMs = oldestFrame->RenderTimeMs();
+
+ const WebRtc_UWord32 timestamp = oldestFrame->TimeStamp();
+
+ _critSect.Leave();
+
+ // return current time
+ return timestamp;
+}
+
+// Answers the question:
+// Will the packet sequence be complete if the next frame is grabbed for decoding right now?
+// That is, have we lost a frame between the last decoded frame and the next, or is the next
+// frame missing one or more packets?
+bool
+VCMJitterBuffer::CompleteSequenceWithNextFrame()
+{
+ CriticalSectionScoped cs(_critSect);
+ // Finding oldest frame ready for decoder, but check sequence number and size
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First();
+ if (oldestFrameListItem == NULL)
+ {
+ // No frame found
+ return true;
+ }
+
+ VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem();
+ const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameListItem);
+ if (nextFrameItem == NULL && !oldestFrame->HaveLastPacket())
+ {
+ // Frame not ready to be decoded.
+ return true;
+ }
+
+ // See if we have lost a frame before this one.
+ if (_lastDecodedSeqNum == -1)
+ {
+ // The sequence is not complete since we haven't yet.
+ if (oldestFrame->FrameType() != kVideoFrameKey)
+ {
+ return false;
+ }
+ }
+ else if (oldestFrame->GetLowSeqNum() == -1)
+ {
+ return false;
+ }
+ else if (oldestFrame->GetLowSeqNum() != (_lastDecodedSeqNum + 1) % 0x00010000)
+ {
+ return false;
+ }
+ return true;
+}
+
+// Returns immediately
+VCMEncodedFrame*
+VCMJitterBuffer::GetFrameForDecoding()
+{
+ CriticalSectionScoped cs(_critSect);
+ if (!_running)
+ {
+ return NULL;
+ }
+
+ if (WaitForNack())
+ {
+ return GetFrameForDecodingNACK();
+ }
+
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First();
+ if (oldestFrameListItem == NULL)
+ {
+ return NULL;
+ }
+ VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem();
+
+ const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameListItem);
+ if (nextFrameItem == NULL && !oldestFrame->HaveLastPacket())
+ {
+ return NULL;
+ }
+
+ // Incomplete frame pulled out from jitter buffer,
+ // update the jitter estimate with what we currently know.
+ // This frame shouldn't have been retransmitted, but if we recently
+ // turned off NACK this might still happen.
+ const bool retransmitted = (oldestFrame->GetNackCount() > 0);
+ if (retransmitted)
+ {
+ _jitterEstimate.FrameNacked();
+ }
+ else if (oldestFrame->Length() > 0)
+ {
+ // Ignore retransmitted and empty frames.
+ // Update with the previous incomplete frame first
+ if (_waitingForCompletion.latestPacketTime >= 0)
+ {
+ UpdateJitterAndDelayEstimates(_waitingForCompletion, true);
+ }
+ // Then wait for this one to get complete
+ _waitingForCompletion.frameSize = oldestFrame->Length();
+ _waitingForCompletion.latestPacketTime = oldestFrame->LatestPacketTimeMs();
+ _waitingForCompletion.timestamp = oldestFrame->TimeStamp();
+ oldestFrame->SetState(kStateDecoding);
+ }
+ _frameBuffersTSOrder.Erase(oldestFrameListItem);
+ oldestFrameListItem = NULL;
+
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ VerifyAndSetPreviousFrameLost(*oldestFrame);
+
+ // store current time
+ _lastDecodedTimeStamp = oldestFrame->TimeStamp();
+
+ // store seqnum
+ _lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
+
+ return oldestFrame;
+}
+
+VCMEncodedFrame*
+VCMJitterBuffer::GetFrameForDecodingNACK()
+{
+ // when we use NACK we don't release non complete frames
+ // unless we have a complete key frame.
+ // In hybrid mode, we may release decodable frames (non-complete)
+
+ // Clean up old frames and empty frames
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ // First look for a complete _continuous_ frame.
+ VCMFrameListItem* oldestFrameListItem = FindOldestCompleteContinuousFrame();
+ VCMFrameBuffer* oldestFrame = NULL;
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+ bool continuous = true;
+ if (oldestFrame == NULL)
+ {
+ continuous = false;
+ // If we didn't find one we're good with a complete key/decodable frame.
+ oldestFrameListItem = _frameBuffersTSOrder.FindFrameListItem(
+ CompleteDecodableKeyFrameCriteria);
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+ if (oldestFrame == NULL)
+ {
+ return NULL;
+ }
+ }
+
+ // We have a complete/decodable continuous frame, decode it.
+ // store seqnum
+ _lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
+ // store current time
+ _lastDecodedTimeStamp = oldestFrame->TimeStamp();
+
+ // Update jitter estimate
+ const bool retransmitted = (oldestFrame->GetNackCount() > 0);
+ if (retransmitted)
+ {
+ _jitterEstimate.FrameNacked();
+ }
+ else if (oldestFrame->Length() > 0)
+ {
+ // Ignore retransmitted and empty frames.
+ UpdateJitterAndDelayEstimates(*oldestFrame, false);
+ }
+
+ // This needs to be done before we clean up old frames,
+ // otherwise we might release the frame we want to decode right now.
+ oldestFrame->SetState(kStateDecoding);
+ _frameBuffersTSOrder.Erase(oldestFrameListItem);
+
+ // Clean up old frames and empty frames
+ CleanUpOldFrames();
+ CleanUpSizeZeroFrames();
+
+ return oldestFrame;
+}
+
+// Must be called under the critical section _critSect. Should never be called with
+// retransmitted frames, they must be filtered out before this function is called.
+void
+VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMJitterSample& sample, bool incompleteFrame)
+{
+ if (sample.latestPacketTime == -1)
+ {
+ return;
+ }
+ if (incompleteFrame)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Received incomplete frame timestamp %u frame size %u at time %u",
+ sample.timestamp, sample.frameSize,
+ MaskWord64ToUWord32(sample.latestPacketTime));
+ }
+ else
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Received complete frame timestamp %u frame size %u at time %u",
+ sample.timestamp, sample.frameSize,
+ MaskWord64ToUWord32(sample.latestPacketTime));
+ }
+ UpdateJitterAndDelayEstimates(sample.latestPacketTime,
+ sample.timestamp,
+ sample.frameSize,
+ incompleteFrame);
+}
+
+// Must be called under the critical section _critSect. Should never be called with
+// retransmitted frames, they must be filtered out before this function is called.
+void
+VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame, bool incompleteFrame)
+{
+ if (frame.LatestPacketTimeMs() == -1)
+ {
+ return;
+ }
+ // No retransmitted frames should be a part of the jitter
+ // estimate.
+ if (incompleteFrame)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Received incomplete frame timestamp %u frame type %d frame size %u"
+ " at time %u, jitter estimate was %u",
+ frame.TimeStamp(), frame.FrameType(), frame.Length(),
+ MaskWord64ToUWord32(frame.LatestPacketTimeMs()),
+ GetEstimatedJitterMsInternal());
+ }
+ else
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Received complete frame timestamp %u frame type %d frame size %u "
+ "at time %u, jitter estimate was %u",
+ frame.TimeStamp(), frame.FrameType(), frame.Length(),
+ MaskWord64ToUWord32(frame.LatestPacketTimeMs()),
+ GetEstimatedJitterMsInternal());
+ }
+ UpdateJitterAndDelayEstimates(frame.LatestPacketTimeMs(), frame.TimeStamp(),
+ frame.Length(), incompleteFrame);
+}
+
+// Must be called under the critical section _critSect. Should never be called with
+// retransmitted frames, they must be filtered out before this function is called.
+void
+VCMJitterBuffer::UpdateJitterAndDelayEstimates(WebRtc_Word64 latestPacketTimeMs,
+ WebRtc_UWord32 timestamp,
+ WebRtc_UWord32 frameSize,
+ bool incompleteFrame)
+{
+ if (latestPacketTimeMs == -1)
+ {
+ return;
+ }
+ WebRtc_Word64 frameDelay;
+ // Calculate the delay estimate
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Packet received and sent to jitter estimate with: timestamp=%u wallClock=%u",
+ timestamp, MaskWord64ToUWord32(latestPacketTimeMs));
+ bool notReordered = _delayEstimate.CalculateDelay(timestamp, &frameDelay, latestPacketTimeMs);
+ // Filter out frames which have been reordered in time by the network
+ if (notReordered)
+ {
+ // Update the jitter estimate with the new samples
+ _jitterEstimate.UpdateEstimate(frameDelay, frameSize, incompleteFrame);
+ }
+}
+
+WebRtc_UWord16*
+VCMJitterBuffer::GetNackList(WebRtc_UWord16& nackSize,bool& listExtended)
+{
+ return CreateNackList(nackSize,listExtended);
+}
+
+// Assume called internally with critsect
+WebRtc_Word32
+VCMJitterBuffer::GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
+ WebRtc_Word32& highSeqNum) const
+{
+ int i = 0;
+ int seqNum = -1;
+
+ highSeqNum = -1;
+ lowSeqNum = _lastDecodedSeqNum;
+
+ // find highest seq numbers
+ for (i = 0; i < _maxNumberOfFrames; ++i)
+ {
+ seqNum = _frameBuffers[i]->GetHighSeqNum();
+
+ // Ignore free / empty frames
+ VCMFrameBufferStateEnum state = _frameBuffers[i]->GetState();
+
+ if ((kStateFree != state) &&
+ (kStateEmpty != state) &&
+ (kStateDecoding != state) &&
+ seqNum != -1)
+ {
+ if (highSeqNum == -1)
+ {
+ // first
+ highSeqNum = seqNum;
+ }
+ else if (seqNum < 0x0fff && highSeqNum > 0xf000)
+ {
+ // wrap
+ highSeqNum = seqNum;
+ }
+ else if(seqNum > 0xf000 && highSeqNum < 0x0fff)
+ {
+ // Do nothing since it is a wrap and this one is older
+ }
+ else if (seqNum > highSeqNum)
+ {
+ highSeqNum = seqNum;
+ }
+ }
+ } // for
+ return 0;
+}
+
+
+WebRtc_UWord16*
+VCMJitterBuffer::CreateNackList(WebRtc_UWord16& nackSize, bool& listExtended)
+{
+ CriticalSectionScoped cs(_critSect);
+ int i = 0;
+ WebRtc_Word32 lowSeqNum = -1;
+ WebRtc_Word32 highSeqNum = -1;
+ listExtended = false;
+
+ // don't create list, if we won't wait for it
+ if (!WaitForNack())
+ {
+ nackSize = 0;
+ return NULL;
+ }
+
+ // Find the lowest (last decoded) sequence number and
+ // the highest (highest sequence number of the newest frame)
+ // sequence number. The nack list is a subset of the range
+ // between those two numbers.
+ GetLowHighSequenceNumbers(lowSeqNum, highSeqNum);
+
+ // write a list of all seq num we have
+ if (lowSeqNum == -1 || highSeqNum == -1)
+ {
+ //This happens if we lose the first packet, nothing is popped
+ if (highSeqNum == -1)
+ {
+ // we have not received any packets yet
+ nackSize = 0;
+ }
+ else
+ {
+ // signal that we want a key frame request to be sent
+ nackSize = 0xffff;
+ }
+ return NULL;
+ }
+
+ int numberOfSeqNum = 0;
+ if (lowSeqNum > highSeqNum)
+ {
+ if (lowSeqNum - highSeqNum > 0x00ff)
+ {
+ // wrap
+ numberOfSeqNum = (0xffff-lowSeqNum) + highSeqNum + 1;
+ }
+ }
+ else
+ {
+ numberOfSeqNum = highSeqNum - lowSeqNum;
+ }
+
+ if (numberOfSeqNum > kNackHistoryLength)
+ {
+ // Nack list is too big, flush and try to restart.
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
+ VCMId(_vcmId, _receiverId),
+ "Nack list too large, try to find a key frame and restart "
+ "from seq: %d. Lowest seq in jb %d", highSeqNum,lowSeqNum);
+
+ // This nack size will trigger a key request...
+ bool foundIFrame = false;
+
+ while (numberOfSeqNum > kNackHistoryLength)
+ {
+ foundIFrame = RecycleFramesUntilKeyFrame();
+
+ if (!foundIFrame)
+ {
+ break;
+ }
+
+ // Check if we still have too many packets in JB
+ lowSeqNum = -1;
+ highSeqNum = -1;
+ GetLowHighSequenceNumbers(lowSeqNum, highSeqNum);
+
+ if (highSeqNum == -1)
+ {
+ assert(lowSeqNum != -1); // This should never happen
+ // We can't calculate the nack list length...
+ return NULL;
+ }
+
+ numberOfSeqNum = 0;
+ if (lowSeqNum > highSeqNum)
+ {
+ if (lowSeqNum - highSeqNum > 0x00ff)
+ {
+ // wrap
+ numberOfSeqNum = (0xffff-lowSeqNum) + highSeqNum + 1;
+ highSeqNum=lowSeqNum;
+ }
+ }
+ else
+ {
+ numberOfSeqNum = highSeqNum - lowSeqNum;
+ }
+
+ } // end while
+
+ if (!foundIFrame)
+ {
+ // No I frame in JB.
+
+ // Set the last decoded sequence number to current high.
+ // This is to not get a large nack list again right away
+ _lastDecodedSeqNum = highSeqNum;
+ // Set to trigger key frame signal
+ nackSize = 0xffff;
+ listExtended = true;
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1,
+ "\tNo key frame found, request one. _lastDecodedSeqNum[0] %d",
+ _lastDecodedSeqNum);
+ }
+ else
+ {
+ // We have cleaned up the jb and found a key frame
+ // The function itself has set last decoded seq.
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1,
+ "\tKey frame found. _lastDecodedSeqNum[0] %d",
+ _lastDecodedSeqNum);
+ nackSize = 0;
+ }
+
+ return NULL;
+ }
+
+ WebRtc_UWord16 seqNumberIterator = (WebRtc_UWord16)(lowSeqNum + 1);
+ for (i = 0; i < numberOfSeqNum; i++)
+ {
+ _NACKSeqNumInternal[i] = seqNumberIterator;
+ seqNumberIterator++;
+ }
+
+ // now we have a list of all sequence numbers that could have been sent
+
+ // zero out the ones we have received
+ for (i = 0; i < _maxNumberOfFrames; i++)
+ {
+ // loop all created frames
+ // We don't need to check if frame is decoding since lowSeqNum is based
+ // on _lastDecodedSeqNum
+ // Ignore free frames
+ VCMFrameBufferStateEnum state = _frameBuffers[i]->GetState();
+
+ if ((kStateFree != state) &&
+ (kStateEmpty != state) &&
+ (kStateDecoding != state))
+ {
+ // Reaching thus far means we are going to update the nack list
+ // When in hybrid mode, we also need to check empty frames, so as
+ // not to add empty packets to the nack list
+ if (_nackMode == kNackHybrid)
+ {
+ // build external rttScore based on RTT value
+ float rttScore = 1.0f;
+ _frameBuffers[i]->ZeroOutSeqNumHybrid(_NACKSeqNumInternal,
+ numberOfSeqNum,
+ rttScore);
+ if (_frameBuffers[i]->IsRetransmitted() == false)
+ {
+ // if no retransmission required,set the state to decodable
+ // meaning that we will not wait for NACK
+ _frameBuffers[i]->SetState(kStateDecodable);
+ }
+ }
+ else
+ {
+ // used when the frame is being processed by the decoding thread
+ // don't need to use that info in this loop
+ _frameBuffers[i]->ZeroOutSeqNum(_NACKSeqNumInternal,
+ numberOfSeqNum);
+ }
+ }
+ }
+
+ // compress list
+ int emptyIndex = -1;
+ for (i = 0; i < numberOfSeqNum; i++)
+ {
+ if (_NACKSeqNumInternal[i] == -1 || _NACKSeqNumInternal[i] == -2 )
+ {
+ // this is empty
+ if (emptyIndex == -1)
+ {
+ // no empty index before, remember this position
+ emptyIndex = i;
+ }
+ }
+ else
+ {
+ // this is not empty
+ if (emptyIndex == -1)
+ {
+ // no empty index, continue
+ }
+ else
+ {
+ _NACKSeqNumInternal[emptyIndex] = _NACKSeqNumInternal[i];
+ _NACKSeqNumInternal[i] = -1;
+ emptyIndex++;
+ }
+ }
+ } // for
+
+ if (emptyIndex == -1)
+ {
+ // no empty
+ nackSize = numberOfSeqNum;
+ }
+ else
+ {
+ nackSize = emptyIndex;
+ }
+ // convert to unsigned short 16 bit and store in a list to be used externally.
+ if (nackSize > _NACKSeqNumLength)
+ {
+ // Larger list means that the nack list was extended since the last call.
+ listExtended = true;
+ }
+
+ for (WebRtc_UWord32 j = 0; j < nackSize; j++)
+ {
+ // Check if the list has been extended since it was last created. I.e,
+ // new items have been added
+ if (_NACKSeqNumLength > j && !listExtended)
+ {
+ WebRtc_UWord32 k = 0;
+ for (k = j; k < _NACKSeqNumLength; k++)
+ {
+ // Found the item in the last list, i.e, no new items found yet.
+ if (_NACKSeqNum[k] == (WebRtc_UWord16)_NACKSeqNumInternal[j])
+ {
+ break;
+ }
+ }
+ if (k == _NACKSeqNumLength) // New item not found in last list.
+ {
+ listExtended = true;
+ }
+ }
+ else
+ {
+ listExtended = true;
+ }
+ _NACKSeqNum[j] = (WebRtc_UWord16)_NACKSeqNumInternal[j];
+ }
+
+ _NACKSeqNumLength = nackSize;
+
+ return _NACKSeqNum;
+}
+
+// Release frame (when done with decoding), forwards to internal function
+void
+VCMJitterBuffer::ReleaseFrame(VCMEncodedFrame* frame)
+{
+ CriticalSectionScoped cs(_critSect);
+ ReleaseFrameInternal(static_cast<VCMFrameBuffer*>(frame));
+}
+
+WebRtc_Word64
+VCMJitterBuffer::LastPacketTime(VCMEncodedFrame* frame, bool& retransmitted) const
+{
+ CriticalSectionScoped cs(_critSect);
+ retransmitted = (static_cast<VCMFrameBuffer*>(frame)->GetNackCount() > 0);
+ return static_cast<VCMFrameBuffer*>(frame)->LatestPacketTimeMs();
+}
+
+WebRtc_Word64
+VCMJitterBuffer::LastDecodedTimestamp() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return _lastDecodedTimeStamp;
+}
+
+// Insert packet
+// Takes crit sect, and inserts packet in frame buffer, possibly does logging
+VCMFrameBufferEnum
+VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet)
+{
+ CriticalSectionScoped cs(_critSect);
+ WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
+ VCMFrameBufferEnum bufferReturn = kSizeError;
+ VCMFrameBufferEnum ret = kSizeError;
+ VCMFrameBuffer* frame = static_cast<VCMFrameBuffer*>(buffer);
+
+ // Empty packets may bias the jitter estimate (lacking size component),
+ // therefore don't let empty packet trigger the following updates:
+ if (packet.frameType != kFrameEmpty)
+ {
+ if (_firstPacket)
+ {
+ // Now it's time to start estimating jitter
+ // reset the delay estimate.
+ _delayEstimate.Reset();
+ _firstPacket = false;
+ }
+
+ if (_waitingForCompletion.timestamp == packet.timestamp)
+ {
+ // This can get bad if we have a lot of duplicate packets,
+ // we will then count some packet multiple times.
+ _waitingForCompletion.frameSize += packet.sizeBytes;
+ _waitingForCompletion.latestPacketTime = nowMs;
+ }
+ else if (_waitingForCompletion.latestPacketTime >= 0 &&
+ _waitingForCompletion.latestPacketTime + 2000 <= nowMs)
+ {
+ // A packet should never be more than two seconds late
+ UpdateJitterAndDelayEstimates(_waitingForCompletion, true);
+ _waitingForCompletion.latestPacketTime = -1;
+ _waitingForCompletion.frameSize = 0;
+ _waitingForCompletion.timestamp = 0;
+ }
+ }
+
+ if (frame != NULL)
+ {
+ VCMFrameBufferStateEnum state = frame->GetState();
+ if (state == kStateDecoding && packet.sizeBytes == 0)
+ {
+ // Filler packet, make sure we update the last decoded seq num
+ // since this packet should've been a part of the frame being decoded.
+ // If the filler packet belongs to a very old frame (already decoded
+ // and freed) a new frame will be created for the filler packet.
+ // That frame will be empty and later on cleaned up.
+ UpdateLastDecodedWithFiller(packet);
+ }
+
+ // Insert packet
+ bufferReturn = frame->InsertPacket(packet, nowMs);
+ ret = bufferReturn;
+
+ if (bufferReturn > 0)
+ {
+ _incomingBitCount += packet.sizeBytes << 3;
+
+ // Has this packet been nacked or is it about to be nacked?
+ if (IsPacketRetransmitted(packet))
+ {
+ frame->IncrementNackCount();
+ }
+
+ // First packet of a frame
+ if (state == kStateEmpty)
+ {
+ if (bufferReturn > 0)
+ {
+ ret = kFirstPacket;
+ }
+ _frameBuffersTSOrder.Insert(frame);
+ }
+ }
+ }
+ switch(bufferReturn)
+ {
+ case kStateError:
+ case kTimeStampError:
+ case kSizeError:
+ {
+ // This will trigger a release in CleanUpSizeZeroFrames
+ if (frame != NULL)
+ {
+ frame->Reset();
+ frame->SetState(kStateEmpty);
+ }
+ break;
+ }
+ case kCompleteSession:
+ {
+ UpdateFrameState(frame);
+ // Signal that we have a received packet
+ _packetEvent.Set();
+ break;
+ }
+ case kIncomplete:
+ {
+ // Signal that we have a received packet
+ _packetEvent.Set();
+ break;
+ }
+ case kNoError:
+ case kDuplicatePacket:
+ {
+ break;
+ }
+ default:
+ {
+ assert(!"JitterBuffer::InsertPacket: Undefined value");
+ }
+ }
+
+ return ret;
+}
+
+void
+VCMJitterBuffer::UpdateLastDecodedWithFiller(const VCMPacket& packet)
+{
+ // Empty packet (filler) inserted to a frame which
+ // is already decoding. Update the last decoded seq no.
+ if (_lastDecodedTimeStamp == packet.timestamp &&
+ (packet.seqNum > _lastDecodedSeqNum ||
+ (packet.seqNum < 0x0fff && _lastDecodedSeqNum > 0xf000)))
+ {
+ _lastDecodedSeqNum = packet.seqNum;
+ }
+}
+
+// Must be called from within _critSect
+void
+VCMJitterBuffer::UpdateOldJitterSample(const VCMPacket& packet)
+{
+ if (_waitingForCompletion.timestamp != packet.timestamp &&
+ LatestTimestamp(_waitingForCompletion.timestamp, packet.timestamp) ==
+ packet.timestamp)
+ {
+ // This is a newer frame than the one waiting for completion.
+ _waitingForCompletion.frameSize = packet.sizeBytes;
+ _waitingForCompletion.timestamp = packet.timestamp;
+ }
+ else
+ {
+ // This can get bad if we have a lot of duplicate packets,
+ // we will then count some packet multiple times.
+ _waitingForCompletion.frameSize += packet.sizeBytes;
+ _jitterEstimate.UpdateMaxFrameSize(_waitingForCompletion.frameSize);
+ }
+}
+
+// Must be called from within _critSect
+bool
+VCMJitterBuffer::IsPacketRetransmitted(const VCMPacket& packet) const
+{
+ if (_NACKSeqNum && _NACKSeqNumLength > 0)
+ {
+ for (WebRtc_UWord16 i = 0; i < _NACKSeqNumLength; i++)
+ {
+ if (packet.seqNum == _NACKSeqNum[i])
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Get nack status (enabled/disabled)
+VCMNackMode
+VCMJitterBuffer::GetNackMode() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return _nackMode;
+}
+
+// Set NACK mode
+void
+VCMJitterBuffer::SetNackMode(VCMNackMode mode)
+{
+ CriticalSectionScoped cs(_critSect);
+ _nackMode = mode;
+ if (_nackMode == kNoNack)
+ {
+ _jitterEstimate.ResetNackCount();
+ }
+}
+
+
+// Recycle oldest frames up to a key frame, used if JB is completely full
+bool
+VCMJitterBuffer::RecycleFramesUntilKeyFrame()
+{
+ // Throw at least one frame.
+ VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First();
+ VCMFrameBuffer* oldestFrame = NULL;
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+
+ // Remove up to oldest key frame
+ bool foundIFrame = false;
+ while (oldestFrameListItem != NULL && !foundIFrame)
+ {
+ // Throw at least one frame.
+ _dropCount++;
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
+ VCMId(_vcmId, _receiverId),
+ "Jitter buffer drop count:%d, lowSeq %d", _dropCount,
+ oldestFrame->GetLowSeqNum());
+ _frameBuffersTSOrder.Erase(oldestFrameListItem);
+ RecycleFrame(oldestFrame);
+
+ oldestFrameListItem = _frameBuffersTSOrder.First();
+ if (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ }
+
+ if(oldestFrame != NULL)
+ {
+ foundIFrame = foundIFrame ||
+ (oldestFrame->FrameType() != kVideoFrameDelta);
+ if (foundIFrame)
+ {
+ // fake the last played out to match the start of this key frame
+ _lastDecodedSeqNum = (WebRtc_UWord16)((WebRtc_UWord16)
+ (oldestFrame->GetLowSeqNum()) - 1);
+ _lastDecodedTimeStamp = (WebRtc_UWord32)
+ (oldestFrame->TimeStamp() - 1);
+ break;
+ }
+ }
+ }
+ _lastDecodedSeqNum = -1;
+ return foundIFrame;
+}
+
+// Must be called under the critical section _critSect.
+void
+VCMJitterBuffer::CleanUpOldFrames()
+{
+ VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First();
+ VCMFrameBuffer* oldestFrame = NULL;
+
+ if (_lastDecodedTimeStamp == -1)
+ {
+ return;
+ }
+
+ while (oldestFrameListItem != NULL)
+ {
+ oldestFrame = oldestFrameListItem->GetItem();
+ WebRtc_UWord32 frameTimeStamp = oldestFrame->TimeStamp();
+
+ // Release the frame if it's older than the last decoded frame.
+ if (_lastDecodedTimeStamp > -1 &&
+ LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
+ frameTimeStamp)
+ == static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp))
+ {
+ const WebRtc_Word32 frameLowSeqNum = oldestFrame->GetLowSeqNum();
+ const WebRtc_Word32 frameHighSeqNum = oldestFrame->GetHighSeqNum();
+ if (frameTimeStamp == _lastDecodedTimeStamp &&
+ ((frameLowSeqNum == (_lastDecodedSeqNum + 1)) ||
+ ((frameLowSeqNum == 0) &&
+ (_lastDecodedSeqNum == 0xffff))))
+ {
+ // Could happen when sending filler data.
+ // Filler packet (size = 0) belonging to last decoded frame.
+ // Frame: | packet | packet | packet M=1 |
+ // filler data (size = 0) | filler data (size = 0)| ...
+
+ // This frame follows the last decoded frame
+ _lastDecodedSeqNum = frameHighSeqNum;
+ }
+
+ _frameBuffersTSOrder.Erase(oldestFrameListItem);
+ ReleaseFrameInternal(oldestFrame);
+ oldestFrameListItem = _frameBuffersTSOrder.First();
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+// This function has changed to use sequence numbers
+// Using timestamp won't work since can get
+// nack requests with a higher time stamp than
+// the following encoded frame, but with a lower sequence number.
+// Must be called under _critSect.
+void
+VCMJitterBuffer::CleanUpSizeZeroFrames()
+{
+ VCMFrameListItem* frameListItem = FindOldestSequenceNum();
+
+ while (frameListItem != NULL)
+ {
+ VCMFrameBuffer* ptrTempBuffer = frameListItem->GetItem();
+
+ // pop frame if its size zero but store seqnum
+ if (ptrTempBuffer->Length() == 0)
+ {
+ WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum();
+ if (frameHighSeqNum == -1)
+ {
+ // This frame has been Reset for this function to clean it up
+ _frameBuffersTSOrder.Erase(frameListItem);
+ ReleaseFrameInternal(ptrTempBuffer);
+ frameListItem = FindOldestSequenceNum();
+ }
+ else
+ {
+ bool releaseFrame = false;
+ const WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum();
+ const WebRtc_Word32 frameLowSeqNum = ptrTempBuffer->GetLowSeqNum();
+
+ if ((frameLowSeqNum == (_lastDecodedSeqNum + 1)) || // Frame is next in line
+ ((frameLowSeqNum == 0) && (_lastDecodedSeqNum== 0xffff)))
+ {
+ // This frame follows the last decoded frame, release it.
+ _lastDecodedSeqNum = frameHighSeqNum;
+ releaseFrame = true;
+ }
+ // If frameHighSeqNum < _lastDecodedSeqNum but need to take wrap into account.
+ else if(frameHighSeqNum < _lastDecodedSeqNum)
+ {
+ if (frameHighSeqNum < 0x0fff &&
+ _lastDecodedSeqNum> 0xf000)
+ {
+ // Wrap, we don't want release this one. It's newer...
+ }
+ else
+ {
+ // This frame has lower seq than last decoded,
+ // and we have no wrap -> it's older.
+ releaseFrame = true;
+ }
+ }
+ else if(frameHighSeqNum > _lastDecodedSeqNum &&
+ _lastDecodedSeqNum < 0x0fff &&
+ frameHighSeqNum > 0xf000)
+ {
+ // Higher seq than last decoded,
+ // but last decoded has recently wrapped.
+ releaseFrame = true;
+ }
+
+ if (releaseFrame)
+ {
+ _frameBuffersTSOrder.Erase(frameListItem);
+ ReleaseFrameInternal(ptrTempBuffer);
+ frameListItem = FindOldestSequenceNum();
+ }
+ else
+ {
+ // We couldn't release this one and we're using nack,
+ // stop trying...
+ frameListItem = NULL;
+ }
+ }
+ }
+ else
+ {
+ // we have a length
+ break;
+ }
+ }
+}
+
+// used in GetFrameForDecoding
+void
+VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame)
+{
+ frame.MakeSessionDecodable(); // make sure the session can be decoded.
+ if (_lastDecodedSeqNum == -1)
+ {
+ // First frame
+ frame.SetPreviousFrameLoss();
+ }
+ else if (frame.GetLowSeqNum() != ((WebRtc_UWord16)_lastDecodedSeqNum +
+ (WebRtc_UWord16)1))
+ {
+ // Frame loss
+ frame.SetPreviousFrameLoss();
+ }
+}
+
+bool
+VCMJitterBuffer::WaitForNack()
+{
+ // NACK disabled -> can't wait
+ if (_nackMode == kNoNack)
+ {
+ return false;
+ }
+ // NACK only -> always wait
+ else if (_nackMode == kNackInfinite)
+ {
+ return true;
+ }
+ // else: hybrid mode, evaluate
+ // RTT high, don't wait
+ if (_rttMs >= kHighRttNackMs)
+ {
+ return false;
+ }
+ // Either NACK only or hybrid
+ return true;
+}
+
+
+}
diff --git a/src/modules/video_coding/main/source/jitter_buffer.h b/src/modules/video_coding/main/source/jitter_buffer.h
new file mode 100644
index 0000000..ab88f38
--- /dev/null
+++ b/src/modules/video_coding/main/source/jitter_buffer.h
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_
+
+#include "typedefs.h"
+#include "critical_section_wrapper.h"
+#include "module_common_types.h"
+#include "video_coding_defines.h"
+#include "inter_frame_delay.h"
+#include "event.h"
+#include "frame_list.h"
+#include "jitter_buffer_common.h"
+#include "jitter_estimator.h"
+
+namespace webrtc
+{
+
+enum VCMNackMode
+{
+ kNackInfinite,
+ kNackHybrid,
+ kNoNack
+};
+
+// forward declarations
+class VCMFrameBuffer;
+class VCMPacket;
+class VCMEncodedFrame;
+
+class VCMJitterSample
+{
+public:
+ VCMJitterSample() : timestamp(0), frameSize(0), latestPacketTime(-1) {}
+ WebRtc_UWord32 timestamp;
+ WebRtc_UWord32 frameSize;
+ WebRtc_Word64 latestPacketTime;
+};
+
+class VCMJitterBuffer
+{
+public:
+ VCMJitterBuffer(WebRtc_Word32 vcmId = -1,
+ WebRtc_Word32 receiverId = -1,
+ bool master = true);
+ virtual ~VCMJitterBuffer();
+
+ VCMJitterBuffer& operator=(const VCMJitterBuffer& rhs);
+
+ // We need a start and stop to break out of the wait event
+ // used in GetCompleteFrameForDecoding
+ void Start();
+ void Stop();
+ bool Running() const;
+
+ // Empty the Jitter buffer of all its data
+ void Flush();
+
+ // Statistics, Get received key and delta frames
+ WebRtc_Word32 GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
+ WebRtc_UWord32& receivedKeyFrames) const;
+
+ // Statistics, Calculate frame and bit rates
+ WebRtc_Word32 GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate);
+
+ // Wait for the first packet in the next frame to arrive, blocks for <= maxWaitTimeMS ms
+ WebRtc_Word64 GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
+ FrameType& incomingFrameType,
+ WebRtc_Word64& renderTimeMs);
+
+ // Will the packet sequence be complete if the next frame is grabbed
+ // for decoding right now? That is, have we lost a frame between the
+ // last decoded frame and the next, or is the next frame missing one
+ // or more packets?
+ bool CompleteSequenceWithNextFrame();
+
+ // Wait maxWaitTimeMS for a complete frame to arrive. After timeout NULL is returned.
+ VCMEncodedFrame* GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS);
+
+ // Get a frame for decoding (even an incomplete) without delay.
+ VCMEncodedFrame* GetFrameForDecoding();
+
+ VCMEncodedFrame* GetFrameForDecodingNACK();
+
+ // Release frame (when done with decoding)
+ void ReleaseFrame(VCMEncodedFrame* frame);
+
+ // Get frame to use for this timestamp
+ WebRtc_Word32 GetFrame(const VCMPacket& packet, VCMEncodedFrame*&);
+ VCMEncodedFrame* GetFrame(const VCMPacket& packet); // deprecated
+
+ // Returns the time in ms when the latest packet was inserted into the frame.
+ // Retransmitted is set to true if any of the packets belonging to the frame
+ // has been retransmitted.
+ WebRtc_Word64 LastPacketTime(VCMEncodedFrame* frame, bool& retransmitted) const;
+
+ // Insert a packet into a frame
+ VCMFrameBufferEnum InsertPacket(VCMEncodedFrame* frame, const VCMPacket& packet);
+
+ // Sync
+ WebRtc_UWord32 GetEstimatedJitterMS();
+ void UpdateRtt(WebRtc_UWord32 rttMs);
+
+ // NACK
+ void SetNackMode(VCMNackMode mode); // Enable/disable nack
+ VCMNackMode GetNackMode() const; // Get nack mode
+ // Get list of missing sequence numbers (size in number of elements)
+ WebRtc_UWord16* GetNackList(WebRtc_UWord16& nackSize, bool& listExtended);
+
+ WebRtc_Word64 LastDecodedTimestamp() const;
+ static WebRtc_UWord32 LatestTimestamp(const WebRtc_UWord32 existingTimestamp,
+ const WebRtc_UWord32 newTimestamp);
+
+protected:
+
+ // Misc help functions
+ // Recycle (release) frame, used if we didn't receive whole frame
+ void RecycleFrame(VCMFrameBuffer* frame);
+ void ReleaseFrameInternal(VCMFrameBuffer* frame);
+ // Flush and reset the jitter buffer. Call under critical section.
+ void FlushInternal();
+ VCMFrameListItem* FindOldestSequenceNum() const;
+
+ // Help functions for insert packet
+ // Get empty frame, creates new (i.e. increases JB size) if necessary
+ VCMFrameBuffer* GetEmptyFrame();
+ // Recycle oldest frames up to a key frame, used if JB is completely full
+ bool RecycleFramesUntilKeyFrame();
+ // Update frame state (set as complete or reconstructable if conditions are met)
+ void UpdateFrameState(VCMFrameBuffer* frameListItem);
+
+ // Help functions for getting a frame
+ // Find oldest complete frame, used for getting next frame to decode
+ VCMFrameListItem* FindOldestCompleteContinuousFrame();
+
+ // Check if a frame is missing the markerbit but is complete
+ bool CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem);
+
+
+ void CleanUpOldFrames();
+ void CleanUpSizeZeroFrames();
+
+ void VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame);
+ bool IsPacketRetransmitted(const VCMPacket& packet) const;
+ void UpdateJitterAndDelayEstimates(VCMJitterSample& sample, bool incompleteFrame);
+ void UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame, bool incompleteFrame);
+ void UpdateJitterAndDelayEstimates(WebRtc_Word64 latestPacketTimeMs,
+ WebRtc_UWord32 timestamp,
+ WebRtc_UWord32 frameSize,
+ bool incompleteFrame);
+ void UpdateOldJitterSample(const VCMPacket& packet);
+ WebRtc_UWord32 GetEstimatedJitterMsInternal();
+
+ // NACK help
+ WebRtc_UWord16* CreateNackList(WebRtc_UWord16& nackSize, bool& listExtended);
+ WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
+ WebRtc_Word32& highSeqNum) const;
+
+ void UpdateLastDecodedWithFiller(const VCMPacket& packet);
+
+private:
+
+ static bool FrameEqualTimestamp(VCMFrameBuffer* frame, const void* timestamp);
+ static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
+ const void* notUsed);
+ // Decide whether should wait for NACK (mainly relevant for hybrid mode)
+ bool WaitForNack();
+
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _receiverId;
+ // If we are running (have started) or not
+ bool _running;
+ CriticalSectionWrapper& _critSect;
+ bool _master;
+ // Event to signal when we have a frame ready for decoder
+ VCMEvent _frameEvent;
+ // Event to signal when we have received a packet
+ VCMEvent _packetEvent;
+ // Number of allocated frames
+ WebRtc_Word32 _maxNumberOfFrames;
+ // Array of pointers to the frames in JB
+ VCMFrameBuffer* _frameBuffers[kMaxNumberOfFrames];
+ VCMFrameListTimestampOrderAsc _frameBuffersTSOrder;
+
+ // timing
+ // Sequence number of last frame that was given to decoder
+ WebRtc_Word32 _lastDecodedSeqNum;
+ // Timestamp of last frame that was given to decoder
+ WebRtc_Word64 _lastDecodedTimeStamp;
+
+ // Statistics
+ // Frame counter for each type (key, delta, golden, key-delta)
+ WebRtc_UWord8 _receiveStatistics[4];
+ // Latest calculated frame rates of incoming stream
+ WebRtc_UWord8 _incomingFrameRate;
+ // Frame counter, reset in GetUpdate
+ WebRtc_UWord32 _incomingFrameCount;
+ // Real time for last _frameCount reset
+ WebRtc_Word64 _timeLastIncomingFrameCount;
+ // Received bits counter, reset in GetUpdate
+ WebRtc_UWord32 _incomingBitCount;
+ WebRtc_UWord32 _incomingBitRate;
+ WebRtc_UWord32 _dropCount; // Frame drop counter
+ // Number of frames in a row that have been too old
+ WebRtc_UWord32 _numConsecutiveOldFrames;
+ // Number of packets in a row that have been too old
+ WebRtc_UWord32 _numConsecutiveOldPackets;
+ // Filters for estimating jitter
+ VCMJitterEstimator _jitterEstimate;
+ // Calculates network delays used for jitter calculations
+ VCMInterFrameDelay _delayEstimate;
+ VCMJitterSample _waitingForCompletion;
+ WebRtc_UWord32 _rttMs;
+
+ // NACK
+ VCMNackMode _nackMode;
+ // Holds the internal nack list (the missing seqence numbers)
+ WebRtc_Word32 _NACKSeqNumInternal[kNackHistoryLength];
+ WebRtc_UWord16 _NACKSeqNum[kNackHistoryLength];
+ WebRtc_UWord32 _NACKSeqNumLength;
+
+ bool _missingMarkerBits;
+ bool _firstPacket;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_
diff --git a/src/modules/video_coding/main/source/jitter_buffer_common.h b/src/modules/video_coding/main/source/jitter_buffer_common.h
new file mode 100644
index 0000000..035a3c1
--- /dev/null
+++ b/src/modules/video_coding/main/source/jitter_buffer_common.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
+#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
+
+namespace webrtc
+{
+
+enum { kMaxNumberOfFrames = 100 };
+enum { kStartNumberOfFrames = 6 }; // in packets, 6 packets are approximately 198 ms,
+ // we need at least one more for process
+enum { kMaxVideoDelayMs = 2000 }; // in ms
+
+enum VCMJitterBufferEnum
+{
+ kMaxConsecutiveOldFrames = 60,
+ kMaxConsecutiveOldPackets = 300,
+ kMaxPacketsInJitterBuffer = 800,
+ kBufferIncStepSizeBytes = 30000, // >20 packets
+ kMaxJBFrameSizeBytes = 4000000 // sanity don't go above 4Mbyte
+};
+
+enum VCMFrameBufferEnum
+{
+ kStateError = -4,
+ kTimeStampError = -2,
+ kSizeError = -1,
+ kNoError = 0,
+ kIncomplete = 1, // Frame incomplete
+ kFirstPacket = 2,
+ kCompleteSession = 3, // at least one layer in the frame complete
+ kDuplicatePacket = 5 // We're receiving a duplicate packet.
+};
+
+enum VCMFrameBufferStateEnum
+{
+ kStateFree, // Unused frame in the JB
+ kStateEmpty, // frame popped by the RTP receiver
+ kStateIncomplete, // frame that have one or more packet(s) stored
+ kStateComplete, // frame that have all packets
+ kStateDecoding, // frame popped by the decoding thread
+ kStateDecodable // Hybrid mode - frame can be decoded
+};
+
+enum { kH264StartCodeLengthBytes = 4};
+
+// Used to indicate if a received packet contain a complete NALU (or equivalent)
+enum VCMNaluCompleteness
+{
+ kNaluUnset = 0, //Packet has not been filled.
+ kNaluComplete = 1, //Packet can be decoded as is.
+ kNaluStart, // Packet contain beginning of NALU
+ kNaluIncomplete, //Packet is not beginning or end of NALU
+ kNaluEnd, // Packet is the end of a NALU
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
diff --git a/src/modules/video_coding/main/source/jitter_estimator.cc b/src/modules/video_coding/main/source/jitter_estimator.cc
new file mode 100644
index 0000000..233fad4
--- /dev/null
+++ b/src/modules/video_coding/main/source/jitter_estimator.cc
@@ -0,0 +1,422 @@
+/*
+ * 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 "trace.h"
+#include "internal_defines.h"
+#include "jitter_estimator.h"
+#include "rtt_filter.h"
+#include "tick_time.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace webrtc {
+
+VCMJitterEstimator::VCMJitterEstimator(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId) :
+_vcmId(vcmId),
+_receiverId(receiverId),
+_phi(0.97),
+_psi(0.9999),
+_alphaCountMax(400),
+_beta(0.9994),
+_thetaLow(0.000001),
+_nackLimit(3),
+_numStdDevDelayOutlier(15),
+_numStdDevFrameSizeOutlier(3),
+_noiseStdDevs(2.33), // ~Less than 1% chance
+ // (look up in normal distribution table)...
+_noiseStdDevOffset(30.0), // ...of getting 30 ms freezes
+_rttFilter(vcmId, receiverId)
+{
+ Reset();
+}
+
+VCMJitterEstimator&
+VCMJitterEstimator::operator=(const VCMJitterEstimator& rhs)
+{
+ if (this != &rhs)
+ {
+ memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
+ memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
+
+ _vcmId = rhs._vcmId;
+ _receiverId = rhs._receiverId;
+ _avgFrameSize = rhs._avgFrameSize;
+ _varFrameSize = rhs._varFrameSize;
+ _maxFrameSize = rhs._maxFrameSize;
+ _fsSum = rhs._fsSum;
+ _fsCount = rhs._fsCount;
+ _lastUpdateT = rhs._lastUpdateT;
+ _prevEstimate = rhs._prevEstimate;
+ _prevFrameSize = rhs._prevFrameSize;
+ _avgNoise = rhs._avgNoise;
+ _alphaCount = rhs._alphaCount;
+ _filterJitterEstimate = rhs._filterJitterEstimate;
+ _startupCount = rhs._startupCount;
+ _latestNackTimestamp = rhs._latestNackTimestamp;
+ _nackCount = rhs._nackCount;
+ _rttFilter = rhs._rttFilter;
+ }
+ return *this;
+}
+
+// Resets the JitterEstimate
+void
+VCMJitterEstimator::Reset()
+{
+ _theta[0] = 1/(512e3/8);
+ _theta[1] = 0;
+ _varNoise = 4.0;
+
+ _thetaCov[0][0] = 1e-4;
+ _thetaCov[1][1] = 1e2;
+ _thetaCov[0][1] = _thetaCov[1][0] = 0;
+ _Qcov[0][0] = 2.5e-10;
+ _Qcov[1][1] = 1e-10;
+ _Qcov[0][1] = _Qcov[1][0] = 0;
+ _avgFrameSize = 500;
+ _maxFrameSize = 500;
+ _varFrameSize = 100;
+ _lastUpdateT = -1;
+ _prevEstimate = -1.0;
+ _prevFrameSize = 0;
+ _avgNoise = 0.0;
+ _alphaCount = 1;
+ _filterJitterEstimate = 0.0;
+ _latestNackTimestamp = 0;
+ _nackCount = 0;
+ _fsSum = 0;
+ _fsCount = 0;
+ _startupCount = 0;
+ _rttFilter.Reset();
+}
+
+void
+VCMJitterEstimator::ResetNackCount()
+{
+ _nackCount = 0;
+}
+
+// Updates the estimates with the new measurements
+void
+VCMJitterEstimator::UpdateEstimate(WebRtc_Word64 frameDelayMS, WebRtc_UWord32 frameSizeBytes,
+ bool incompleteFrame /* = false */)
+{
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding,
+ VCMId(_vcmId, _receiverId),
+ "Jitter estimate updated with: frameSize=%d frameDelayMS=%d",
+ frameSizeBytes, frameDelayMS);
+ if (frameSizeBytes == 0)
+ {
+ return;
+ }
+ int deltaFS = frameSizeBytes - _prevFrameSize;
+ if (_fsCount < kFsAccuStartupSamples)
+ {
+ _fsSum += frameSizeBytes;
+ _fsCount++;
+ }
+ else if (_fsCount == kFsAccuStartupSamples)
+ {
+ // Give the frame size filter
+ _avgFrameSize = static_cast<double>(_fsSum) /
+ static_cast<double>(_fsCount);
+ _fsCount++;
+ }
+ if (!incompleteFrame || frameSizeBytes > _avgFrameSize)
+ {
+ double avgFrameSize = _phi * _avgFrameSize +
+ (1 - _phi) * frameSizeBytes;
+ if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize))
+ {
+ // Only update the average frame size if this sample wasn't a
+ // key frame
+ _avgFrameSize = avgFrameSize;
+ }
+ // Update the variance anyway since we want to capture cases where we only get
+ // key frames.
+ _varFrameSize = VCM_MAX(_phi * _varFrameSize + (1 - _phi) *
+ (frameSizeBytes - avgFrameSize) *
+ (frameSizeBytes - avgFrameSize), 1.0);
+ }
+
+ // Update max frameSize estimate
+ _maxFrameSize = VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
+
+ if (_prevFrameSize == 0)
+ {
+ _prevFrameSize = frameSizeBytes;
+ return;
+ }
+ _prevFrameSize = frameSizeBytes;
+
+ // Only update the Kalman filter if the sample is not considered
+ // an extreme outlier. Even if it is an extreme outlier from a
+ // delay point of view, if the frame size also is large the
+ // deviation is probably due to an incorrect line slope.
+ double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
+
+ if (abs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
+ frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize))
+ {
+ // Update the variance of the deviation from the
+ // line given by the Kalman filter
+ EstimateRandomJitter(deviation, incompleteFrame);
+ // Prevent updating with frames which have been congested by a large
+ // frame, and therefore arrives almost at the same time as that frame.
+ // This can occur when we receive a large frame (key frame) which
+ // has been delayed. The next frame is of normal size (delta frame),
+ // and thus deltaFS will be << 0. This removes all frame samples
+ // which arrives after a key frame.
+ if ((!incompleteFrame || deviation >= 0.0) &&
+ static_cast<double>(deltaFS) > - 0.25 * _maxFrameSize)
+ {
+ // Update the Kalman filter with the new data
+ KalmanEstimateChannel(frameDelayMS, deltaFS);
+ }
+ }
+ else
+ {
+ int nStdDev = (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
+ EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
+ }
+ // Post process the total estimated jitter
+ if (_startupCount >= kStartupDelaySamples)
+ {
+ PostProcessEstimate();
+ }
+ else
+ {
+ _startupCount++;
+ }
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Framesize statistics: max=%f average=%f", _maxFrameSize, _avgFrameSize);
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "The estimated slope is: theta=(%f, %f)", _theta[0], _theta[1]);
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Random jitter: mean=%f variance=%f", _avgNoise, _varNoise);
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Current jitter estimate: %f", _filterJitterEstimate);
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Current max RTT: %u", _rttFilter.RttMs());
+}
+
+// Updates the nack/packet ratio
+void
+VCMJitterEstimator::FrameNacked()
+{
+ // Wait until _nackLimit retransmissions has been received,
+ // then always add ~1 RTT delay.
+ // TODO(holmer): Should we ever remove the additional delay if the
+ // the packet losses seem to have stopped? We could for instance scale
+ // the number of RTTs to add with the amount of retransmissions in a given
+ // time interval, or similar.
+ if (_nackCount < _nackLimit)
+ {
+ _nackCount++;
+ }
+}
+
+// Updates Kalman estimate of the channel
+// The caller is expected to sanity check the inputs.
+void
+VCMJitterEstimator::KalmanEstimateChannel(WebRtc_Word64 frameDelayMS,
+ WebRtc_Word32 deltaFSBytes)
+{
+ double Mh[2];
+ double hMh_sigma;
+ double kalmanGain[2];
+ double measureRes;
+ double t00, t01;
+
+ // Kalman filtering
+
+ // Prediction
+ // M = M + Q
+ _thetaCov[0][0] += _Qcov[0][0];
+ _thetaCov[0][1] += _Qcov[0][1];
+ _thetaCov[1][0] += _Qcov[1][0];
+ _thetaCov[1][1] += _Qcov[1][1];
+
+ // Kalman gain
+ // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
+ // h = [dFS 1]
+ // Mh = M*h'
+ // hMh_sigma = h*M*h' + R
+ Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
+ Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
+ // sigma weights measurements with a small deltaFS as noisy and
+ // measurements with large deltaFS as good
+ if (_maxFrameSize < 1.0)
+ {
+ return;
+ }
+ double sigma = (300.0 * exp(-abs(static_cast<double>(deltaFSBytes)) /
+ (1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise);
+ if (sigma < 1.0)
+ {
+ sigma = 1.0;
+ }
+ hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
+ if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0))
+ {
+ assert(false);
+ return;
+ }
+ kalmanGain[0] = Mh[0] / hMh_sigma;
+ kalmanGain[1] = Mh[1] / hMh_sigma;
+
+ // Correction
+ // theta = theta + K*(dT - h*theta)
+ measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
+ _theta[0] += kalmanGain[0] * measureRes;
+ _theta[1] += kalmanGain[1] * measureRes;
+
+ if (_theta[0] < _thetaLow)
+ {
+ _theta[0] = _thetaLow;
+ }
+
+ // M = (I - K*h)*M
+ t00 = _thetaCov[0][0];
+ t01 = _thetaCov[0][1];
+ _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
+ kalmanGain[0] * _thetaCov[1][0];
+ _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
+ kalmanGain[0] * _thetaCov[1][1];
+ _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
+ kalmanGain[1] * deltaFSBytes * t00;
+ _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
+ kalmanGain[1] * deltaFSBytes * t01;
+
+ // Covariance matrix, must be positive semi-definite
+ assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
+ _thetaCov[0][0] * _thetaCov[1][1] - _thetaCov[0][1] * _thetaCov[1][0] >= 0 &&
+ _thetaCov[0][0] >= 0);
+}
+
+// Calculate difference in delay between a sample and the
+// expected delay estimated by the Kalman filter
+double
+VCMJitterEstimator::DeviationFromExpectedDelay(WebRtc_Word64 frameDelayMS,
+ WebRtc_Word32 deltaFSBytes) const
+{
+ return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
+}
+
+// Estimates the random jitter by calculating the variance of the
+// sample distance from the line given by theta.
+void
+VCMJitterEstimator::EstimateRandomJitter(double d_dT, bool incompleteFrame)
+{
+ double alpha;
+ if (_alphaCount == 0)
+ {
+ assert(_alphaCount > 0);
+ return;
+ }
+ alpha = static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
+ _alphaCount++;
+ if (_alphaCount > _alphaCountMax)
+ {
+ _alphaCount = _alphaCountMax;
+ }
+ double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
+ double varNoise = alpha * _varNoise +
+ (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
+ if (!incompleteFrame || varNoise > _varNoise)
+ {
+ _avgNoise = avgNoise;
+ _varNoise = varNoise;
+ }
+ if (_varNoise < 1.0)
+ {
+ // The variance should never be zero, since we might get
+ // stuck and consider all samples as outliers.
+ _varNoise = 1.0;
+ }
+}
+
+double
+VCMJitterEstimator::NoiseThreshold() const
+{
+ double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
+ if (noiseThreshold < 1.0)
+ {
+ noiseThreshold = 1.0;
+ }
+ return noiseThreshold;
+}
+
+// Calculates the current jitter estimate from the filtered estimates
+double
+VCMJitterEstimator::CalculateEstimate()
+{
+ double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
+
+ // A very low estimate (or negative) is neglected
+ if (ret < 1.0) {
+ if (_prevEstimate <= 0.01)
+ {
+ ret = 1.0;
+ }
+ else
+ {
+ ret = _prevEstimate;
+ }
+ }
+ if (ret > 10000.0) // Sanity
+ {
+ ret = 10000.0;
+ }
+ _prevEstimate = ret;
+ return ret;
+}
+
+void
+VCMJitterEstimator::PostProcessEstimate()
+{
+ _filterJitterEstimate = CalculateEstimate();
+}
+
+void
+VCMJitterEstimator::UpdateRtt(WebRtc_UWord32 rttMs)
+{
+ _rttFilter.Update(rttMs);
+}
+
+void
+VCMJitterEstimator::UpdateMaxFrameSize(WebRtc_UWord32 frameSizeBytes)
+{
+ if (_maxFrameSize < frameSizeBytes)
+ {
+ _maxFrameSize = frameSizeBytes;
+ }
+}
+
+// Returns the current filtered estimate if available,
+// otherwise tries to calculate an estimate.
+double
+VCMJitterEstimator::GetJitterEstimate(double rttMultiplier)
+{
+ double jitterMS = CalculateEstimate();
+ if (_filterJitterEstimate > jitterMS)
+ {
+ jitterMS = _filterJitterEstimate;
+ }
+ if (_nackCount >= _nackLimit)
+ {
+ return jitterMS + _rttFilter.RttMs() * rttMultiplier;
+ }
+ return jitterMS;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/jitter_estimator.h b/src/modules/video_coding/main/source/jitter_estimator.h
new file mode 100644
index 0000000..6fc4703
--- /dev/null
+++ b/src/modules/video_coding/main/source/jitter_estimator.h
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_
+#define WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_
+
+#include "typedefs.h"
+#include "rtt_filter.h"
+
+namespace webrtc
+{
+
+class VCMJitterEstimator
+{
+public:
+ VCMJitterEstimator(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0);
+
+ VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs);
+
+ // Resets the estimate to the initial state
+ void Reset();
+ void ResetNackCount();
+
+ // Updates the jitter estimate with the new data.
+ //
+ // Input:
+ // - frameDelay : Delay-delta calculated by UTILDelayEstimate in milliseconds
+ // - frameSize : Frame size of the current frame.
+ // - incompleteFrame : Flags if the frame is used to update the estimate before it
+ // was complete. Default is false.
+ void UpdateEstimate(WebRtc_Word64 frameDelayMS,
+ WebRtc_UWord32 frameSizeBytes,
+ bool incompleteFrame = false);
+
+ // Returns the current jitter estimate in milliseconds and adds
+ // also adds an RTT dependent term in cases of retransmission.
+ // Input:
+ // - rttMultiplier : RTT param multiplier (when applicable).
+ //
+ // Return value : Jitter estimate in milliseconds
+ double GetJitterEstimate(double rttMultiplier);
+
+ // Updates the nack counter.
+ void FrameNacked();
+
+ // Updates the RTT filter.
+ //
+ // Input:
+ // - rttMs : RTT in ms
+ void UpdateRtt(WebRtc_UWord32 rttMs);
+
+ void UpdateMaxFrameSize(WebRtc_UWord32 frameSizeBytes);
+
+ // A constant describing the delay from the jitter buffer
+ // to the delay on the receiving side which is not accounted
+ // for by the jitter buffer nor the decoding delay estimate.
+ static const WebRtc_UWord32 OPERATING_SYSTEM_JITTER = 10;
+
+protected:
+ // These are protected for better testing possibilities
+ double _theta[2]; // Estimated line parameters (slope, offset)
+ double _varNoise; // Variance of the time-deviation from the line
+
+private:
+ // Updates the Kalman filter for the line describing
+ // the frame size dependent jitter.
+ //
+ // Input:
+ // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in milliseconds
+ // - deltaFSBytes : Frame size delta, i.e.
+ // : frame size at time T minus frame size at time T-1
+ void KalmanEstimateChannel(WebRtc_Word64 frameDelayMS, WebRtc_Word32 deltaFSBytes);
+
+ // Updates the random jitter estimate, i.e. the variance
+ // of the time deviations from the line given by the Kalman filter.
+ //
+ // Input:
+ // - d_dT : The deviation from the kalman estimate
+ // - incompleteFrame : True if the frame used to update the estimate
+ // with was incomplete
+ void EstimateRandomJitter(double d_dT, bool incompleteFrame);
+
+ double NoiseThreshold() const;
+
+ // Calculates the current jitter estimate.
+ //
+ // Return value : The current jitter estimate in milliseconds
+ double CalculateEstimate();
+
+ // Post process the calculated estimate
+ void PostProcessEstimate();
+
+ // Calculates the difference in delay between a sample and the
+ // expected delay estimated by the Kalman filter.
+ //
+ // Input:
+ // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in milliseconds
+ // - deltaFS : Frame size delta, i.e. frame size at time
+ // T minus frame size at time T-1
+ //
+ // Return value : The difference in milliseconds
+ double DeviationFromExpectedDelay(WebRtc_Word64 frameDelayMS,
+ WebRtc_Word32 deltaFSBytes) const;
+
+ // Constants, filter parameters
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _receiverId;
+ const double _phi;
+ const double _psi;
+ const WebRtc_UWord32 _alphaCountMax;
+ const double _beta;
+ const double _thetaLow;
+ const WebRtc_UWord32 _nackLimit;
+ const WebRtc_Word32 _numStdDevDelayOutlier;
+ const WebRtc_Word32 _numStdDevFrameSizeOutlier;
+ const double _noiseStdDevs;
+ const double _noiseStdDevOffset;
+
+ double _thetaCov[2][2]; // Estimate covariance
+ double _Qcov[2][2]; // Process noise covariance
+ double _avgFrameSize; // Average frame size
+ double _varFrameSize; // Frame size variance
+ double _maxFrameSize; // Largest frame size received (descending
+ // with a factor _psi)
+ WebRtc_UWord32 _fsSum;
+ WebRtc_UWord32 _fsCount;
+
+ WebRtc_Word64 _lastUpdateT;
+ double _prevEstimate; // The previously returned jitter estimate
+ WebRtc_UWord32 _prevFrameSize; // Frame size of the previous frame
+ double _avgNoise; // Average of the random jitter
+ WebRtc_UWord32 _alphaCount;
+ double _filterJitterEstimate; // The filtered sum of jitter estimates
+
+ WebRtc_UWord32 _startupCount;
+
+ WebRtc_Word64 _latestNackTimestamp; // Timestamp in ms when the latest nack was seen
+ WebRtc_UWord32 _nackCount; // Keeps track of the number of nacks received,
+ // but never goes above _nackLimit
+ VCMRttFilter _rttFilter;
+
+ enum { kStartupDelaySamples = 30 };
+ enum { kFsAccuStartupSamples = 5 };
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_
diff --git a/src/modules/video_coding/main/source/media_opt_util.cc b/src/modules/video_coding/main/source/media_opt_util.cc
new file mode 100644
index 0000000..a0937b6
--- /dev/null
+++ b/src/modules/video_coding/main/source/media_opt_util.cc
@@ -0,0 +1,943 @@
+/*
+ * 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 "video_coding_defines.h"
+#include "fec_tables_xor.h"
+#include "er_tables_xor.h"
+#include "nack_fec_tables.h"
+#include "qm_select_data.h"
+#include "media_opt_util.h"
+
+#include <math.h>
+#include <float.h>
+#include <limits.h>
+
+namespace webrtc {
+
+bool
+VCMProtectionMethod::BetterThan(VCMProtectionMethod *pm)
+{
+ if (pm == NULL)
+ {
+ return true;
+ }
+ return pm->_score > _score;
+}
+
+bool
+VCMNackFecMethod::ProtectionFactor(const VCMProtectionParameters* /*parameters*/)
+{
+ // use FEC model with modification with RTT for now
+ return true;
+}
+
+bool
+VCMNackFecMethod::EffectivePacketLoss(const
+ VCMProtectionParameters* /*parameters*/)
+{
+ // use FEC model with modification with RTT for now
+ return true;
+}
+
+bool
+VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
+{
+ // Hybrid Nack FEC has three operational modes:
+ // 1. Low RTT - Nack only (Set FEC rates to zero)
+ // 2. High RTT - FEC Only
+ // 3. Medium RTT values - Hybrid ; in hybrid mode, we will only nack the
+ // residual following the decoding of the FEC (refer to JB logic)
+
+ // Low RTT - NACK only mode
+ if (parameters->rtt < kLowRttNackMs)
+ {
+ // Set the FEC parameters to 0
+ _protectionFactorK = 0;
+ _protectionFactorD = 0;
+
+ // assume packets will be restored via NACK
+ // TODO: relax this assumption?
+ _effectivePacketLoss = 0;
+ _score = _efficiency;
+ return true;
+ }
+ // otherwise: we count on FEC; if the RTT is below a threshold, then we can
+ // nack the residual, based on a decision made in the JB.
+ // TODO(mikhal): adapt the FEC rate based on the RTT, i.e. the the level on
+ // which we will rely on NACK, e.g. less as we approach upper threshold.
+ VCMFecMethod fecMethod;
+
+ const WebRtc_UWord8 plossMax = 129;
+
+ // Compute the protection factor
+ fecMethod.ProtectionFactor(parameters);
+
+ // Compute the effective packet loss
+ fecMethod.EffectivePacketLoss(parameters);
+
+ WebRtc_UWord8 protFactorK = fecMethod._protectionFactorK;
+ WebRtc_UWord8 protFactorD = fecMethod._protectionFactorD;
+ WebRtc_UWord8 effPacketLoss = fecMethod._effectivePacketLoss;
+ float resPacketLoss = fecMethod._residualPacketLoss;
+
+ // Correct FEC rates based on the RTT ( NACK effectiveness)
+ WebRtc_Word16 rttIndex= (WebRtc_UWord16) parameters->rtt;
+ float softnessRtt = 1.0;
+ if (parameters->rtt < kHighRttNackMs)
+ {
+ // TODO(mikhal): update table
+ softnessRtt = (float)VCMNackFecTable[rttIndex] / (float)4096.0;
+
+ // soften ER with NACK on
+ // table depends on RTT relative to rttMax (NACK Threshold)
+ _effectivePacketLoss = (WebRtc_UWord8)(effPacketLoss * softnessRtt);
+
+ // soften FEC with NACK on
+ // table depends on RTT relative to rttMax (NACK Threshold)
+ _protectionFactorK = (WebRtc_UWord8) (protFactorK * softnessRtt);
+ _protectionFactorD = (WebRtc_UWord8) (protFactorD * softnessRtt);
+ }
+ // else - NACK is disabled, rely on FEC only
+
+
+ // make sure I frame protection is at least larger than P frame protection,
+ // and at least as high as received loss
+ WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
+ _protectionFactorK = static_cast<WebRtc_UWord8> (VCM_MAX(packetLoss,
+ VCM_MAX(_scaleProtKey * protFactorD, protFactorK)));
+
+ // check limit on amount of protection for I frame: 50% is max
+ if (_protectionFactorK >= plossMax)
+ _protectionFactorK = plossMax - 1;
+
+ // Bit cost for NackFec
+
+ // NACK cost: based on residual packet loss (since we should only NACK
+ // packets not recovered by FEC)
+ _efficiency = 0.0f;
+ if (parameters->rtt < kHighRttNackMs)
+ {
+ _efficiency = parameters->bitRate * resPacketLoss /
+ (1.0f + resPacketLoss);
+ }
+ else
+ {
+ // efficiency based on FEC only
+ // add FEC cost: ignore I frames for now
+ float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
+ if (fecRate >= 0.0f)
+ _efficiency += parameters->bitRate * fecRate;
+ }
+ _score = _efficiency;
+
+ // Protection/fec rates obtained above are defined relative to total number
+ // of packets (total rate: source + fec) FEC in RTP module assumes
+ // protection factor is defined relative to source number of packets so we
+ // should convert the factor to reduce mismatch between mediaOpt's rate and
+ // the actual one
+ WebRtc_UWord8 codeRate = protFactorK;
+ _protectionFactorK = fecMethod.ConvertFECRate(codeRate);
+ codeRate = protFactorD;
+ _protectionFactorD = fecMethod.ConvertFECRate(codeRate);
+
+ return true;
+}
+
+bool
+VCMNackMethod::EffectivePacketLoss(WebRtc_UWord8 effPacketLoss,
+ WebRtc_UWord16 rttTime)
+{
+ WebRtc_UWord16 rttMax = MaxRttNack();
+
+ // For large RTT, we should rely on some Error Resilience, so we set
+ // packetLossEnc = 0 for RTT less than the NACK threshold
+ if (rttTime < rttMax)
+ {
+ effPacketLoss = 0; //may want a softer transition here
+ }
+ _effectivePacketLoss = effPacketLoss;
+
+ return true;
+}
+
+bool
+VCMNackMethod::UpdateParameters(const VCMProtectionParameters* parameters)
+{
+ // Compute the effective packet loss for ER
+ WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
+ WebRtc_UWord16 rttTime = (WebRtc_UWord16) parameters->rtt;
+ EffectivePacketLoss(effPacketLoss, rttTime);
+
+ // Compute the NACK bit cost
+ _efficiency = parameters->bitRate * parameters->lossPr /
+ (1.0f + parameters->lossPr);
+ _score = _efficiency;
+ if (parameters->rtt > _NACK_MAX_RTT)
+ {
+ _score = 0.0f;
+ return false;
+ }
+ return true;
+}
+
+WebRtc_UWord8
+VCMFecMethod::BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta,
+ WebRtc_UWord8 packetFrameKey) const
+{
+ WebRtc_UWord8 boostRateKey = 2;
+ // default: ratio scales the FEC protection up for I frames
+ WebRtc_UWord8 ratio = 1;
+
+ if (packetFrameDelta > 0)
+ {
+ ratio = (WebRtc_Word8) (packetFrameKey / packetFrameDelta);
+ }
+ ratio = VCM_MAX(boostRateKey, ratio);
+
+ return ratio;
+}
+
+WebRtc_UWord8
+VCMFecMethod::ConvertFECRate(WebRtc_UWord8 codeRateRTP) const
+{
+ return static_cast<WebRtc_UWord8> (VCM_MIN(255,(0.5 + 255.0 * codeRateRTP /
+ (float)(255 - codeRateRTP))));
+}
+
+// AvgRecoveryFEC: average recovery from FEC, assuming random packet loss model
+// Computed offline for a range of FEC code parameters and loss rates
+float
+VCMFecMethod::AvgRecoveryFEC(const VCMProtectionParameters* parameters) const
+{
+ // Total (avg) bits available per frame: total rate over actual/sent frame
+ // rate units are kbits/frame
+ const WebRtc_UWord16 bitRatePerFrame = static_cast<WebRtc_UWord16>
+ (parameters->bitRate / (parameters->frameRate));
+
+ // Total (avg) number of packets per frame (source and fec):
+ const WebRtc_UWord8 avgTotPackets = 1 +
+ (WebRtc_UWord8) ((float) bitRatePerFrame * 1000.0
+ / (float) (8.0 * _maxPayloadSize) + 0.5);
+
+ // parameters for tables
+ const WebRtc_UWord8 codeSize = 24;
+ const WebRtc_UWord8 plossMax = 129;
+ const WebRtc_UWord16 maxErTableSize = 38700;
+
+ // Get index for table
+ const float protectionFactor = (float) _protectionFactorD / (float) 255;
+ WebRtc_UWord8 fecPacketsPerFrame = (WebRtc_UWord8) (0.5 + protectionFactor
+ * avgTotPackets);
+ WebRtc_UWord8 sourcePacketsPerFrame = avgTotPackets - fecPacketsPerFrame;
+
+ if (fecPacketsPerFrame == 0)
+ {
+ return 0.0; // no protection, so avg. recov from FEC == 0
+ }
+
+ // table defined up to codeSizexcodeSize code
+ if (sourcePacketsPerFrame > codeSize)
+ {
+ sourcePacketsPerFrame = codeSize;
+ }
+
+ // check: protection factor is maxed at 50%, so this should never happen
+ if (sourcePacketsPerFrame < 1)
+ {
+ assert("average number of source packets below 1\n");
+ }
+
+ // index for ER tables: up to codeSizexcodeSize mask
+ WebRtc_UWord16 codeIndexTable[codeSize * codeSize];
+ WebRtc_UWord16 k = -1;
+ for (WebRtc_UWord8 i = 1; i <= codeSize; i++)
+ {
+ for (WebRtc_UWord8 j = 1; j <= i; j++)
+ {
+ k += 1;
+ codeIndexTable[(j - 1) * codeSize + i - 1] = k;
+ }
+ }
+
+ const WebRtc_UWord8 lossRate = (WebRtc_UWord8) (255.0 *
+ parameters->lossPr + 0.5f);
+
+ const WebRtc_UWord16 codeIndex = (fecPacketsPerFrame - 1) * codeSize
+ + (sourcePacketsPerFrame - 1);
+ const WebRtc_UWord16 indexTable = codeIndexTable[codeIndex] * plossMax
+ + lossRate;
+
+ const WebRtc_UWord16 codeIndex2 = (fecPacketsPerFrame) * codeSize
+ + (sourcePacketsPerFrame);
+ WebRtc_UWord16 indexTable2 = codeIndexTable[codeIndex2] * plossMax
+ + lossRate;
+
+ // checks on table index
+ if (indexTable >= maxErTableSize)
+ {
+ assert("ER table index too large\n");
+ }
+
+ if (indexTable2 >= maxErTableSize)
+ {
+ indexTable2 = indexTable;
+ }
+
+ // Get the average effective packet loss recovery from FEC
+ // this is from tables, computed using random loss model
+ WebRtc_UWord8 avgFecRecov1 = 0;
+ WebRtc_UWord8 avgFecRecov2 = 0;
+ float avgFecRecov = 0;
+
+ if (fecPacketsPerFrame > 0)
+ {
+ avgFecRecov1 = VCMAvgFECRecoveryXOR[indexTable];
+ avgFecRecov2 = VCMAvgFECRecoveryXOR[indexTable2];
+ }
+
+ // interpolate over two FEC codes
+ const float weightRpl = (float) (0.5 + protectionFactor * avgTotPackets)
+ - (float) fecPacketsPerFrame;
+ avgFecRecov = (float) weightRpl * (float) avgFecRecov2 + (float)
+ (1.0 - weightRpl) * (float) avgFecRecov1;
+
+ return avgFecRecov;
+}
+
+bool
+VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters)
+{
+ // FEC PROTECTION SETTINGS: varies with packet loss and bitrate
+
+ WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
+
+ // No protection if (filtered) packetLoss is 0
+ if (packetLoss == 0)
+ {
+ _protectionFactorK = 0;
+ _protectionFactorD = 0;
+ return true;
+ }
+
+ // Size of tables
+ const WebRtc_UWord16 maxFecTableSize = 6450;
+ // Parameters for range of rate and packet loss for tables
+ const WebRtc_UWord8 ratePar1 = 5;
+ const WebRtc_UWord8 ratePar2 = 49;
+ const WebRtc_UWord8 plossMax = 129;
+
+ const float bitRate = parameters->bitRate;
+
+ // Total (avg) bits available per frame: total rate over actual/frame_rate.
+ // Units are kbits/frame
+ const WebRtc_UWord16 bitRatePerFrame = static_cast<WebRtc_UWord16>
+ (bitRate /
+ (parameters->frameRate));
+
+ // TODO (marpan): Incorporate frame size (bpp) into FEC setting
+
+ // Total (avg) number of packets per frame (source and fec):
+ const WebRtc_UWord8 avgTotPackets = 1 + (WebRtc_UWord8)
+ ((float) bitRatePerFrame * 1000.0
+ / (float) (8.0 * _maxPayloadSize) + 0.5);
+
+
+ // First partition protection: ~ 20%
+ WebRtc_UWord8 firstPartitionProt = (WebRtc_UWord8) (255 * 0.20);
+
+ // Threshold on packetLoss and bitRrate/frameRate (=average #packets),
+ // above which we allocate protection to cover at least roughly
+ // first partition size.
+ WebRtc_UWord8 lossThr = 0;
+ WebRtc_UWord8 packetNumThr = 1;
+
+ // Modulation of protection with available bits/frame (or avgTotpackets)
+ float weight1 = 0.5;
+ float weight2 = 0.5;
+ if (avgTotPackets > 4)
+ {
+ weight1 = 0.75;
+ weight2 = 0.25;
+ }
+ if (avgTotPackets > 6)
+ {
+ weight1 = 1.5;
+ weight2 = 0.;
+ }
+
+ // FEC rate parameters: for P and I frame
+ WebRtc_UWord8 codeRateDelta = 0;
+ WebRtc_UWord8 codeRateKey = 0;
+
+ // Get index for table: the FEC protection depends on the (average)
+ // available bits/frame. The range on the rate index corresponds to rates
+ // (bps) from 200k to 8000k, for 30fps
+ WebRtc_UWord8 rateIndexTable =
+ (WebRtc_UWord8) VCM_MAX(VCM_MIN((bitRatePerFrame - ratePar1) /
+ ratePar1, ratePar2), 0);
+
+ // Restrict packet loss range to 50:
+ // current tables defined only up to 50%
+ if (packetLoss >= plossMax)
+ {
+ packetLoss = plossMax - 1;
+ }
+ WebRtc_UWord16 indexTable = rateIndexTable * plossMax + packetLoss;
+
+ // Check on table index
+ assert(indexTable < maxFecTableSize);
+
+ // Protection factor for P frame
+ codeRateDelta = VCMCodeRateXORTable[indexTable];
+
+ if (packetLoss > lossThr && avgTotPackets > packetNumThr)
+ {
+ // Average with minimum protection level given by (average) total
+ // number of packets
+ codeRateDelta = static_cast<WebRtc_UWord8>((weight1 *
+ (float) codeRateDelta + weight2 * 255.0 / (float) avgTotPackets));
+
+ // Set a minimum based on first partition size.
+ if (codeRateDelta < firstPartitionProt)
+ {
+ codeRateDelta = firstPartitionProt;
+ }
+ }
+
+ // Check limit on amount of protection for P frame; 50% is max.
+ if (codeRateDelta >= plossMax)
+ {
+ codeRateDelta = plossMax - 1;
+ }
+
+ // For Key frame:
+ // Effectively at a higher rate, so we scale/boost the rate
+ // The boost factor may depend on several factors: ratio of packet
+ // number of I to P frames, how much protection placed on P frames, etc.
+ const WebRtc_UWord8 packetFrameDelta = (WebRtc_UWord8)
+ (0.5 + parameters->packetsPerFrame);
+ const WebRtc_UWord8 packetFrameKey = (WebRtc_UWord8)
+ (0.5 + parameters->packetsPerFrameKey);
+ const WebRtc_UWord8 boostKey = BoostCodeRateKey(packetFrameDelta,
+ packetFrameKey);
+
+ rateIndexTable = (WebRtc_UWord8) VCM_MAX(VCM_MIN(
+ 1 + (boostKey * bitRatePerFrame - ratePar1) /
+ ratePar1,ratePar2),0);
+ WebRtc_UWord16 indexTableKey = rateIndexTable * plossMax + packetLoss;
+
+ indexTableKey = VCM_MIN(indexTableKey, maxFecTableSize);
+
+ // Check on table index
+ assert(indexTableKey < maxFecTableSize);
+
+ // Protection factor for I frame
+ codeRateKey = VCMCodeRateXORTable[indexTableKey];
+
+ // Boosting for Key frame.
+ WebRtc_UWord32 boostKeyProt = _scaleProtKey * codeRateDelta;
+ if ( boostKeyProt >= plossMax)
+ {
+ boostKeyProt = plossMax - 1;
+ }
+
+ // Make sure I frame protection is at least larger than P frame protection,
+ // and at least as high as filtered packet loss.
+ codeRateKey = static_cast<WebRtc_UWord8> (VCM_MAX(packetLoss,
+ VCM_MAX(boostKeyProt, codeRateKey)));
+
+ // Check limit on amount of protection for I frame: 50% is max.
+ if (codeRateKey >= plossMax)
+ {
+ codeRateKey = plossMax - 1;
+ }
+
+ _protectionFactorK = codeRateKey;
+ _protectionFactorD = codeRateDelta;
+
+ // DONE WITH FEC PROTECTION SETTINGS
+ return true;
+}
+
+bool
+VCMFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters)
+{
+ // ER SETTINGS:
+ // Effective packet loss to encoder is based on RPL (residual packet loss)
+ // this is a soft setting based on degree of FEC protection
+ // RPL = received/input packet loss - average_FEC_recovery
+ // note: received/input packet loss may be filtered based on FilteredLoss
+
+ // The input packet loss:
+ WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr);
+
+ float scaleErRS = 0.5;
+ float scaleErXOR = 0.5;
+ float minErLevel = (float) 0.025;
+ // float scaleErRS = 1.0;
+ // float scaleErXOR = 1.0;
+ // float minErLevel = (float) 0.0;
+
+ float avgFecRecov = 0.;
+ // Effective packet loss for ER:
+ float scaleEr = scaleErXOR;
+ avgFecRecov = AvgRecoveryFEC(parameters);
+
+ // Residual Packet Loss:
+ _residualPacketLoss = (float) (effPacketLoss - avgFecRecov) / (float) 255.0;
+
+ //Effective Packet Loss for encoder:
+ _effectivePacketLoss = 0;
+ if (effPacketLoss > 0) {
+ _effectivePacketLoss = VCM_MAX((effPacketLoss -
+ (WebRtc_UWord8)(scaleEr * avgFecRecov)),
+ static_cast<WebRtc_UWord8>(minErLevel * 255));
+ }
+
+ // DONE WITH ER SETTING
+ return true;
+}
+
+bool
+VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
+{
+ // Compute the protection factor
+ ProtectionFactor(parameters);
+
+ // Compute the effective packet loss
+ EffectivePacketLoss(parameters);
+
+ // Compute the bit cost
+ // Ignore key frames for now.
+ float fecRate = static_cast<float> (_protectionFactorD) / 255.0f;
+ if (fecRate >= 0.0f)
+ {
+ // use this formula if the fecRate (protection factor) is defined
+ // relative to number of source packets
+ // this is the case for the previous tables:
+ // _efficiency = parameters->bitRate * ( 1.0 - 1.0 / (1.0 + fecRate));
+
+ // in the new tables, the fecRate is defined relative to total number of
+ // packets (total rate), so overhead cost is:
+ _efficiency = parameters->bitRate * fecRate;
+ }
+ else
+ {
+ _efficiency = 0.0f;
+ }
+ _score = _efficiency;
+
+ // Protection/fec rates obtained above is defined relative to total number
+ // of packets (total rate: source+fec) FEC in RTP module assumes protection
+ // factor is defined relative to source number of packets so we should
+ // convert the factor to reduce mismatch between mediaOpt suggested rate and
+ // the actual rate
+ _protectionFactorK = ConvertFECRate(_protectionFactorK);
+ _protectionFactorD = ConvertFECRate(_protectionFactorD);
+
+ return true;
+}
+
+bool
+VCMIntraReqMethod::UpdateParameters(const VCMProtectionParameters* parameters)
+{
+ float packetRate = parameters->packetsPerFrame * parameters->frameRate;
+ // Assume that all lost packets cohere to different frames
+ float lossRate = parameters->lossPr * packetRate;
+ if (parameters->keyFrameSize <= 1e-3)
+ {
+ _score = FLT_MAX;
+ return false;
+ }
+ _efficiency = lossRate * parameters->keyFrameSize;
+ _score = _efficiency;
+ if (parameters->lossPr >= 1.0f / parameters->keyFrameSize ||
+ parameters->rtt > _IREQ_MAX_RTT)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool
+VCMPeriodicIntraMethod::UpdateParameters(const
+ VCMProtectionParameters* /*parameters*/)
+{
+ // Periodic I-frames. The last thing we want to use.
+ _efficiency = 0.0f;
+ _score = FLT_MAX;
+ return true;
+}
+
+bool
+VCMMbIntraRefreshMethod::UpdateParameters(const
+ VCMProtectionParameters* parameters)
+{
+ // Assume optimal for now.
+ _efficiency = parameters->bitRate * parameters->lossPr /
+ (1.0f + parameters->lossPr);
+ _score = _efficiency;
+ if (parameters->bitRate < _MBREF_MIN_BITRATE)
+ {
+ return false;
+ }
+ return true;
+}
+
+WebRtc_UWord16
+VCMNackMethod::MaxRttNack() const
+{
+ return _NACK_MAX_RTT;
+}
+
+VCMLossProtectionLogic::~VCMLossProtectionLogic()
+{
+ ClearLossProtections();
+}
+
+void
+VCMLossProtectionLogic::ClearLossProtections()
+{
+ ListItem *item;
+ while ((item = _availableMethods.First()) != 0) {
+ VCMProtectionMethod *method = static_cast<VCMProtectionMethod*>
+ (item->GetItem());
+ if (method != NULL)
+ {
+ delete method;
+ }
+ _availableMethods.PopFront();
+ }
+ _selectedMethod = NULL;
+}
+
+bool
+VCMLossProtectionLogic::AddMethod(VCMProtectionMethod *newMethod)
+{
+ VCMProtectionMethod *method;
+ ListItem *item;
+ if (newMethod == NULL)
+ {
+ return false;
+ }
+ for (item = _availableMethods.First(); item != NULL;
+ item = _availableMethods.Next(item))
+ {
+ method = static_cast<VCMProtectionMethod *> (item->GetItem());
+ if (method != NULL && method->Type() == newMethod->Type())
+ {
+ return false;
+ }
+ }
+ _availableMethods.PushBack(newMethod);
+ return true;
+}
+bool
+VCMLossProtectionLogic::RemoveMethod(VCMProtectionMethodEnum methodType)
+{
+ VCMProtectionMethod *method;
+ ListItem *item;
+ bool foundAndRemoved = false;
+ for (item = _availableMethods.First(); item != NULL;
+ item = _availableMethods.Next(item))
+ {
+ method = static_cast<VCMProtectionMethod *> (item->GetItem());
+ if (method != NULL && method->Type() == methodType)
+ {
+ if (_selectedMethod != NULL &&
+ _selectedMethod->Type() == method->Type())
+ {
+ _selectedMethod = NULL;
+ }
+ _availableMethods.Erase(item);
+ item = NULL;
+ delete method;
+ foundAndRemoved = true;
+ }
+ }
+ return foundAndRemoved;
+}
+
+VCMProtectionMethod*
+VCMLossProtectionLogic::FindMethod(VCMProtectionMethodEnum methodType) const
+{
+ VCMProtectionMethod *method;
+ ListItem *item;
+ for (item = _availableMethods.First(); item != NULL;
+ item = _availableMethods.Next(item))
+ {
+ method = static_cast<VCMProtectionMethod *> (item->GetItem());
+ if (method != NULL && method->Type() == methodType)
+ {
+ return method;
+ }
+ }
+ return NULL;
+}
+
+float
+VCMLossProtectionLogic::HighestOverhead() const
+{
+ VCMProtectionMethod *method;
+ ListItem *item;
+ float highestOverhead = 0.0f;
+ for (item = _availableMethods.First(); item != NULL;
+ item = _availableMethods.Next(item))
+ {
+ method = static_cast<VCMProtectionMethod *> (item->GetItem());
+ if (method != NULL && method->RequiredBitRate() > highestOverhead)
+ {
+ highestOverhead = method->RequiredBitRate();
+ }
+ }
+ return highestOverhead;
+}
+
+void
+VCMLossProtectionLogic::UpdateRtt(WebRtc_UWord32 rtt)
+{
+ _rtt = rtt;
+}
+
+void
+VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss)
+{
+ _residualPacketLoss = residualPacketLoss;
+}
+
+void
+VCMLossProtectionLogic::UpdateFecType(VCMFecTypes fecType)
+{
+ _fecType = fecType;
+}
+
+void
+VCMLossProtectionLogic::UpdateLossPr(WebRtc_UWord8 lossPr255)
+{
+ const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ UpdateMaxLossHistory(lossPr255, now);
+ _lossPr255.Apply(static_cast<float> (now - _lastPrUpdateT),
+ static_cast<float> (lossPr255));
+ _lastPrUpdateT = now;
+ _lossPr = _lossPr255.Value() / 255.0f;
+}
+
+void
+VCMLossProtectionLogic::UpdateMaxLossHistory(WebRtc_UWord8 lossPr255,
+ WebRtc_Word64 now)
+{
+ if (_lossPrHistory[0].timeMs >= 0 &&
+ now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs)
+ {
+ if (lossPr255 > _shortMaxLossPr255)
+ {
+ _shortMaxLossPr255 = lossPr255;
+ }
+ }
+ else
+ {
+ // Only add a new value to the history once a second
+ if (_lossPrHistory[0].timeMs == -1)
+ {
+ // First, no shift
+ _shortMaxLossPr255 = lossPr255;
+ }
+ else
+ {
+ // Shift
+ for (WebRtc_Word32 i = (kLossPrHistorySize - 2); i >= 0; i--)
+ {
+ _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255;
+ _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs;
+ }
+ }
+ if (_shortMaxLossPr255 == 0)
+ {
+ _shortMaxLossPr255 = lossPr255;
+ }
+
+ _lossPrHistory[0].lossPr255 = _shortMaxLossPr255;
+ _lossPrHistory[0].timeMs = now;
+ _shortMaxLossPr255 = 0;
+ }
+}
+
+WebRtc_UWord8
+VCMLossProtectionLogic::MaxFilteredLossPr(WebRtc_Word64 nowMs) const
+{
+ WebRtc_UWord8 maxFound = _shortMaxLossPr255;
+ if (_lossPrHistory[0].timeMs == -1)
+ {
+ return maxFound;
+ }
+ for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++)
+ {
+ if (_lossPrHistory[i].timeMs == -1)
+ {
+ break;
+ }
+ if (nowMs - _lossPrHistory[i].timeMs >
+ kLossPrHistorySize * kLossPrShortFilterWinMs)
+ {
+ // This sample (and all samples after this) is too old
+ break;
+ }
+ if (_lossPrHistory[i].lossPr255 > maxFound)
+ {
+ // This sample is the largest one this far into the history
+ maxFound = _lossPrHistory[i].lossPr255;
+ }
+ }
+ return maxFound;
+}
+
+WebRtc_UWord8
+VCMLossProtectionLogic::FilteredLoss() const
+{
+ //take the average received loss
+ //return static_cast<WebRtc_UWord8>(_lossPr255.Value() + 0.5f);
+
+ //TODO: Update for hybrid
+ //take the windowed max of the received loss
+ if (_selectedMethod != NULL && _selectedMethod->Type() == kFEC)
+ {
+ return MaxFilteredLossPr(VCMTickTime::MillisecondTimestamp());
+ }
+ else
+ {
+ return static_cast<WebRtc_UWord8> (_lossPr255.Value() + 0.5);
+ }
+}
+
+void
+VCMLossProtectionLogic::UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc)
+{
+ _lossPr = (float) packetLossEnc / (float) 255.0;
+}
+
+void
+VCMLossProtectionLogic::UpdateBitRate(float bitRate)
+{
+ _bitRate = bitRate;
+}
+
+void
+VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets)
+{
+ const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ _packetsPerFrame.Apply(static_cast<float>(now - _lastPacketPerFrameUpdateT),
+ nPackets);
+ _lastPacketPerFrameUpdateT = now;
+}
+
+void
+VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets)
+{
+ const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ _packetsPerFrameKey.Apply(static_cast<float>(now -
+ _lastPacketPerFrameUpdateTKey), nPackets);
+ _lastPacketPerFrameUpdateTKey = now;
+}
+
+void
+VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize)
+{
+ _keyFrameSize = keyFrameSize;
+}
+
+void
+VCMLossProtectionLogic::UpdateFrameSize(WebRtc_UWord16 width,
+ WebRtc_UWord16 height)
+{
+ _codecWidth = width;
+ _codecHeight = height;
+}
+
+bool
+VCMLossProtectionLogic::UpdateMethod(VCMProtectionMethod *newMethod /*=NULL */)
+{
+ _currentParameters.rtt = _rtt;
+ _currentParameters.lossPr = _lossPr;
+ _currentParameters.bitRate = _bitRate;
+ _currentParameters.frameRate = _frameRate; // rename actual frame rate?
+ _currentParameters.keyFrameSize = _keyFrameSize;
+ _currentParameters.fecRateDelta = _fecRateDelta;
+ _currentParameters.fecRateKey = _fecRateKey;
+ _currentParameters.packetsPerFrame = _packetsPerFrame.Value();
+ _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value();
+ _currentParameters.residualPacketLoss = _residualPacketLoss;
+ _currentParameters.fecType = _fecType;
+ _currentParameters.codecWidth = _codecWidth;
+ _currentParameters.codecHeight = _codecHeight;
+
+ if (newMethod == NULL)
+ {
+ //_selectedMethod = _bestNotOkMethod = NULL;
+ VCMProtectionMethod *method;
+ ListItem *item;
+ for (item = _availableMethods.First(); item != NULL;
+ item = _availableMethods.Next(item))
+ {
+ method = static_cast<VCMProtectionMethod *> (item->GetItem());
+ if (method != NULL)
+ {
+ if (method->Type() == kFEC)
+ {
+ _selectedMethod = method;
+ }
+ if (method->Type() == kNACK)
+ {
+ _selectedMethod = method;
+ }
+ if (method->Type() == kNackFec)
+ {
+ _selectedMethod = method;
+ }
+ method->UpdateParameters(&_currentParameters);
+ }
+ }
+ if (_selectedMethod != NULL && _selectedMethod->Type() != kFEC)
+ {
+ _selectedMethod = method;
+ }
+ }
+ else
+ {
+ _selectedMethod = newMethod;
+ _selectedMethod->UpdateParameters(&_currentParameters);
+ }
+ return true;
+}
+
+VCMProtectionMethod*
+VCMLossProtectionLogic::SelectedMethod() const
+{
+ return _selectedMethod;
+}
+
+void VCMLossProtectionLogic::Reset()
+{
+ const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ _lastPrUpdateT = now;
+ _lastPacketPerFrameUpdateT = now;
+ _lastPacketPerFrameUpdateTKey = now;
+ _lossPr255.Reset(0.9999f);
+ _packetsPerFrame.Reset(0.9999f);
+ _fecRateDelta = _fecRateKey = 0;
+ for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++)
+ {
+ _lossPrHistory[i].lossPr255 = 0;
+ _lossPrHistory[i].timeMs = -1;
+ }
+ _shortMaxLossPr255 = 0;
+ ClearLossProtections();
+}
+
+}
diff --git a/src/modules/video_coding/main/source/media_opt_util.h b/src/modules/video_coding/main/source/media_opt_util.h
new file mode 100644
index 0000000..8298406
--- /dev/null
+++ b/src/modules/video_coding/main/source/media_opt_util.h
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_
+#define WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_
+
+#include "typedefs.h"
+#include "list_wrapper.h"
+#include "trace.h"
+#include "exp_filter.h"
+#include "internal_defines.h"
+#include "tick_time.h"
+
+#include <cmath>
+#include <cstdlib>
+
+
+namespace webrtc
+{
+class ListWrapper;
+
+enum { kLossPrHistorySize = 30 }; // 30 time periods
+enum { kLossPrShortFilterWinMs = 1000 }; // 1000 ms, total filter length is 30 000 ms
+
+enum VCMFecTypes
+{
+ kXORFec
+};
+
+// Thresholds for hybrid NACK/FEC
+// common to media optimization and the jitter buffer.
+enum HybridNackTH {
+ kHighRttNackMs = 100,
+ kLowRttNackMs = 20
+};
+
+struct VCMProtectionParameters
+{
+ VCMProtectionParameters() : rtt(0), lossPr(0), bitRate(0), packetsPerFrame(0),
+ frameRate(0), keyFrameSize(0), fecRateDelta(0), fecRateKey(0),
+ residualPacketLoss(0.0), fecType(kXORFec), codecWidth(0),
+ codecHeight(0) {}
+
+ WebRtc_UWord32 rtt;
+ float lossPr;
+ float bitRate;
+ float packetsPerFrame;
+ float packetsPerFrameKey;
+ float frameRate;
+ float keyFrameSize;
+ WebRtc_UWord8 fecRateDelta;
+ WebRtc_UWord8 fecRateKey;
+ float residualPacketLoss;
+ VCMFecTypes fecType;
+ WebRtc_UWord16 codecWidth;
+ WebRtc_UWord16 codecHeight;
+
+};
+
+
+/******************************/
+/* VCMProtectionMethod class */
+/****************************/
+
+enum VCMProtectionMethodEnum
+{
+ kNACK,
+ kFEC,
+ kNackFec,
+ kIntraRequest, // I-frame request
+ kPeriodicIntra, // I-frame refresh
+ kMBIntraRefresh, // Macro block refresh
+ kNone
+};
+
+class VCMLossProbabilitySample
+{
+public:
+ VCMLossProbabilitySample() : lossPr255(0), timeMs(-1) {};
+
+ WebRtc_UWord8 lossPr255;
+ WebRtc_Word64 timeMs;
+};
+
+
+
+class VCMProtectionMethod
+{
+public:
+ //friend VCMProtectionMethod;
+ VCMProtectionMethod(VCMProtectionMethodEnum type) : _protectionFactorK(0),
+ _protectionFactorD(0), _residualPacketLoss(0.0), _scaleProtKey(2.0),
+ _maxPayloadSize(1460), _efficiency(0), _score(0), _type(type) {}
+ virtual ~VCMProtectionMethod() {}
+
+ // Updates the efficiency of the method using the parameters provided
+ //
+ // Input:
+ // - parameters : Parameters used to calculate the efficiency
+ //
+ // Return value : True if this method is recommended in
+ // the given conditions.
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters) = 0;
+
+ // Returns the protection type
+ //
+ // Return value : The protection type
+ enum VCMProtectionMethodEnum Type() const { return _type; }
+
+ // Evaluates if this protection method is considered
+ // better than the provided method.
+ //
+ // Input:
+ // - pm : The protection method to compare with
+ bool BetterThan(VCMProtectionMethod *pm);
+
+ // Returns the bit rate required by this protection method
+ // during these conditions.
+ //
+ // Return value : Required bit rate
+ virtual float RequiredBitRate() { return _efficiency; }
+
+ // Returns the effective packet loss for ER, required by this protection method
+ //
+ // Return value : Required effective packet loss
+ virtual WebRtc_UWord8 RequiredPacketLossER() { return _effectivePacketLoss; }
+
+ // Extracts the FEC protection factor for Key frame, required by this protection method
+ //
+ // Return value : Required protectionFactor for Key frame
+ virtual WebRtc_UWord8 RequiredProtectionFactorK() { return _protectionFactorK; }
+
+ // Extracts the FEC protection factor for Delta frame, required by this protection method
+ //
+ // Return value : Required protectionFactor for delta frame
+ virtual WebRtc_UWord8 RequiredProtectionFactorD() { return _protectionFactorD; }
+
+ WebRtc_UWord8 _effectivePacketLoss;
+ WebRtc_UWord8 _protectionFactorK;
+ WebRtc_UWord8 _protectionFactorD;
+ float _residualPacketLoss;
+ float _scaleProtKey;
+ WebRtc_Word32 _maxPayloadSize;
+
+protected:
+ float _efficiency;
+ float _score;
+
+private:
+ const enum VCMProtectionMethodEnum _type;
+
+};
+
+class VCMNackMethod : public VCMProtectionMethod
+{
+public:
+ VCMNackMethod() : VCMProtectionMethod(kNACK), _NACK_MAX_RTT(200) {}
+ virtual ~VCMNackMethod() {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+ //get the effective packet loss for ER
+ bool EffectivePacketLoss(WebRtc_UWord8 effPacketLoss, WebRtc_UWord16 rttTime);
+ //get the threshold for NACK
+ WebRtc_UWord16 MaxRttNack() const;
+private:
+ const WebRtc_UWord16 _NACK_MAX_RTT;
+};
+
+class VCMFecMethod : public VCMProtectionMethod
+{
+public:
+ VCMFecMethod() : VCMProtectionMethod(kFEC) {}
+ virtual ~VCMFecMethod() {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+ //get the effective packet loss for ER
+ bool EffectivePacketLoss(const VCMProtectionParameters* parameters);
+ //get the FEC protection factors
+ bool ProtectionFactor(const VCMProtectionParameters* parameters);
+ //get the boost for key frame protection
+ WebRtc_UWord8 BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta,
+ WebRtc_UWord8 packetFrameKey) const;
+ //convert the rates: defined relative to total# packets or source# packets
+ WebRtc_UWord8 ConvertFECRate(WebRtc_UWord8 codeRate) const;
+ //get the average effective recovery from FEC: for random loss model
+ float AvgRecoveryFEC(const VCMProtectionParameters* parameters) const;
+};
+
+
+class VCMNackFecMethod : public VCMProtectionMethod
+{
+public:
+ VCMNackFecMethod() : VCMProtectionMethod(kNackFec) {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+ //get the effective packet loss for ER
+ bool EffectivePacketLoss(const VCMProtectionParameters* parameters);
+ //get the FEC protection factors
+ bool ProtectionFactor(const VCMProtectionParameters* parameters);
+
+};
+
+
+class VCMIntraReqMethod : public VCMProtectionMethod
+{
+public:
+ VCMIntraReqMethod() : VCMProtectionMethod(kIntraRequest), _IREQ_MAX_RTT(150) {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+private:
+ const WebRtc_UWord32 _IREQ_MAX_RTT;
+};
+
+class VCMPeriodicIntraMethod : public VCMProtectionMethod
+{
+public:
+ VCMPeriodicIntraMethod() : VCMProtectionMethod(kPeriodicIntra) {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+};
+
+class VCMMbIntraRefreshMethod : public VCMProtectionMethod
+{
+public:
+ VCMMbIntraRefreshMethod() :
+ VCMProtectionMethod(kMBIntraRefresh), _MBREF_MIN_BITRATE(150) {}
+ virtual bool UpdateParameters(const VCMProtectionParameters* parameters);
+ virtual float RequiredBitRate() { return 0.0; }
+private:
+ const WebRtc_UWord32 _MBREF_MIN_BITRATE;
+};
+
+class VCMLossProtectionLogic
+{
+public:
+ VCMLossProtectionLogic() : _availableMethods(), _selectedMethod(NULL),
+ _bestNotOkMethod(NULL), _rtt(0), _lossPr(0.0f), _bitRate(0.0f), _frameRate(0.0f),
+ _keyFrameSize(0.0f), _fecRateKey(0), _fecRateDelta(0), _lastPrUpdateT(0),
+ _lossPr255(0.9999f), _lossPrHistory(), _shortMaxLossPr255(0),
+ _packetsPerFrame(0.9999f), _packetsPerFrameKey(0.9999f), _residualPacketLoss(0),
+ _boostRateKey(2), _codecWidth(0), _codecHeight(0)
+ { Reset(); }
+
+ ~VCMLossProtectionLogic();
+
+ void ClearLossProtections();
+ bool AddMethod(VCMProtectionMethod *newMethod);
+ bool RemoveMethod(VCMProtectionMethodEnum methodType);
+ VCMProtectionMethod* FindMethod(VCMProtectionMethodEnum methodType) const;
+ float HighestOverhead() const;
+
+ // Update the round-trip time
+ //
+ // Input:
+ // - rtt : Round-trip time in seconds.
+ void UpdateRtt(WebRtc_UWord32 rtt);
+
+ // Update residual packet loss
+ //
+ // Input:
+ // - residualPacketLoss : residual packet loss: effective loss after FEC recovery
+ void UpdateResidualPacketLoss(float residualPacketLoss);
+
+ // Update fecType
+ //
+ // Input:
+ // - fecType : kXORFec for generic XOR FEC
+ void UpdateFecType(VCMFecTypes fecType);
+
+ // Update the loss probability.
+ //
+ // Input:
+ // - lossPr255 : The packet loss probability in the interval [0, 255],
+ // reported by RTCP.
+ void UpdateLossPr(WebRtc_UWord8 lossPr255);
+
+ // Update the filtered packet loss.
+ //
+ // Input:
+ // - packetLossEnc : The reported packet loss filtered (max window or average)
+ void UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc);
+
+ // Update the current target bit rate.
+ //
+ // Input:
+ // - bitRate : The current target bit rate in kbits/s
+ void UpdateBitRate(float bitRate);
+
+ // Update the number of packets per frame estimate, for delta frames
+ //
+ // Input:
+ // - nPackets : Number of packets used to send the latest frame.
+ void UpdatePacketsPerFrame(float nPackets);
+
+ // Update the number of packets per frame estimate, for key frames
+ //
+ // Input:
+ // - nPackets : Number of packets used to send the latest frame.
+ void UpdatePacketsPerFrameKey(float nPackets);
+
+ // Update the keyFrameSize estimate
+ //
+ // Input:
+ // - keyFrameSize : The size of the latest sent key frame.
+ void UpdateKeyFrameSize(float keyFrameSize);
+
+ // Update the frame rate
+ //
+ // Input:
+ // - frameRate : The current target frame rate.
+ void UpdateFrameRate(float frameRate) { _frameRate = frameRate; }
+
+ // Update the frame size
+ //
+ // Input:
+ // - width : The codec frame width.
+ // - height : The codec frame height.
+ void UpdateFrameSize(WebRtc_UWord16 width, WebRtc_UWord16 height);
+
+ // The amount of packet loss to cover for with FEC.
+ //
+ // Input:
+ // - fecRateKey : Packet loss to cover for with FEC when sending key frames.
+ // - fecRateDelta : Packet loss to cover for with FEC when sending delta frames.
+ void UpdateFECRates(WebRtc_UWord8 fecRateKey, WebRtc_UWord8 fecRateDelta)
+ { _fecRateKey = fecRateKey; _fecRateDelta = fecRateDelta; }
+
+ // Update the protection methods with the current VCMProtectionParameters and
+ // choose the best method available. The update involves computing the robustness settings
+ // for the protection method.
+ //
+ // Input:
+ // - newMethod : If not NULL, this is method will be selected by force.
+ //
+ // Return value : True if the selected method is recommended using these settings,
+ // false if it's the best method, but still not recommended to be used.
+ // E.g. if NACK is the best available, but the RTT is too large, false
+ // will be returned.
+ bool UpdateMethod(VCMProtectionMethod *newMethod = NULL);
+
+ // Returns the method currently selected.
+ //
+ // Return value : The protection method currently selected.
+ VCMProtectionMethod* SelectedMethod() const;
+
+ // Returns the filtered loss probability in the interval [0, 255].
+ //
+ // Return value : The filtered loss probability
+ WebRtc_UWord8 FilteredLoss() const;
+
+ // Get constraint on NACK
+ //
+ // return value : RTT threshold for using NACK
+ WebRtc_UWord8 GetNackThreshold() const;
+
+ void Reset();
+
+private:
+ // Sets the available loss protection methods.
+ void UpdateMaxLossHistory(WebRtc_UWord8 lossPr255, WebRtc_Word64 now);
+ WebRtc_UWord8 MaxFilteredLossPr(WebRtc_Word64 nowMs) const;
+ ListWrapper _availableMethods;
+ VCMProtectionMethod* _selectedMethod;
+ VCMProtectionMethod* _bestNotOkMethod;
+ VCMProtectionParameters _currentParameters;
+ WebRtc_UWord32 _rtt;
+ float _lossPr;
+ float _bitRate;
+ float _frameRate;
+ float _keyFrameSize;
+ WebRtc_UWord8 _fecRateKey;
+ WebRtc_UWord8 _fecRateDelta;
+ WebRtc_Word64 _lastPrUpdateT;
+ WebRtc_Word64 _lastPacketPerFrameUpdateT;
+ WebRtc_Word64 _lastPacketPerFrameUpdateTKey;
+ VCMExpFilter _lossPr255;
+ VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize];
+ WebRtc_UWord8 _shortMaxLossPr255;
+ VCMExpFilter _packetsPerFrame;
+ VCMExpFilter _packetsPerFrameKey;
+ float _residualPacketLoss;
+ WebRtc_UWord8 _boostRateKey;
+ VCMFecTypes _fecType;
+ WebRtc_UWord16 _codecWidth;
+ WebRtc_UWord16 _codecHeight;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_
diff --git a/src/modules/video_coding/main/source/media_optimization.cc b/src/modules/video_coding/main/source/media_optimization.cc
new file mode 100644
index 0000000..e215986
--- /dev/null
+++ b/src/modules/video_coding/main/source/media_optimization.cc
@@ -0,0 +1,707 @@
+/*
+ * 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 "media_optimization.h"
+#include "content_metrics_processing.h"
+#include "frame_dropper.h"
+#include "qm_select.h"
+
+namespace webrtc {
+
+VCMMediaOptimization::VCMMediaOptimization(WebRtc_Word32 id):
+_id(id),
+_maxBitRate(0),
+_sendCodecType(kVideoCodecUnknown),
+_codecWidth(0),
+_codecHeight(0),
+_userFrameRate(0),
+_lossProtOverhead(0),
+_packetLossEnc(0),
+_fractionLost(0),
+_sendStatisticsZeroEncode(0),
+_maxPayloadSize(1460),
+_lastBitRate(0),
+_targetBitRate(0),
+_incomingFrameRate(0),
+_enableQm(false),
+_videoProtectionCallback(NULL),
+_videoQMSettingsCallback(NULL),
+_encodedFrameSamples(),
+_avgSentBitRateBps(0.0f),
+_keyFrameCnt(0),
+_deltaFrameCnt(0),
+_lastQMUpdateTime(0),
+_lastChangeTime(0)
+{
+ memset(_sendStatistics, 0, sizeof(_sendStatistics));
+ memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes));
+
+ _frameDropper = new VCMFrameDropper(_id);
+ _lossProtLogic = new VCMLossProtectionLogic();
+ _content = new VCMContentMetricsProcessing();
+ _qms = new VCMQmSelect();
+}
+
+VCMMediaOptimization::~VCMMediaOptimization(void)
+{
+ _lossProtLogic->ClearLossProtections();
+ delete _lossProtLogic;
+ delete _frameDropper;
+ delete _content;
+ delete _qms;
+}
+
+WebRtc_Word32
+VCMMediaOptimization::Reset()
+{
+ memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes));
+ InputFrameRate(); // Resets _incomingFrameRate
+ _frameDropper->Reset();
+ _lossProtLogic->Reset();
+ _frameDropper->SetRates(0, 0);
+ _content->Reset();
+ _qms->Reset();
+ _lossProtLogic->UpdateFrameRate(_incomingFrameRate);
+ _lossProtLogic->Reset();
+ _sendStatisticsZeroEncode = 0;
+ _lastBitRate = 0;
+ _targetBitRate = 0;
+ _lossProtOverhead = 0;
+ _codecWidth = 0;
+ _codecHeight = 0;
+ _userFrameRate = 0;
+ _keyFrameCnt = 0;
+ _deltaFrameCnt = 0;
+ _lastQMUpdateTime = 0;
+ _lastChangeTime = 0;
+ for (WebRtc_Word32 i = 0; i < kBitrateMaxFrameSamples; i++)
+ {
+ _encodedFrameSamples[i]._sizeBytes = -1;
+ _encodedFrameSamples[i]._timeCompleteMs = -1;
+ }
+ _avgSentBitRateBps = 0.0f;
+ return VCM_OK;
+}
+
+WebRtc_UWord32
+VCMMediaOptimization::SetTargetRates(WebRtc_UWord32 bitRate,
+ WebRtc_UWord8 &fractionLost,
+ WebRtc_UWord32 roundTripTimeMs)
+{
+ VCMProtectionMethod *selectedMethod = _lossProtLogic->SelectedMethod();
+ _lossProtLogic->UpdateBitRate(static_cast<float>(bitRate));
+ _lossProtLogic->UpdateLossPr(fractionLost);
+ _lossProtLogic->UpdateRtt(roundTripTimeMs);
+ _lossProtLogic->UpdateResidualPacketLoss(static_cast<float>(fractionLost));
+
+ VCMFecTypes fecType = kXORFec; // generic FEC
+ _lossProtLogic->UpdateFecType(fecType);
+
+ // Get frame rate for encoder: this is the actual/sent frame rate
+ float actualFrameRate = SentFrameRate();
+
+ // sanity
+ if (actualFrameRate < 1.0)
+ {
+ actualFrameRate = 1.0;
+ }
+
+ // Update frame rate for the loss protection logic class: frame rate should
+ // be the actual/sent rate
+ _lossProtLogic->UpdateFrameRate(actualFrameRate);
+
+ _fractionLost = fractionLost;
+
+ // The effective packet loss may be the received loss or filtered, i.e.,
+ // average or max filter may be used.
+ // We should think about which filter is appropriate for low/high bit rates,
+ // low/high loss rates, etc.
+ WebRtc_UWord8 packetLossEnc = _lossProtLogic->FilteredLoss();
+
+ //For now use the filtered loss for computing the robustness settings
+ _lossProtLogic->UpdateFilteredLossPr(packetLossEnc);
+
+ // Rate cost of the protection methods
+ _lossProtOverhead = 0;
+
+ if (selectedMethod && (selectedMethod->Type() == kFEC ||
+ selectedMethod->Type() == kNackFec ))
+ {
+
+ // Update method will compute the robustness settings for the given
+ // protection method and the overhead cost
+ // the protection method is set by the user via SetVideoProtection.
+ // The robustness settings are: the effective packet loss for ER and the
+ // FEC protection settings
+ _lossProtLogic->UpdateMethod();
+
+ // Get the code rate for Key frames
+ const WebRtc_UWord8 codeRateKeyRTP = selectedMethod->RequiredProtectionFactorK();
+
+ // Get the code rate for Delta frames
+ const WebRtc_UWord8 codeRateDeltaRTP = selectedMethod->RequiredProtectionFactorD();
+
+ // Get the effective packet loss for ER
+ packetLossEnc = selectedMethod->RequiredPacketLossER();
+
+ // NACK is on for NACK and NackFec protection method: off for FEC method
+ bool nackStatus = (selectedMethod->Type() == kNackFec ||
+ selectedMethod->Type() == kNACK);
+
+ if(_videoProtectionCallback)
+ {
+ _videoProtectionCallback->ProtectionRequest(codeRateDeltaRTP,
+ codeRateKeyRTP,
+ nackStatus);
+ }
+ }
+
+ // Get the bit cost of protection method
+ _lossProtOverhead = static_cast<WebRtc_UWord32>(_lossProtLogic->HighestOverhead() + 0.5f);
+
+ // Update effective packet loss for encoder: note: fractionLost was passed as reference
+ fractionLost = packetLossEnc;
+
+ WebRtc_UWord32 nackBitRate=0;
+ if(selectedMethod && _lossProtLogic->FindMethod(kNACK) != NULL)
+ {
+ // TODO(mikhal): update frame dropper with bit rate including both nack and fec
+ // Make sure we don't over-use the channel momentarily. This is
+ // necessary for NACK since it can be very bursty.
+ nackBitRate = (_lastBitRate * fractionLost) / 255;
+ if (nackBitRate > _targetBitRate)
+ {
+ nackBitRate = _targetBitRate;
+ }
+ _frameDropper->SetRates(static_cast<float>(bitRate - nackBitRate), 0);
+ }
+ else
+ {
+ _frameDropper->SetRates(static_cast<float>(bitRate - _lossProtOverhead), 0);
+ }
+
+ // This may be used for UpdateEncoderBitRate: lastBitRate is total rate,
+ // before compensation
+ _lastBitRate = _targetBitRate;
+
+ //Source coding rate: total rate - protection overhead
+ _targetBitRate = bitRate - _lossProtOverhead;
+
+ if (_enableQm)
+ {
+ //Update QM with rates
+ _qms->UpdateRates((float)_targetBitRate, _avgSentBitRateBps,
+ _incomingFrameRate, _fractionLost);
+ //Check for QM selection
+ bool selectQM = checkStatusForQMchange();
+ if (selectQM)
+ {
+ SelectQuality();
+ }
+ // Reset the short-term averaged content data.
+ _content->ResetShortTermAvgData();
+ }
+
+ return _targetBitRate;
+}
+
+
+bool
+VCMMediaOptimization::DropFrame()
+{
+ // leak appropriate number of bytes
+ _frameDropper->Leak((WebRtc_UWord32)(InputFrameRate() + 0.5f));
+ return _frameDropper->DropFrame();
+}
+
+WebRtc_Word32
+VCMMediaOptimization::SentFrameCount(VCMFrameCount &frameCount) const
+{
+ frameCount.numDeltaFrames = _deltaFrameCnt;
+ frameCount.numKeyFrames = _keyFrameCnt;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMMediaOptimization::SetEncodingData(VideoCodecType sendCodecType, WebRtc_Word32 maxBitRate,
+ WebRtc_UWord32 frameRate, WebRtc_UWord32 bitRate,
+ WebRtc_UWord16 width, WebRtc_UWord16 height)
+{
+ // Everything codec specific should be reset here since this means the codec has changed.
+ // If native dimension values have changed, then either user initiated change, or QM
+ // initiated change. Will be able to determine only after the processing of the first frame
+ _lastChangeTime = VCMTickTime::MillisecondTimestamp();
+ _content->Reset();
+ _content->UpdateFrameRate(frameRate);
+
+ _maxBitRate = maxBitRate;
+ _sendCodecType = sendCodecType;
+ _targetBitRate = bitRate;
+ _lossProtLogic->UpdateBitRate(static_cast<float>(bitRate));
+ _lossProtLogic->UpdateFrameRate(static_cast<float>(frameRate));
+ _lossProtLogic->UpdateFrameSize(width, height);
+ _frameDropper->Reset();
+ _frameDropper->SetRates(static_cast<float>(bitRate), static_cast<float>(frameRate));
+ _userFrameRate = (float)frameRate;
+ _codecWidth = width;
+ _codecHeight = height;
+ WebRtc_Word32 ret = VCM_OK;
+ ret = _qms->Initialize((float)_targetBitRate, _userFrameRate, _codecWidth, _codecHeight);
+ return ret;
+}
+
+WebRtc_Word32
+VCMMediaOptimization::RegisterProtectionCallback(VCMProtectionCallback* protectionCallback)
+{
+ _videoProtectionCallback = protectionCallback;
+ return VCM_OK;
+
+}
+
+
+void
+VCMMediaOptimization::EnableFrameDropper(bool enable)
+{
+ _frameDropper->Enable(enable);
+}
+
+
+void
+VCMMediaOptimization::EnableNack(bool enable)
+{
+ // Add NACK to the list of loss protection methods
+ bool updated = false;
+ if (enable)
+ {
+ VCMProtectionMethod *nackMethod = new VCMNackMethod();
+ updated = _lossProtLogic->AddMethod(nackMethod);
+ if (!updated)
+ {
+ delete nackMethod;
+ }
+ }
+ else
+ {
+ updated = _lossProtLogic->RemoveMethod(kNACK);
+ }
+ if (updated)
+ {
+ _lossProtLogic->UpdateMethod();
+ }
+}
+
+bool
+VCMMediaOptimization::IsNackEnabled()
+{
+ return (_lossProtLogic->FindMethod(kNACK) != NULL);
+}
+
+void
+VCMMediaOptimization::EnableFEC(bool enable)
+{
+ // Add FEC to the list of loss protection methods
+ bool updated = false;
+ if (enable)
+ {
+ VCMProtectionMethod *fecMethod = new VCMFecMethod();
+ updated = _lossProtLogic->AddMethod(fecMethod);
+ if (!updated)
+ {
+ delete fecMethod;
+ }
+ }
+ else
+ {
+ updated = _lossProtLogic->RemoveMethod(kFEC);
+ }
+ if (updated)
+ {
+ _lossProtLogic->UpdateMethod();
+ }
+}
+void
+VCMMediaOptimization::EnableNackFEC(bool enable)
+{
+ // Add NackFec to the list of loss protection methods
+ bool updated = false;
+ if (enable)
+ {
+ VCMProtectionMethod *nackfecMethod = new VCMNackFecMethod();
+ updated = _lossProtLogic->AddMethod(nackfecMethod);
+ if (!updated)
+ {
+ delete nackfecMethod;
+ }
+ }
+ else
+ {
+ updated = _lossProtLogic->RemoveMethod(kNackFec);
+ }
+ if (updated)
+ {
+ _lossProtLogic->UpdateMethod();
+ }
+}
+
+bool
+VCMMediaOptimization::IsFecEnabled()
+{
+ return (_lossProtLogic->FindMethod(kFEC) != NULL);
+}
+
+bool
+VCMMediaOptimization::IsNackFecEnabled()
+{
+ return (_lossProtLogic->FindMethod(kNackFec) != NULL);
+}
+
+void
+VCMMediaOptimization::SetMtu(WebRtc_Word32 mtu)
+{
+ _maxPayloadSize = mtu;
+}
+
+float
+VCMMediaOptimization::SentFrameRate()
+{
+ if(_frameDropper)
+ {
+ return _frameDropper->ActualFrameRate((WebRtc_UWord32)(InputFrameRate() + 0.5f));
+ }
+
+ return VCM_CODEC_ERROR;
+}
+
+float
+VCMMediaOptimization::SentBitRate()
+{
+ UpdateBitRateEstimate(-1, VCMTickTime::MillisecondTimestamp());
+ return _avgSentBitRateBps / 1000.0f;
+}
+
+WebRtc_Word32
+VCMMediaOptimization::MaxBitRate()
+{
+ return _maxBitRate;
+}
+
+WebRtc_Word32
+VCMMediaOptimization::UpdateWithEncodedData(WebRtc_Word32 encodedLength,
+ FrameType encodedFrameType)
+{
+ // look into the ViE version - debug mode - needs also number of layers.
+ UpdateBitRateEstimate(encodedLength, VCMTickTime::MillisecondTimestamp());
+ if(encodedLength > 0)
+ {
+ const bool deltaFrame = (encodedFrameType != kVideoFrameKey &&
+ encodedFrameType != kVideoFrameGolden);
+
+ _frameDropper->Fill(encodedLength, deltaFrame);
+ if (_maxPayloadSize > 0 && encodedLength > 0)
+ {
+ const float minPacketsPerFrame = encodedLength /
+ static_cast<float>(_maxPayloadSize);
+ if (deltaFrame)
+ {
+ _lossProtLogic->UpdatePacketsPerFrame(minPacketsPerFrame);
+ }
+ else
+ {
+ _lossProtLogic->UpdatePacketsPerFrameKey(minPacketsPerFrame);
+ }
+
+ if (_enableQm)
+ {
+ // update quality select with encoded length
+ _qms->UpdateEncodedSize(encodedLength, encodedFrameType);
+ }
+ }
+ if (!deltaFrame && encodedLength > 0)
+ {
+ _lossProtLogic->UpdateKeyFrameSize(static_cast<float>(encodedLength));
+ }
+
+ // updating counters
+ if (deltaFrame){
+ _deltaFrameCnt++;
+ } else {
+ _keyFrameCnt++;
+ }
+
+ }
+
+ return VCM_OK;
+
+}
+
+void VCMMediaOptimization::UpdateBitRateEstimate(WebRtc_Word64 encodedLength,
+ WebRtc_Word64 nowMs)
+{
+ int i = kBitrateMaxFrameSamples - 1;
+ WebRtc_UWord32 frameSizeSum = 0;
+ WebRtc_Word64 timeOldest = -1;
+ // Find an empty slot for storing the new sample and at the same time
+ // accumulate the history.
+ for (; i >= 0; i--)
+ {
+ if (_encodedFrameSamples[i]._sizeBytes == -1)
+ {
+ // Found empty slot
+ break;
+ }
+ if (nowMs - _encodedFrameSamples[i]._timeCompleteMs < kBitrateAverageWinMs)
+ {
+ frameSizeSum += static_cast<WebRtc_UWord32>(_encodedFrameSamples[i]._sizeBytes);
+ if (timeOldest == -1)
+ {
+ timeOldest = _encodedFrameSamples[i]._timeCompleteMs;
+ }
+ }
+ }
+ if (encodedLength > 0)
+ {
+ if (i < 0)
+ {
+ // No empty slot, shift
+ for (i = kBitrateMaxFrameSamples - 2; i >= 0; i--)
+ {
+ _encodedFrameSamples[i + 1] = _encodedFrameSamples[i];
+ }
+ i++;
+ }
+ // Insert new sample
+ _encodedFrameSamples[i]._sizeBytes = encodedLength;
+ _encodedFrameSamples[i]._timeCompleteMs = nowMs;
+ }
+ if (timeOldest > -1)
+ {
+ // Update average bit rate
+ float denom = static_cast<float>(nowMs - timeOldest);
+ if (denom < 1.0)
+ {
+ denom = 1.0;
+ }
+ _avgSentBitRateBps = (frameSizeSum + encodedLength) * 8 * 1000 / denom;
+ }
+ else if (encodedLength > 0)
+ {
+ _avgSentBitRateBps = static_cast<float>(encodedLength * 8);
+ }
+ else
+ {
+ _avgSentBitRateBps = 0;
+ }
+}
+
+
+WebRtc_Word32
+VCMMediaOptimization::RegisterVideoQMCallback(VCMQMSettingsCallback *videoQMSettings)
+{
+ _videoQMSettingsCallback = videoQMSettings;
+ // Callback setting controls QM
+ if (_videoQMSettingsCallback != NULL)
+ {
+ _enableQm = true;
+ }
+ else
+ {
+ _enableQm = false;
+ }
+ return VCM_OK;
+}
+
+void
+VCMMediaOptimization::updateContentData(const VideoContentMetrics *contentMetrics)
+{
+ //Updating content metrics
+ if (contentMetrics == NULL)
+ {
+ //No QM if metrics are NULL
+ _enableQm = false;
+ _qms->Reset();
+ }
+ else
+ {
+ _content->UpdateContentData(contentMetrics);
+ }
+}
+
+WebRtc_Word32
+VCMMediaOptimization::SelectQuality()
+{
+ // Reset quantities for QM select
+ _qms->ResetQM();
+
+ // Update QM will long-term averaged content metrics.
+ _qms->UpdateContent(_content->LongTermAvgData());
+
+ // Select quality mode
+ VCMQualityMode* qm = NULL;
+ WebRtc_Word32 ret = _qms->SelectQuality(&qm);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ // Check for updates to spatial/temporal modes
+ QMUpdate(qm);
+
+ // Reset all the rate and related frame counters quantities
+ _qms->ResetRates();
+
+ // Reset counters
+ _lastQMUpdateTime = VCMTickTime::MillisecondTimestamp();
+
+ // Reset content metrics
+ _content->Reset();
+
+ return VCM_OK;
+}
+
+
+// Check timing constraints and look for significant change in:
+// (1) scene content
+// (2) target bit rate
+
+bool
+VCMMediaOptimization::checkStatusForQMchange()
+{
+
+ bool status = true;
+
+ // Check that we do not call QMSelect too often, and that we waited some time
+ // (to sample the metrics) from the event lastChangeTime
+ // lastChangeTime is the time where user changed the size/rate/frame rate
+ // (via SetEncodingData)
+ WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ if ((now - _lastQMUpdateTime) < kQmMinIntervalMs ||
+ (now - _lastChangeTime) < kQmMinIntervalMs)
+ {
+ status = false;
+ }
+
+ return status;
+
+}
+
+bool
+VCMMediaOptimization::QMUpdate(VCMQualityMode* qm)
+{
+ // Check for no change
+ if (qm->spatialHeightFact == 1 &&
+ qm->spatialWidthFact == 1 &&
+ qm->temporalFact == 1)
+ {
+ return false;
+ }
+
+ // Content metrics hold native values
+ VideoContentMetrics* cm = _content->LongTermAvgData();
+
+ // Temporal
+ WebRtc_UWord32 frameRate = static_cast<WebRtc_UWord32>(_incomingFrameRate + 0.5f);
+ // Check if go back up in temporal resolution
+ if (qm->temporalFact == 0)
+ {
+ frameRate = (WebRtc_UWord32) 2 * _incomingFrameRate;
+ }
+ // go down in temporal resolution
+ else
+ {
+ frameRate = (WebRtc_UWord32)(_incomingFrameRate / qm->temporalFact + 1);
+ }
+
+ // Spatial
+ WebRtc_UWord32 height = _codecHeight;
+ WebRtc_UWord32 width = _codecWidth;
+ // Check if go back up in spatial resolution
+ if (qm->spatialHeightFact == 0 && qm->spatialWidthFact == 0)
+ {
+ height = cm->nativeHeight;
+ width = cm->nativeWidth;
+ }
+ else
+ {
+ height = _codecHeight / qm->spatialHeightFact;
+ width = _codecWidth / qm->spatialWidthFact;
+ }
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, _id,
+ "Quality Mode Update: W = %d, H = %d, FR = %f",
+ width, height, frameRate);
+
+ // Update VPM with new target frame rate and size
+ _videoQMSettingsCallback->SetVideoQMSettings(frameRate, width, height);
+
+ return true;
+}
+
+
+
+void
+VCMMediaOptimization::UpdateIncomingFrameRate()
+{
+ WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
+ if(_incomingFrameTimes[0] == 0)
+ {
+ // first no shift
+ } else
+ {
+ // shift
+ for(WebRtc_Word32 i = (kFrameCountHistorySize - 2); i >= 0 ; i--)
+ {
+ _incomingFrameTimes[i+1] = _incomingFrameTimes[i];
+ }
+ }
+ _incomingFrameTimes[0] = now;
+ ProcessIncomingFrameRate(now);
+}
+
+// allowing VCM to keep track of incoming frame rate
+void
+VCMMediaOptimization::ProcessIncomingFrameRate(WebRtc_Word64 now)
+{
+ WebRtc_Word32 num = 0;
+ WebRtc_Word32 nrOfFrames = 0;
+ for (num = 1; num < (kFrameCountHistorySize - 1); num++)
+ {
+ if (_incomingFrameTimes[num] <= 0 ||
+ // don't use data older than 2 s
+ now - _incomingFrameTimes[num] > kFrameHistoryWinMs)
+ {
+ break;
+ } else
+ {
+ nrOfFrames++;
+ }
+ }
+ if (num > 1)
+ {
+ const WebRtc_Word64 diff = now - _incomingFrameTimes[num-1];
+ _incomingFrameRate = 1.0;
+ if(diff >0)
+ {
+ _incomingFrameRate = nrOfFrames * 1000.0f / static_cast<float>(diff);
+ }
+ }
+ else
+ {
+ _incomingFrameRate = static_cast<float>(nrOfFrames);
+ }
+}
+
+WebRtc_UWord32
+VCMMediaOptimization::InputFrameRate()
+{
+ ProcessIncomingFrameRate(VCMTickTime::MillisecondTimestamp());
+ return WebRtc_UWord32 (_incomingFrameRate + 0.5f);
+}
+
+}
diff --git a/src/modules/video_coding/main/source/media_optimization.h b/src/modules/video_coding/main/source/media_optimization.h
new file mode 100644
index 0000000..8a9993e
--- /dev/null
+++ b/src/modules/video_coding/main/source/media_optimization.h
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_
+#define WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_
+
+#include "list_wrapper.h"
+#include "module_common_types.h"
+#include "video_coding.h"
+#include "trace.h"
+#include "media_opt_util.h"
+#include "qm_select.h"
+
+namespace webrtc
+{
+
+enum { kBitrateMaxFrameSamples = 60 };
+enum { kBitrateAverageWinMs = 1000 };
+
+class VCMContentMetricsProcessing;
+class VCMFrameDropper;
+
+struct VCMEncodedFrameSample
+{
+ VCMEncodedFrameSample() : _sizeBytes(-1), _timeCompleteMs(-1) {}
+
+ WebRtc_Word64 _sizeBytes;
+ WebRtc_Word64 _timeCompleteMs;
+};
+
+class VCMMediaOptimization
+{
+public:
+ VCMMediaOptimization(WebRtc_Word32 id);
+ ~VCMMediaOptimization(void);
+ /*
+ * Reset the Media Optimization module
+ */
+ WebRtc_Word32 Reset();
+ /**
+ * Set target Rates for the encoder given the channel parameters
+ * Inputs: bitRate - target bitRate, in the conference case this is the rate
+ * between the sending client and the server
+ * fractionLost - packet loss in % in the network
+ * roundTripTimeMs - round trip time in miliseconds
+ * minBitRate - the bit rate of the end-point with lowest rate
+ * maxBitRate - the bit rate of the end-point with highest rate
+ */
+ WebRtc_UWord32 SetTargetRates(WebRtc_UWord32 bitRate,
+ WebRtc_UWord8 &fractionLost,
+ WebRtc_UWord32 roundTripTimeMs);
+
+ /**
+ * Inform media optimization of initial encoding state
+ */
+ WebRtc_Word32 SetEncodingData(VideoCodecType sendCodecType,
+ WebRtc_Word32 maxBitRate,
+ WebRtc_UWord32 frameRate,
+ WebRtc_UWord32 bitRate,
+ WebRtc_UWord16 width,
+ WebRtc_UWord16 height);
+ /**
+ * Enable NACK and update error resilience parameters
+ */
+ void EnableNack(bool enable);
+ /**
+ * Returns weather or not NACK is enabled
+ */
+ bool IsNackEnabled();
+ /**
+ * Enable FEC and update error resilience parameters
+ */
+ void EnableFEC(bool enable);
+ /**
+ * Returns weather or not FEC is enabled
+ */
+ bool IsFecEnabled();
+ /**
+ * Returns weather or not NackFec is enabled
+ */
+ bool IsNackFecEnabled();
+ /**
+ * Updates the max pay load size
+ */
+ /**
+ * Enable NackFec and update error resilience parameters
+ */
+ void EnableNackFEC(bool enable);
+
+ void SetMtu(WebRtc_Word32 mtu);
+
+ /*
+ * Get actual input frame rate
+ */
+ WebRtc_UWord32 InputFrameRate();
+
+ /*
+ * Get actual sent frame rate
+ */
+ float SentFrameRate();
+ /*
+ * Get actual sent bit rate
+ */
+ float SentBitRate();
+ /*
+ * Get maximum allowed bit rate
+ */
+ WebRtc_Word32 MaxBitRate();
+ /*
+ * Inform Media Optimization of encoding output: Length and frame type
+ */
+ WebRtc_Word32 UpdateWithEncodedData(WebRtc_Word32 encodedLength,
+ FrameType encodedFrameType);
+ /*
+ * Register a protection callback to be used to inform the user about the
+ * protection methods used
+ */
+ WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protectionCallback);
+ /*
+ * Register a quality settings callback to be used to inform VPM/user about the optimal
+ * quality settings (frame rate/dimension) required
+ */
+ WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings);
+ void EnableFrameDropper(bool enable);
+
+ bool DropFrame();
+
+ /*
+ * Get number of key/delta frames encoded
+ */
+ WebRtc_Word32 SentFrameCount(VCMFrameCount &frameCount) const;
+
+ /*
+ * update incoming frame rate value
+ */
+ void UpdateIncomingFrameRate();
+
+ /**
+ * Update content metric Data
+ */
+ void updateContentData(const VideoContentMetrics* contentMetrics);
+
+ /**
+ * Compute new Quality Mode
+ */
+ WebRtc_Word32 SelectQuality();
+
+private:
+
+ void UpdateBitRateEstimate(WebRtc_Word64 encodedLength, WebRtc_Word64 nowMs);
+ /*
+ * verify if QM settings differ from default, i.e. if an update is required
+ * Compute actual values, as will be sent to the encoder
+ */
+ bool QMUpdate(VCMQualityMode* qm);
+ /**
+ * check if we should make a QM change
+ * will return 1 if yes, 0 otherwise
+ */
+ bool checkStatusForQMchange();
+
+ void ProcessIncomingFrameRate(WebRtc_Word64 now);
+
+ enum { kFrameCountHistorySize = 90};
+ enum { kFrameHistoryWinMs = 2000};
+
+ WebRtc_Word32 _id;
+
+ WebRtc_Word32 _maxBitRate;
+ VideoCodecType _sendCodecType;
+ WebRtc_UWord16 _codecWidth;
+ WebRtc_UWord16 _codecHeight;
+ float _userFrameRate;
+
+ VCMFrameDropper* _frameDropper;
+ VCMLossProtectionLogic* _lossProtLogic;
+ WebRtc_UWord32 _lossProtOverhead;
+ WebRtc_UWord8 _packetLossEnc;
+ WebRtc_UWord8 _fractionLost;
+
+
+ WebRtc_UWord32 _sendStatistics[4];
+ WebRtc_UWord32 _sendStatisticsZeroEncode;
+ WebRtc_Word32 _maxPayloadSize;
+ WebRtc_UWord32 _lastBitRate;
+ WebRtc_UWord32 _targetBitRate;
+
+ float _incomingFrameRate;
+ WebRtc_Word64 _incomingFrameTimes[kFrameCountHistorySize];
+
+ bool _enableQm;
+
+ VCMProtectionCallback* _videoProtectionCallback;
+ VCMQMSettingsCallback* _videoQMSettingsCallback;
+
+ VCMEncodedFrameSample _encodedFrameSamples[kBitrateMaxFrameSamples];
+ float _avgSentBitRateBps;
+
+ WebRtc_UWord32 _keyFrameCnt;
+ WebRtc_UWord32 _deltaFrameCnt;
+
+ VCMContentMetricsProcessing* _content;
+ VCMQmSelect* _qms;
+
+ WebRtc_Word64 _lastQMUpdateTime;
+ WebRtc_Word64 _lastChangeTime; // content or user triggered
+
+
+}; // end of VCMMediaOptimization class definition
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_
diff --git a/src/modules/video_coding/main/source/nack_fec_tables.h b/src/modules/video_coding/main/source/nack_fec_tables.h
new file mode 100644
index 0000000..40cf0b5
--- /dev/null
+++ b/src/modules/video_coding/main/source/nack_fec_tables.h
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_
+
+namespace webrtc
+{
+
+// Table for softening FEC rate for NACK/FEC protection method
+const WebRtc_UWord16 VCMNackFecTable[200] = {
+
+27,
+28,
+30,
+31,
+33,
+35,
+36,
+38,
+40,
+42,
+45,
+47,
+49,
+52,
+54,
+57,
+60,
+63,
+66,
+70,
+73,
+77,
+81,
+85,
+89,
+94,
+98,
+103,
+108,
+114,
+120,
+126,
+132,
+138,
+145,
+152,
+160,
+168,
+176,
+185,
+194,
+203,
+213,
+223,
+234,
+246,
+257,
+270,
+283,
+296,
+310,
+325,
+340,
+356,
+373,
+390,
+408,
+427,
+446,
+467,
+488,
+510,
+532,
+556,
+581,
+606,
+632,
+659,
+688,
+717,
+747,
+778,
+810,
+843,
+877,
+912,
+948,
+985,
+1022,
+1061,
+1101,
+1142,
+1183,
+1226,
+1269,
+1314,
+1359,
+1404,
+1451,
+1498,
+1546,
+1594,
+1643,
+1693,
+1743,
+1793,
+1843,
+1894,
+1945,
+1996,
+2048,
+2099,
+2150,
+2201,
+2252,
+2302,
+2352,
+2402,
+2452,
+2501,
+2549,
+2597,
+2644,
+2691,
+2736,
+2781,
+2826,
+2869,
+2912,
+2953,
+2994,
+3034,
+3073,
+3110,
+3147,
+3183,
+3218,
+3252,
+3285,
+3317,
+3348,
+3378,
+3407,
+3436,
+3463,
+3489,
+3514,
+3539,
+3563,
+3585,
+3607,
+3628,
+3649,
+3668,
+3687,
+3705,
+3722,
+3739,
+3755,
+3770,
+3785,
+3799,
+3812,
+3825,
+3838,
+3849,
+3861,
+3872,
+3882,
+3892,
+3901,
+3910,
+3919,
+3927,
+3935,
+3943,
+3950,
+3957,
+3963,
+3969,
+3975,
+3981,
+3987,
+3992,
+3997,
+4001,
+4006,
+4010,
+4014,
+4018,
+4022,
+4025,
+4029,
+4032,
+4035,
+4038,
+4041,
+4043,
+4046,
+4048,
+4050,
+4053,
+4055,
+4057,
+4059,
+4060,
+4062,
+4064,
+4065,
+4067,
+
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_
diff --git a/src/modules/video_coding/main/source/packet.cc b/src/modules/video_coding/main/source/packet.cc
new file mode 100644
index 0000000..f5077e4
--- /dev/null
+++ b/src/modules/video_coding/main/source/packet.cc
@@ -0,0 +1,77 @@
+/*
+ * 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 "packet.h"
+#include "module_common_types.h"
+
+#include <assert.h>
+
+namespace webrtc {
+
+VCMPacket::VCMPacket(const WebRtc_UWord8* ptr,
+ const WebRtc_UWord32 size,
+ const WebRtcRTPHeader& rtpHeader) :
+ payloadType(rtpHeader.header.payloadType),
+ timestamp(rtpHeader.header.timestamp),
+ seqNum(rtpHeader.header.sequenceNumber),
+ dataPtr(ptr),
+ sizeBytes(size),
+ markerBit(rtpHeader.header.markerBit),
+
+ frameType(rtpHeader.frameType),
+ codec(kVideoCodecUnknown),
+ isFirstPacket(rtpHeader.type.Video.isFirstPacket),
+ completeNALU(kNaluComplete),
+ insertStartCode(false),
+ bits(false)
+{
+ CopyCodecSpecifics(rtpHeader.type.Video);
+}
+
+VCMPacket::VCMPacket(const WebRtc_UWord8* ptr, WebRtc_UWord32 size, WebRtc_UWord16 seq, WebRtc_UWord32 ts, bool mBit) :
+ payloadType(0),
+ timestamp(ts),
+ seqNum(seq),
+ dataPtr(ptr),
+ sizeBytes(size),
+ markerBit(mBit),
+
+ frameType(kVideoFrameDelta),
+ codec(kVideoCodecUnknown),
+ isFirstPacket(false),
+ completeNALU(kNaluComplete),
+ insertStartCode(false),
+ bits(false)
+{}
+
+void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader)
+{
+ RTPVideoTypeHeader codecHeader = videoHeader.codecHeader;
+ switch(videoHeader.codec)
+ {
+ case kRTPVideoVP8:
+ {
+ codec = kVideoCodecVP8;
+ break;
+ }
+ case kRTPVideoI420:
+ {
+ codec = kVideoCodecI420;
+ break;
+ }
+ default:
+ {
+ codec = kVideoCodecUnknown;
+ break;
+ }
+ }
+}
+
+}
diff --git a/src/modules/video_coding/main/source/packet.h b/src/modules/video_coding/main/source/packet.h
new file mode 100644
index 0000000..f1fdfd1
--- /dev/null
+++ b/src/modules/video_coding/main/source/packet.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_PACKET_H_
+#define WEBRTC_MODULES_VIDEO_CODING_PACKET_H_
+
+#include "typedefs.h"
+#include "module_common_types.h"
+#include "jitter_buffer_common.h"
+
+namespace webrtc
+{
+
+class VCMPacket
+{
+public:
+ VCMPacket(const WebRtc_UWord8* ptr,
+ const WebRtc_UWord32 size,
+ const WebRtcRTPHeader& rtpHeader);
+ VCMPacket(const WebRtc_UWord8* ptr,
+ WebRtc_UWord32 size,
+ WebRtc_UWord16 seqNum,
+ WebRtc_UWord32 timestamp,
+ bool markerBit);
+
+ WebRtc_UWord8 payloadType;
+ WebRtc_UWord32 timestamp;
+ WebRtc_UWord16 seqNum;
+ const WebRtc_UWord8* dataPtr;
+ WebRtc_UWord32 sizeBytes;
+ bool markerBit;
+
+ FrameType frameType;
+ webrtc::VideoCodecType codec;
+
+ bool isFirstPacket; // Is this first packet in a frame.
+ VCMNaluCompleteness completeNALU; // Default is kNaluIncomplete.
+ bool insertStartCode; // True if a start code should be inserted before this
+ // packet.
+ bool bits; // The first bits of this packets are zero and the
+ // first
+ // byte should be ORed with the last packet of the
+ // previous frame.
+
+protected:
+ void CopyCodecSpecifics(const RTPVideoHeader& videoHeader);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_PACKET_H_
diff --git a/src/modules/video_coding/main/source/qm_select.cc b/src/modules/video_coding/main/source/qm_select.cc
new file mode 100644
index 0000000..3220db0
--- /dev/null
+++ b/src/modules/video_coding/main/source/qm_select.cc
@@ -0,0 +1,734 @@
+/*
+ * 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 "qm_select.h"
+#include "internal_defines.h"
+#include "qm_select_data.h"
+
+#include "module_common_types.h"
+#include "video_coding_defines.h"
+#include "trace.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+VCMQmSelect::VCMQmSelect()
+{
+ _qm = new VCMQualityMode();
+ _contentMetrics = new VideoContentMetrics();
+ Reset();
+}
+
+VCMQmSelect::~VCMQmSelect()
+{
+ delete _qm;
+ delete _contentMetrics;
+}
+
+void
+VCMQmSelect::ResetQM()
+{
+ _motion.Reset();
+ _spatial.Reset();
+ _coherence.Reset();
+ _stationaryMotion = 0;
+ _aspectRatio = 1;
+ _maxRateQM = 0;
+ _imageType = 1;
+ _userResolutionPref = 50; // Neutral
+ _qm->Reset();
+ return;
+}
+
+void
+VCMQmSelect::ResetRates()
+{
+ _sumEncodedBytes = 0;
+ _sumTargetRate = 0.0f;
+ _sumIncomingFrameRate = 0.0f;
+ _sumFrameRateMM = 0.0f;
+ _sumSeqRateMM = 0.0f;
+ _sumPacketLoss = 0.0f;
+ _frameCnt = 0;
+ _frameCntDelta = 0;
+ _lowBufferCnt = 0;
+ _updateRateCnt = 0;
+ return;
+}
+
+void
+VCMQmSelect::Reset()
+{
+ _stateDecFactorSpatial = 1;
+ _stateDecFactorTemp = 1;
+ _bufferLevel = 0.0f;
+ _targetBitRate = 0.0f;
+ _incomingFrameRate = 0.0f;
+ _userFrameRate = 0.0f;
+ _perFrameBandwidth =0.0f;
+ _prevTotalRate = 0.0f;
+ _prevRttTime = 0;
+ _prevPacketLoss = 0;
+ ResetQM();
+ ResetRates();
+ return;
+}
+
+//Initialize after reset of encoder
+WebRtc_Word32
+VCMQmSelect::Initialize(float bitRate, float userFrameRate,
+ WebRtc_UWord32 width, WebRtc_UWord32 height)
+{
+ if (userFrameRate == 0.0f || width == 0 || height == 0)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ _targetBitRate = bitRate;
+ _userFrameRate = userFrameRate;
+
+ // Encoder width and height
+ _width = width;
+ _height = height;
+
+ // Initial buffer level
+ _bufferLevel = INIT_BUFFER_LEVEL * _targetBitRate;
+
+ // Per-frame bandwidth
+ if ( _incomingFrameRate == 0 )
+ {
+ _perFrameBandwidth = _targetBitRate / _userFrameRate;
+ _incomingFrameRate = _userFrameRate;
+ }
+ else
+ {
+ // Take average: this is due to delay in update of new encoder frame rate:
+ // userFrameRate is the new one,
+ // incomingFrameRate is the old one (based on previous ~ 1sec/RTCP report)
+ _perFrameBandwidth = 0.5 *( _targetBitRate / _userFrameRate +
+ _targetBitRate / _incomingFrameRate );
+ }
+ _init = true;
+
+
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMQmSelect::SetPreferences(WebRtc_Word8 resolPref)
+{
+ // Preference setting for temporal over spatial resolution
+ // 100 means temporal, 0 means spatial, 50 is neutral
+ _userResolutionPref = resolPref;
+
+ return VCM_OK;
+}
+
+//Update after every encoded frame
+void
+VCMQmSelect::UpdateEncodedSize(WebRtc_Word64 encodedSize,
+ FrameType encodedFrameType)
+{
+ // Update encoded size;
+ _sumEncodedBytes += encodedSize;
+ _frameCnt++;
+
+ // Convert to Kbps
+ float encodedSizeKbits = (float)((encodedSize * 8.0) / 1000.0);
+
+ // Update the buffer level:
+ // per_frame_BW is updated when encoder is updated, every RTCP reports
+ _bufferLevel += _perFrameBandwidth - encodedSizeKbits;
+
+ // Mismatch here is based on difference of actual encoded frame size and
+ // per-frame bandwidth, for delta frames
+ // This is a much stronger condition on rate mismatch than sumSeqRateMM
+ // Note: not used in this version
+ /*
+ const bool deltaFrame = (encodedFrameType != kVideoFrameKey &&
+ encodedFrameType != kVideoFrameGolden);
+
+ // Sum the frame mismatch:
+ if (deltaFrame)
+ {
+ _frameCntDelta++;
+ if (encodedSizeKbits > 0)
+ _sumFrameRateMM +=
+ (float) (fabs(encodedSizeKbits - _perFrameBandwidth) /
+ encodedSizeKbits);
+ }
+ */
+
+ // Counter for occurrences of low buffer level
+ if (_bufferLevel <= PERC_BUFFER_THR * INIT_BUFFER_LEVEL * _targetBitRate)
+ {
+ _lowBufferCnt++;
+ }
+
+}
+
+//Update various quantities after SetTargetRates in MediaOpt
+void
+VCMQmSelect::UpdateRates(float targetBitRate, float avgSentBitRate,
+ float incomingFrameRate, WebRtc_UWord8 packetLoss)
+{
+
+ // Sum the target bitrate and incoming frame rate:
+ // these values are the encoder rates (from previous update ~1sec),
+ // i.e, before the update for next ~1sec
+ _sumTargetRate += _targetBitRate;
+ _sumIncomingFrameRate += _incomingFrameRate;
+ _updateRateCnt++;
+
+ // Sum the received (from RTCP reports) packet loss rates
+ _sumPacketLoss += (float) packetLoss / 255.0f;
+
+ // Convert average sent bitrate to kbps
+ float avgSentBitRatekbps = avgSentBitRate / 1000.0f;
+
+ // Sum the sequence rate mismatch:
+ // Mismatch here is based on difference between target rate the encoder
+ // used (in previous ~1sec) and the average actual
+ // encoding rate measured at current time
+ if (fabs(_targetBitRate - avgSentBitRatekbps) < THRESH_SUM_MM &&
+ _targetBitRate > 0.0 )
+ {
+ _sumSeqRateMM += (float)
+ (fabs(_targetBitRate - avgSentBitRatekbps) / _targetBitRate );
+ }
+
+ // Update QM with the current new target and frame rate:
+ // these values are ones the encoder will use for the current/next ~1sec
+ _targetBitRate = targetBitRate;
+ _incomingFrameRate = incomingFrameRate;
+
+ // Update QM with an (average) encoder per_frame_bandwidth:
+ // this is the per_frame_bw for the current/next ~1sec
+ _perFrameBandwidth = 0.0f;
+ if (_incomingFrameRate > 0.0f)
+ {
+ _perFrameBandwidth = _targetBitRate / _incomingFrameRate;
+ }
+
+}
+
+// Adjust the FEC rate based on the content and the network state
+// (packet loss rate, total rate/bandwidth, round trip time).
+// Note that packetLoss here is the filtered loss value.
+WebRtc_UWord8
+VCMQmSelect::AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
+ float frameRate,WebRtc_UWord16 rttTime,
+ WebRtc_UWord8 packetLoss)
+{
+ // Default: no adjustment
+ WebRtc_UWord8 codeRateDeltaAdjust = codeRateDelta;
+ float adjustFec = 1.0f;
+
+ // TODO (marpan):
+ // Set FEC adjustment factor
+
+ codeRateDeltaAdjust = static_cast<WebRtc_UWord8>(codeRateDelta * adjustFec);
+
+ // Keep track of previous values of network state:
+ // adjustment may be also based on pattern of changes in network state
+ _prevTotalRate = totalRate;
+ _prevRttTime = rttTime;
+ _prevPacketLoss = packetLoss;
+
+ return codeRateDeltaAdjust;
+}
+
+void
+VCMQmSelect::UpdateContent(const VideoContentMetrics* contentMetrics)
+{
+ _contentMetrics = contentMetrics;
+}
+
+// Select the resolution factors: frame size and frame rate change: (QM modes)
+// Selection is for going back up in resolution, or going down in.
+WebRtc_Word32
+VCMQmSelect::SelectQuality(VCMQualityMode** qm)
+{
+ if (!_init)
+ {
+ return VCM_UNINITIALIZED;
+ }
+ if (_contentMetrics == NULL)
+ {
+ Reset(); //default values
+ *qm = _qm;
+ return VCM_OK;
+ }
+
+ // Default settings
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ _qm->temporalFact = 1;
+
+
+ // Update native values
+ _nativeWidth = _contentMetrics->nativeWidth;
+ _nativeHeight = _contentMetrics->nativeHeight;
+ _nativeFrameRate = _contentMetrics->nativeFrameRate;
+
+ // Aspect ratio: used for selection of 1x2,2x1,2x2
+ _aspectRatio = (float)_width / (float)_height;
+
+ float avgTargetRate = 0.0f;
+ float avgIncomingFrameRate = 0.0f;
+ float ratioBufferLow = 0.0f;
+ float rateMisMatch = 0.0f;
+ float avgPacketLoss = 0.0f;
+ if (_frameCnt > 0)
+ {
+ ratioBufferLow = (float)_lowBufferCnt / (float)_frameCnt;
+ }
+ if (_updateRateCnt > 0)
+ {
+ // Use seq-rate mismatch for now
+ rateMisMatch = (float)_sumSeqRateMM / (float)_updateRateCnt;
+ //rateMisMatch = (float)_sumFrameRateMM / (float)_frameCntDelta;
+
+ // Average target and incoming frame rates
+ avgTargetRate = (float)_sumTargetRate / (float)_updateRateCnt;
+ avgIncomingFrameRate = (float)_sumIncomingFrameRate /
+ (float)_updateRateCnt;
+
+ // Average received packet loss rate
+ avgPacketLoss = (float)_sumPacketLoss / (float)_updateRateCnt;
+ }
+
+ // For QM selection below, may want to weight the average encoder rates
+ // with the current (for next ~1sec) rate values.
+ // Uniform average for now:
+ float w1 = 0.5f;
+ float w2 = 0.5f;
+ avgTargetRate = w1 * avgTargetRate + w2 * _targetBitRate;
+ avgIncomingFrameRate = w1 * avgIncomingFrameRate + w2 * _incomingFrameRate;
+
+ // Set the maximum transitional rate and image type:
+ // for up-sampled spatial dimensions.
+ // This is needed to get the transRate for going back up in
+ // spatial resolution (only 2x2 allowed in this version).
+ SetMaxRateForQM(2 * _width, 2 * _height);
+ WebRtc_UWord8 imageType2 = _imageType;
+ WebRtc_UWord32 maxRateQM2 = _maxRateQM;
+
+ // Set the maximum transitional rate and image type:
+ // for the encoder spatial dimensions.
+ SetMaxRateForQM(_width, _height);
+
+ // Compute class state of the content.
+ MotionNFD();
+ Spatial();
+
+ //
+ // Get transitional rate from table, based on image type and content class.
+ //
+
+ // Get image class and content class: for going down spatially
+ WebRtc_UWord8 imageClass = 1;
+ if (_imageType <= 3) imageClass = 0;
+ WebRtc_UWord8 contentClass = 3 * _motion.level + _spatial.level;
+ WebRtc_UWord8 tableIndex = imageClass * 9 + contentClass;
+ float scaleTransRate = kScaleTransRateQm[tableIndex];
+
+ // Get image class and content class: for going up spatially
+ WebRtc_UWord8 imageClass2 = 1;
+ if (imageType2 <= 3) imageClass2 = 0;
+ WebRtc_UWord8 tableIndex2 = imageClass2 * 9 + contentClass;
+ float scaleTransRate2 = kScaleTransRateQm[tableIndex2];
+
+ // Transitonal rate for going down
+ WebRtc_UWord32 estimatedTransRateDown = static_cast<WebRtc_UWord32>
+ (_incomingFrameRate * scaleTransRate * _maxRateQM / 30);
+
+ // Transitional rate for going up temporally
+ WebRtc_UWord32 estimatedTransRateUpT = static_cast<WebRtc_UWord32>
+ (TRANS_RATE_SCALE_UP_TEMP * 2 * _incomingFrameRate *
+ scaleTransRate * _maxRateQM / 30);
+
+ // Transitional rate for going up spatially
+ WebRtc_UWord32 estimatedTransRateUpS = static_cast<WebRtc_UWord32>
+ (TRANS_RATE_SCALE_UP_SPATIAL * _incomingFrameRate *
+ scaleTransRate2 * maxRateQM2 / 30);
+
+ //
+ // Done with transitional rates
+ //
+
+ //
+ //CHECK FOR GOING BACK UP IN RESOLUTION
+ //
+ bool selectedUp = false;
+ // Check if native has been spatially down-sampled
+ if (_stateDecFactorSpatial > 1)
+ {
+ // Check conditions on buffer level and rate_mismatch
+ if ( (avgTargetRate > estimatedTransRateUpS) &&
+ (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM))
+ {
+ // width/height scaled back up:
+ // setting 0 indicates scaling back to native
+ _qm->spatialHeightFact = 0;
+ _qm->spatialWidthFact = 0;
+ selectedUp = true;
+ }
+ }
+ //Check if native has been temporally down-sampled
+ if (_stateDecFactorTemp > 1)
+ {
+ if ( (avgTargetRate > estimatedTransRateUpT) &&
+ (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM))
+ {
+ // temporal scale back up:
+ // setting 0 indicates scaling back to native
+ _qm->temporalFact = 0;
+ selectedUp = true;
+ }
+ }
+
+ // Leave QM if we selected to go back up in either spatial or temporal
+ if (selectedUp == true)
+ {
+ // Update down-sampling state
+ // Note: only temp reduction by 2 is allowed
+ if (_qm->temporalFact == 0)
+ {
+ _stateDecFactorTemp = _stateDecFactorTemp / 2;
+ }
+ // Update down-sampling state
+ // Note: only spatial reduction by 2x2 is allowed
+ if (_qm->spatialHeightFact == 0 && _qm->spatialWidthFact == 0 )
+ {
+ _stateDecFactorSpatial = _stateDecFactorSpatial / 4;
+ }
+ *qm = _qm;
+ return VCM_OK;
+ }
+
+ //
+ // Done with checking for going back up in resolution
+ //
+
+ //
+ //CHECK FOR RESOLUTION REDUCTION
+ //
+
+ // Resolution reduction if:
+ // (1) target rate is lower than transitional rate, or
+ // (2) buffer level is not stable, or
+ // (3) rate mismatch is larger than threshold
+
+ // Bias down-sampling based on packet loss conditions
+ if (avgPacketLoss > LOSS_THR)
+ {
+ estimatedTransRateDown = LOSS_RATE_FAC * estimatedTransRateDown;
+ }
+
+ if ((avgTargetRate < estimatedTransRateDown ) ||
+ (ratioBufferLow > MAX_BUFFER_LOW)
+ || (rateMisMatch > MAX_RATE_MM))
+ {
+
+ WebRtc_UWord8 spatialFact = 1;
+ WebRtc_UWord8 tempFact = 1;
+
+ // Get the action
+ spatialFact = kSpatialAction[contentClass];
+ tempFact = kTemporalAction[contentClass];
+
+ switch(spatialFact)
+ {
+ case 4:
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 2;
+ break;
+ case 2:
+ //default is 1x2 (H)
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 1;
+ // Select 1x2,2x1, or back to 2x2
+ // Note: directional selection not used in this version
+ // SelectSpatialDirectionMode((float) estimatedTransRateDown);
+ break;
+ default:
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ break;
+ }
+ _qm->temporalFact = tempFact;
+
+ // Sanity check on ST QM selection:
+ // override the settings for too small image size and frame rate
+ // Also check the limit on current down-sampling state
+
+ // No spatial sampling if image size is too small (QCIF)
+ if ( (_width * _height) <= MIN_IMAGE_SIZE ||
+ _stateDecFactorSpatial >= MAX_SPATIAL_DOWN_FACT)
+ {
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ }
+
+ // No frame rate reduction below some point:
+ // use the (average) incoming frame rate
+ if ( avgIncomingFrameRate <= MIN_FRAME_RATE_QM ||
+ _stateDecFactorTemp >= MAX_TEMP_DOWN_FACT)
+ {
+ _qm->temporalFact = 1;
+ }
+
+ // No down-sampling if current downsampling state is above threshold
+ if (_stateDecFactorTemp * _stateDecFactorSpatial >=
+ MAX_SPATIAL_TEMP_DOWN_FACT)
+ {
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ _qm->temporalFact = 1;
+ }
+ //
+ // Done with sanity checks on ST QM selection
+ //
+
+ // Update down-sampling states
+ _stateDecFactorSpatial = _stateDecFactorSpatial * _qm->spatialWidthFact
+ * _qm->spatialHeightFact;
+ _stateDecFactorTemp = _stateDecFactorTemp * _qm->temporalFact;
+
+ if (_qm->spatialWidthFact != 1 || _qm->spatialHeightFact != 1 ||
+ _qm->temporalFact != 1)
+ {
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, -1,
+ "Resolution reduction occurred"
+ "Content Metrics are: Motion = %d , Spatial = %d, "
+ "Rates are: Est. Trans. BR = %d, Avg.Target BR = %f",
+ _motion.level, _spatial.level,
+ estimatedTransRateDown, avgTargetRate);
+ }
+
+ }
+ else
+ {
+ *qm = _qm;
+ return VCM_OK;
+ }
+ // Done with checking for resolution reduction
+
+ *qm = _qm;
+ return VCM_OK;
+
+
+}
+
+WebRtc_Word32
+VCMQmSelect::SelectSpatialDirectionMode(float transRate)
+{
+ // Default is 1x2 (H)
+
+ // For bit rates well below transitional rate, we select 2x2
+ if ( _targetBitRate < transRate * RATE_RED_SPATIAL_2X2 )
+ {
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 2;
+ return VCM_OK;
+ }
+
+ // Otherwise check prediction errors, aspect ratio, horizontalness
+
+ float spatialErr = _contentMetrics->spatialPredErr;
+ float spatialErrH = _contentMetrics->spatialPredErrH;
+ float spatialErrV = _contentMetrics->spatialPredErrV;
+
+ // Favor 1x2 if aspect_ratio is 16:9
+ if (_aspectRatio >= 16.0f / 9.0f )
+ {
+ //check if 1x2 has lowest prediction error
+ if (spatialErrH < spatialErr && spatialErrH < spatialErrV)
+ {
+ return VCM_OK;
+ }
+ }
+
+ // Check for 2x2 selection: favor 2x2 over 1x2 and 2x1
+ if (spatialErr < spatialErrH * (1.0f + SPATIAL_ERR_2X2_VS_H) &&
+ spatialErr < spatialErrV * (1.0f + SPATIAL_ERR_2X2_VS_V))
+ {
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 2;
+ return VCM_OK;
+ }
+
+ // Check for 2x1 selection:
+ if (spatialErrV < spatialErrH * (1.0f - SPATIAL_ERR_V_VS_H) &&
+ spatialErrV < spatialErr * (1.0f - SPATIAL_ERR_2X2_VS_V))
+ {
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 2;
+ return VCM_OK;
+ }
+
+ return VCM_OK;
+}
+
+void
+VCMQmSelect::Coherence()
+{
+ float horizNZ = _contentMetrics->motionHorizontalness;
+ float distortionNZ = _contentMetrics->motionClusterDistortion;
+
+ // Coherence measure: combine horizontalness with cluster distortion
+ _coherence.value = COH_MAX;
+ if (distortionNZ > 0.)
+ {
+ _coherence.value = horizNZ / distortionNZ;
+ }
+ _coherence.value = VCM_MIN(COH_MAX, _coherence.value);
+
+ if (_coherence.value < COHERENCE_THR)
+ {
+ _coherence.level = kLow;
+ }
+ else
+ {
+ _coherence.level = kHigh;
+ }
+
+}
+
+void
+VCMQmSelect::MotionNFD()
+{
+ _motion.value = _contentMetrics->motionMagnitudeNZ;
+
+ // Determine motion level
+ if (_motion.value < LOW_MOTION_NFD)
+ {
+ _motion.level = kLow;
+ }
+ else if (_motion.value > HIGH_MOTION_NFD)
+ {
+ _motion.level = kHigh;
+ }
+ else
+ {
+ _motion.level = kDefault;
+ }
+
+}
+
+void
+VCMQmSelect::Motion()
+{
+
+ float sizeZeroMotion = _contentMetrics->sizeZeroMotion;
+ float motionMagNZ = _contentMetrics->motionMagnitudeNZ;
+
+ // Take product of size and magnitude with equal weight
+ _motion.value = (1.0f - sizeZeroMotion) * motionMagNZ;
+
+ // Stabilize: motionMagNZ could be large when only a
+ // few motion blocks are non-zero
+ _stationaryMotion = false;
+ if (sizeZeroMotion > HIGH_ZERO_MOTION_SIZE)
+ {
+ _motion.value = 0.0f;
+ _stationaryMotion = true;
+ }
+ // Determine motion level
+ if (_motion.value < LOW_MOTION)
+ {
+ _motion.level = kLow;
+ }
+ else if (_motion.value > HIGH_MOTION)
+ {
+ _motion.level = kHigh;
+ }
+ else
+ {
+ _motion.level = kDefault;
+ }
+}
+
+
+void
+VCMQmSelect::Spatial()
+{
+ float spatialErr = _contentMetrics->spatialPredErr;
+ float spatialErrH = _contentMetrics->spatialPredErrH;
+ float spatialErrV = _contentMetrics->spatialPredErrV;
+ // Spatial measure: take average of 3 prediction errors
+ _spatial.value = (spatialErr + spatialErrH + spatialErrV) / 3.0f;
+
+ float scale = 1.0f;
+ // Reduce thresholds for HD scenes
+ if (_imageType > 3)
+ {
+ scale = (float)SCALE_TEXTURE_HD;
+ }
+
+ if (_spatial.value > scale * HIGH_TEXTURE)
+ {
+ _spatial.level = kHigh;
+ }
+ else if (_spatial.value < scale * LOW_TEXTURE)
+ {
+ _spatial.level = kLow;
+ }
+ else
+ {
+ _spatial.level = kDefault;
+ }
+}
+
+
+WebRtc_Word32
+VCMQmSelect::SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height)
+{
+ // Match image type
+ WebRtc_UWord32 imageSize = width * height;
+
+ if (imageSize < kFrameSizeTh[0])
+ {
+ _imageType = 0;
+ }
+ else if (imageSize < kFrameSizeTh[1])
+ {
+ _imageType = 1;
+ }
+ else if (imageSize < kFrameSizeTh[2])
+ {
+ _imageType = 2;
+ }
+ else if (imageSize < kFrameSizeTh[3])
+ {
+ _imageType = 3;
+ }
+ else if (imageSize < kFrameSizeTh[4])
+ {
+ _imageType = 4;
+ }
+ else if (imageSize < kFrameSizeTh[5])
+ {
+ _imageType = 5;
+ }
+ else
+ {
+ _imageType = 6;
+ }
+
+ // Set max rate based on image size
+ _maxRateQM = kMaxRateQm[_imageType];
+
+ return VCM_OK;
+}
+
+} // end of namespace
diff --git a/src/modules/video_coding/main/source/qm_select.h b/src/modules/video_coding/main/source/qm_select.h
new file mode 100644
index 0000000..3bca4bc
--- /dev/null
+++ b/src/modules/video_coding/main/source/qm_select.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
+#define WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
+
+#include "typedefs.h"
+#include "common_types.h"
+/************************/
+/* Quality Modes */
+/**********************/
+
+namespace webrtc
+{
+
+struct VideoContentMetrics;
+
+struct VCMQualityMode
+{
+ VCMQualityMode():spatialWidthFact(1), spatialHeightFact(1),
+ temporalFact(1){}
+ void Reset()
+ {
+ spatialWidthFact = 1;
+ spatialHeightFact = 1;
+ temporalFact = 1;
+ }
+
+ WebRtc_UWord16 spatialWidthFact;
+ WebRtc_UWord16 spatialHeightFact;
+ WebRtc_UWord16 temporalFact;
+};
+
+enum VCMMagValues
+{
+ kLow,
+ kHigh,
+ kDefault //default do nothing mode
+};
+
+struct VCMContFeature
+{
+ VCMContFeature(): value(0.0f), level(kDefault){}
+
+ void Reset()
+ {
+ value = 0.0f;
+ level = kDefault;
+ }
+
+ float value;
+ VCMMagValues level;
+};
+
+class VCMQmSelect
+{
+public:
+ VCMQmSelect();
+ ~VCMQmSelect();
+
+ // Initialize:
+ WebRtc_Word32 Initialize(float bitRate, float userFrameRate,
+ WebRtc_UWord32 width, WebRtc_UWord32 height);
+
+ // Allow the user to set preferences: favor frame rate/resolution
+ WebRtc_Word32 SetPreferences(WebRtc_Word8 resolPref);
+
+ // Extract ST (spatio-temporal) QM behavior and make decision
+ // Inputs: qm: Reference to the quality modes pointer
+ WebRtc_Word32 SelectQuality(VCMQualityMode** qm);
+
+ // Update QM with actual bit rate
+ // (size of the latest encoded frame) and frame type.
+ void UpdateEncodedSize(WebRtc_Word64 encodedSize,
+ FrameType encodedFrameType);
+
+ // Update QM with new bit/frame/loss rates from SetTargetRates
+ void UpdateRates(float targetBitRate, float avgSentRate,
+ float incomingFrameRate, WebRtc_UWord8 packetLoss);
+
+ // Update QM with the content metrics
+ void UpdateContent(const VideoContentMetrics* contentMetrics);
+
+ // Adjust FEC rate based on content
+ WebRtc_UWord8 AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
+ float frameRate, WebRtc_UWord16 rttTime,
+ WebRtc_UWord8 packetLoss);
+
+
+ // Select 1x2,2x2,2x2 spatial sampling mode
+ WebRtc_Word32 SelectSpatialDirectionMode(float transRate);
+
+ // Reset values prior to QMSelect
+ void ResetQM();
+
+ // Reset rate quantities and counter values after every QMSelect call
+ void ResetRates();
+
+ // Reset all
+ void Reset();
+private:
+
+ // Compute spatial texture magnitude and level
+ void Spatial();
+
+ // Compute motion magnitude and level
+ void Motion();
+
+ // Compute motion magnitude and level for NFD metric
+ void MotionNFD();
+
+ // Compute coherence magnitude and level
+ void Coherence();
+
+ // Set the max rate for QM selection
+ WebRtc_Word32 SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height);
+
+ // Content Data
+ const VideoContentMetrics* _contentMetrics;
+
+ // Encoder rate control parameters, network parameters
+ float _targetBitRate;
+ float _userFrameRate;
+ float _incomingFrameRate;
+ float _perFrameBandwidth;
+ float _bufferLevel;
+ float _sumTargetRate;
+ float _sumIncomingFrameRate;
+ float _sumSeqRateMM;
+ float _sumFrameRateMM;
+ float _sumPacketLoss;
+ float _prevTotalRate;
+ WebRtc_UWord16 _prevRttTime;
+ WebRtc_UWord8 _prevPacketLoss;
+ WebRtc_Word64 _sumEncodedBytes;
+
+ // Encoder and native frame sizes
+ WebRtc_UWord32 _width;
+ WebRtc_UWord32 _height;
+ WebRtc_UWord32 _nativeWidth;
+ WebRtc_UWord32 _nativeHeight;
+ WebRtc_UWord8 _stateDecFactorSpatial;
+
+ WebRtc_UWord32 _nativeFrameRate;
+ WebRtc_UWord8 _stateDecFactorTemp;
+
+ // Counters
+ WebRtc_UWord32 _frameCnt;
+ WebRtc_UWord32 _frameCntDelta;
+ WebRtc_UWord32 _updateRateCnt;
+ WebRtc_UWord32 _lowBufferCnt;
+
+ // Content L/M/H values
+ VCMContFeature _motion;
+ VCMContFeature _spatial;
+ VCMContFeature _coherence;
+ bool _stationaryMotion;
+
+ // Aspect ratio
+ float _aspectRatio;
+
+ // Max rate to saturate the transitionalRate
+ WebRtc_UWord32 _maxRateQM;
+ WebRtc_UWord8 _imageType;
+
+ // User preference for resolution or qmax change
+ WebRtc_UWord8 _userResolutionPref;
+ bool _init;
+ VCMQualityMode* _qm;
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
diff --git a/src/modules/video_coding/main/source/qm_select_data.h b/src/modules/video_coding/main/source/qm_select_data.h
new file mode 100644
index 0000000..813c110
--- /dev/null
+++ b/src/modules/video_coding/main/source/qm_select_data.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_
+
+/***************************************************************
+*QMSelectData.h
+* This file includes parameters for content-aware media optimization
+****************************************************************/
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+//
+// PARAMETERS FOR RESOLUTION ADAPTATION
+//
+
+// Initial level of buffer in secs: should corresponds to wrapper settings
+#define INIT_BUFFER_LEVEL 0.5
+
+// Threshold of (max) buffer size below which we consider too low (underflow)
+#define PERC_BUFFER_THR 0.10
+
+// Threshold on rate mismatch
+#define MAX_RATE_MM 0.5
+
+// Avoid outliers in seq-rate MM
+#define THRESH_SUM_MM 1000
+
+// Threshold on the occurrences of low buffer levels
+#define MAX_BUFFER_LOW 0.5
+
+// Factor for transitional rate for going back up in resolution
+#define TRANS_RATE_SCALE_UP_SPATIAL 1.25
+#define TRANS_RATE_SCALE_UP_TEMP 1.25
+
+// Threshold on packet loss rate, above which favor resolution reduction
+#define LOSS_THR 0.1
+
+// Factor for reducing transitonal bitrate under packet loss
+#define LOSS_RATE_FAC 1.0
+
+// Maximum possible transitional rate for down-sampling:
+// (units in kbps), for 30fps
+const WebRtc_UWord16 kMaxRateQm[7] = {
+ 100, //QCIF
+ 500, //CIF
+ 800, //VGA
+ 1500, //4CIF
+ 2000, //720 HD 4:3,
+ 2500, //720 HD 16:9
+ 3000 //1080HD
+};
+
+// Scale for transitional rate: based on content class
+// motion=L/H/D,spatial==L/H/D: for low, high, middle levels
+const float kScaleTransRateQm[18] = {
+ //4CIF and lower
+ 0.25f, // L, L
+ 0.75f, // L, H
+ 0.75f, // L, D
+ 0.75f, // H ,L
+ 0.50f, // H, H
+ 0.50f, // H, D
+ 0.50f, // D, L
+ 0.63f, // D, D
+ 0.25f, // D, H
+
+ //over 4CIF: WHD, HD
+ 0.25f, // L, L
+ 0.75f, // L, H
+ 0.75f, // L, D
+ 0.75f, // H ,L
+ 0.50f, // H, H
+ 0.50f, // H, D
+ 0.50f, // D, L
+ 0.63f, // D, D
+ 0.25f // D, H
+};
+
+// Action for down-sampling:
+// motion=L/H/D,spatial==L/H/D: for low, high, middle levels
+const WebRtc_UWord8 kSpatialAction[9] = {
+ 1, // L, L
+ 1, // L, H
+ 1, // L, D
+ 4, // H ,L
+ 1, // H, H
+ 4, // H, D
+ 4, // D, L
+ 1, // D, D
+ 1, // D, H
+};
+
+const WebRtc_UWord8 kTemporalAction[9] = {
+ 1, // L, L
+ 2, // L, H
+ 2, // L, D
+ 1, // H ,L
+ 2, // H, H
+ 1, // H, D
+ 1, // D, L
+ 2, // D, D
+ 1, // D, H
+};
+
+// Control the total amount of down-sampling allowed
+#define MAX_SPATIAL_DOWN_FACT 4
+#define MAX_TEMP_DOWN_FACT 4
+#define MAX_SPATIAL_TEMP_DOWN_FACT 8
+
+// Minimum image size for a spatial down-sampling:
+// no spatial down-sampling if input size <= MIN_IMAGE_SIZE
+#define MIN_IMAGE_SIZE 25344 //176*144
+
+// Minimum frame rate for temporal down-sampling:
+// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE
+#define MIN_FRAME_RATE_QM 8
+
+// Boundaries for the closest standard frame size
+const WebRtc_UWord32 kFrameSizeTh[6] = {
+ 63360, //between 176*144 and 352*288
+ 204288, //between 352*288 and 640*480
+ 356352, //between 640*480 and 704*576
+ 548352, //between 704*576 and 960*720
+ 806400, //between 960*720 and 1280*720
+ 1497600, // between 1280*720 and 1920*1080
+};
+
+
+//
+// PARAMETERS FOR FEC ADJUSTMENT: TODO (marpan)
+//
+
+
+//
+// PARAMETETS FOR SETTING LOW/HIGH STATES OF CONTENT METRICS:
+//
+
+// Threshold to determine if high amount of zero_motion
+#define HIGH_ZERO_MOTION_SIZE 0.95
+
+// Thresholds for motion:
+// motion level is derived from motion vectors: motion = size_nz*magn_nz
+#define HIGH_MOTION 0.7
+#define LOW_MOTION 0.4
+
+// Thresholds for motion: motion level is from NFD
+#define HIGH_MOTION_NFD 0.075
+#define LOW_MOTION_NFD 0.04
+
+// Thresholds for spatial prediction error:
+// this is appLied on the min(2x2,1x2,2x1)
+#define HIGH_TEXTURE 0.035
+#define LOW_TEXTURE 0.025
+
+// Used to reduce thresholds for HD scenes: correction factor since higher
+// correlation in HD scenes means lower spatial prediction error
+#define SCALE_TEXTURE_HD 0.9;
+
+// Thresholds for distortion and horizontalness:
+// applied on product: horiz_nz/dist_nz
+#define COHERENCE_THR 1.0
+#define COH_MAX 10
+
+// percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1
+#define RATE_RED_SPATIAL_2X2 0.6
+
+#define SPATIAL_ERR_2X2_VS_H 0.1 //percentage to favor 2x2
+#define SPATIAL_ERR_2X2_VS_V 0.1 //percentage to favor 2x2 over V
+#define SPATIAL_ERR_V_VS_H 0.1 //percentage to favor H over V
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_
diff --git a/src/modules/video_coding/main/source/receiver.cc b/src/modules/video_coding/main/source/receiver.cc
new file mode 100644
index 0000000..676bab8
--- /dev/null
+++ b/src/modules/video_coding/main/source/receiver.cc
@@ -0,0 +1,452 @@
+/*
+ * 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 "video_coding.h"
+#include "trace.h"
+#include "encoded_frame.h"
+#include "internal_defines.h"
+#include "receiver.h"
+#include "tick_time.h"
+
+#include <assert.h>
+
+namespace webrtc {
+
+VCMReceiver::VCMReceiver(VCMTiming& timing,
+ WebRtc_Word32 vcmId,
+ WebRtc_Word32 receiverId,
+ bool master)
+:
+_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_vcmId(vcmId),
+_receiverId(receiverId),
+_master(master),
+_jitterBuffer(vcmId, receiverId, master),
+_timing(timing),
+_renderWaitEvent(*new VCMEvent()),
+_state(kPassive)
+{
+}
+
+VCMReceiver::~VCMReceiver()
+{
+ _renderWaitEvent.Set();
+ delete &_renderWaitEvent;
+ delete &_critSect;
+}
+
+WebRtc_Word32
+VCMReceiver::Initialize()
+{
+ CriticalSectionScoped cs(_critSect);
+ if (!_jitterBuffer.Running())
+ {
+ _jitterBuffer.Start();
+ }
+ else
+ {
+ _jitterBuffer.Flush();
+ }
+ _renderWaitEvent.Reset();
+ if (_master)
+ {
+ _state = kReceiving;
+ }
+ else
+ {
+ _state = kPassive;
+ SetNackMode(kNoNack);
+ }
+ return VCM_OK;
+}
+
+void VCMReceiver::UpdateRtt(WebRtc_UWord32 rtt)
+{
+ _jitterBuffer.UpdateRtt(rtt);
+}
+
+WebRtc_Word32
+VCMReceiver::InsertPacket(const VCMPacket& packet,
+ WebRtc_UWord16 frameWidth,
+ WebRtc_UWord16 frameHeight)
+{
+ // Find an empty frame
+ VCMEncodedFrame *buffer = NULL;
+ const WebRtc_Word32 error = _jitterBuffer.GetFrame(packet, buffer);
+ if (error == VCM_OLD_PACKET_ERROR)
+ {
+ return VCM_OK;
+ }
+ else if (error < 0)
+ {
+ return error;
+ }
+
+ {
+ CriticalSectionScoped cs(_critSect);
+
+ if (frameWidth && frameHeight)
+ {
+ buffer->SetEncodedSize(static_cast<WebRtc_UWord32>(frameWidth),
+ static_cast<WebRtc_UWord32>(frameHeight));
+ }
+
+ if (_master)
+ {
+ // Only trace the primary receiver to make it possible
+ // to parse and plot the trace file.
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Packet seqNo %u of frame %u at %u",
+ packet.seqNum, packet.timestamp,
+ MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp()));
+ }
+
+ const bool emptyFrame = (buffer->Length() == 0);
+ const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
+
+ WebRtc_Word64 renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);
+
+ if (renderTimeMs < 0)
+ {
+ // Render time error. Assume that this is due to some change in
+ // the incoming video stream and reset the JB and the timing.
+ _jitterBuffer.Flush();
+ _timing.Reset();
+ return VCM_OK;
+ }
+ else if (renderTimeMs < nowMs - kMaxVideoDelayMs)
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "This frame should have been rendered more than %u ms ago."
+ "Flushing jitter buffer and resetting timing.", kMaxVideoDelayMs);
+ _jitterBuffer.Flush();
+ _timing.Reset();
+ return VCM_OK;
+ }
+ else if (_timing.TargetVideoDelay() > kMaxVideoDelayMs)
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "More than %u ms target delay. Flushing jitter buffer and resetting timing.",
+ kMaxVideoDelayMs);
+ _jitterBuffer.Flush();
+ _timing.Reset();
+ return VCM_OK;
+ }
+
+ // First packet received belonging to this frame.
+ if (buffer->Length() == 0)
+ {
+ const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
+ if (_master)
+ {
+ // Only trace the primary receiver to make it possible to parse and plot the trace file.
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "First packet of frame %u at %u", packet.timestamp,
+ MaskWord64ToUWord32(nowMs));
+ }
+ renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);
+ if (renderTimeMs >= 0)
+ {
+ buffer->SetRenderTime(renderTimeMs);
+ }
+ else
+ {
+ buffer->SetRenderTime(nowMs);
+ }
+ }
+
+ // Insert packet into jitter buffer
+ // both media and empty packets
+ const VCMFrameBufferEnum ret = _jitterBuffer.InsertPacket(buffer, packet);
+
+ if (ret < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Error inserting packet seqNo=%u, timeStamp=%u",
+ packet.seqNum, packet.timestamp);
+ return VCM_JITTER_BUFFER_ERROR;
+ }
+ }
+ return VCM_OK;
+}
+
+VCMEncodedFrame*
+VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, WebRtc_Word64& nextRenderTimeMs,
+ bool renderTiming, VCMReceiver* dualReceiver)
+{
+ // No need to enter the critical section here since the jitter buffer
+ // is thread-safe.
+ FrameType incomingFrameType = kVideoFrameDelta;
+ nextRenderTimeMs = -1;
+ const WebRtc_Word64 startTimeMs = VCMTickTime::MillisecondTimestamp();
+ WebRtc_Word64 ret = _jitterBuffer.GetNextTimeStamp(maxWaitTimeMs,
+ incomingFrameType,
+ nextRenderTimeMs);
+ if (ret < 0)
+ {
+ // No timestamp in jitter buffer at the moment
+ return NULL;
+ }
+ const WebRtc_UWord32 timeStamp = static_cast<WebRtc_UWord32>(ret);
+
+ // Update the timing
+ _timing.SetRequiredDelay(_jitterBuffer.GetEstimatedJitterMS());
+ _timing.UpdateCurrentDelay(timeStamp);
+
+ const WebRtc_Word32 tempWaitTime = maxWaitTimeMs -
+ static_cast<WebRtc_Word32>(VCMTickTime::MillisecondTimestamp() - startTimeMs);
+ WebRtc_UWord16 newMaxWaitTime = static_cast<WebRtc_UWord16>(VCM_MAX(tempWaitTime, 0));
+
+ VCMEncodedFrame* frame = NULL;
+
+ if (renderTiming)
+ {
+ frame = FrameForDecoding(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
+ }
+ else
+ {
+ frame = FrameForRendering(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
+ }
+
+ if (frame != NULL)
+ {
+ bool retransmitted = false;
+ const WebRtc_Word64 lastPacketTimeMs =
+ _jitterBuffer.LastPacketTime(frame, retransmitted);
+ if (lastPacketTimeMs >= 0 && !retransmitted)
+ {
+ // We don't want to include timestamps which have suffered from retransmission
+ // here, since we compensate with extra retransmission delay within
+ // the jitter estimate.
+ _timing.IncomingTimestamp(timeStamp, lastPacketTimeMs);
+ }
+ if (dualReceiver != NULL)
+ {
+ dualReceiver->UpdateState(*frame);
+ }
+ }
+ return frame;
+}
+
+VCMEncodedFrame*
+VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs,
+ WebRtc_Word64 nextRenderTimeMs,
+ VCMReceiver* dualReceiver)
+{
+ // How long can we wait until we must decode the next frame
+ WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
+ VCMTickTime::MillisecondTimestamp());
+
+ // Try to get a complete frame from the jitter buffer
+ VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);
+
+ if (frame == NULL && maxWaitTimeMs == 0 && waitTimeMs > 0)
+ {
+ // If we're not allowed to wait for frames to get complete we must calculate if
+ // it's time to decode, and if it's not we will just return for now.
+ return NULL;
+ }
+
+ if (frame == NULL)
+ {
+ // Wait for a complete frame
+ waitTimeMs = VCM_MIN(waitTimeMs, maxWaitTimeMs);
+ frame = _jitterBuffer.GetCompleteFrameForDecoding(waitTimeMs);
+ }
+ if (frame == NULL)
+ {
+ // Get an incomplete frame
+ if (_timing.MaxWaitingTime(nextRenderTimeMs, VCMTickTime::MillisecondTimestamp()) > 0)
+ {
+ // Still time to wait for a complete frame
+ return NULL;
+ }
+
+ // No time left to wait, we must decode this frame now.
+ const bool dualReceiverEnabledAndPassive = dualReceiver != NULL &&
+ dualReceiver->State() == kPassive &&
+ dualReceiver->NackMode() == kNackInfinite;
+ if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame())
+ {
+ // Jitter buffer state might get corrupt with this frame.
+ dualReceiver->CopyJitterBufferStateFromReceiver(*this);
+ }
+
+ frame = _jitterBuffer.GetFrameForDecoding();
+ }
+ return frame;
+}
+
+VCMEncodedFrame*
+VCMReceiver::FrameForRendering(WebRtc_UWord16 maxWaitTimeMs,
+ WebRtc_Word64 nextRenderTimeMs,
+ VCMReceiver* dualReceiver)
+{
+ // How long MUST we wait until we must decode the next frame. This is different for the case
+ // where we have a renderer which can render at a specified time. Here we must wait as long
+ // as possible before giving the frame to the decoder, which will render the frame as soon
+ // as it has been decoded.
+ WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
+ VCMTickTime::MillisecondTimestamp());
+ if (maxWaitTimeMs < waitTimeMs)
+ {
+ // If we're not allowed to wait until the frame is supposed to be rendered
+ // we will have to return NULL for now.
+ return NULL;
+ }
+ // Wait until it's time to render
+ _renderWaitEvent.Wait(waitTimeMs);
+
+ // Get a complete frame if possible
+ VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);
+
+ if (frame == NULL)
+ {
+ // Get an incomplete frame
+ const bool dualReceiverEnabledAndPassive = dualReceiver != NULL &&
+ dualReceiver->State() == kPassive &&
+ dualReceiver->NackMode() == kNackInfinite;
+ if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame())
+ {
+ // Jitter buffer state might get corrupt with this frame.
+ dualReceiver->CopyJitterBufferStateFromReceiver(*this);
+ }
+
+ frame = _jitterBuffer.GetFrameForDecoding();
+ }
+ return frame;
+}
+
+void
+VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame)
+{
+ _jitterBuffer.ReleaseFrame(frame);
+}
+
+WebRtc_Word32
+VCMReceiver::ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate)
+{
+ const WebRtc_Word32 ret = _jitterBuffer.GetUpdate(frameRate, bitRate);
+ bitRate /= 1000; // Should be in kbps
+ return ret;
+}
+
+WebRtc_Word32
+VCMReceiver::ReceivedFrameCount(VCMFrameCount& frameCount) const
+{
+ return _jitterBuffer.GetFrameStatistics(frameCount.numDeltaFrames,
+ frameCount.numKeyFrames);
+}
+
+void
+VCMReceiver::SetNackMode(VCMNackMode nackMode)
+{
+ CriticalSectionScoped cs(_critSect);
+ _jitterBuffer.SetNackMode(nackMode);
+ if (!_master)
+ {
+ _state = kPassive; // The dual decoder defaults to passive
+ }
+}
+
+VCMNackMode
+VCMReceiver::NackMode() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return _jitterBuffer.GetNackMode();
+}
+
+VCMNackStatus
+VCMReceiver::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size)
+{
+ bool extended = false;
+ WebRtc_UWord16 nackListSize = 0;
+ WebRtc_UWord16* internalNackList = _jitterBuffer.GetNackList(nackListSize, extended);
+ if (internalNackList == NULL && nackListSize == 0xffff)
+ {
+ // This combination is used to trigger key frame requests.
+ size = 0;
+ return kNackKeyFrameRequest;
+ }
+ if (nackListSize > size)
+ {
+ size = nackListSize;
+ return kNackNeedMoreMemory;
+ }
+ memcpy(nackList, internalNackList, nackListSize * sizeof(WebRtc_UWord16));
+ size = nackListSize;
+ return kNackOk;
+}
+
+// Decide whether we should change decoder state. This should be done if the dual decoder
+// has caught up with the decoder decoding with packet losses.
+bool
+VCMReceiver::DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const
+{
+ if (dualFrame == NULL)
+ {
+ return false;
+ }
+ if (_jitterBuffer.LastDecodedTimestamp() == dualFrame->TimeStamp())
+ {
+ dualReceiver.UpdateState(kWaitForPrimaryDecode);
+ return true;
+ }
+ return false;
+}
+
+void
+VCMReceiver::CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver)
+{
+ _jitterBuffer = receiver._jitterBuffer;
+}
+
+VCMReceiverState
+VCMReceiver::State() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return _state;
+}
+
+void
+VCMReceiver::UpdateState(VCMReceiverState newState)
+{
+ CriticalSectionScoped cs(_critSect);
+ assert(!(_state == kPassive && newState == kWaitForPrimaryDecode));
+// assert(!(_state == kReceiving && newState == kPassive));
+ _state = newState;
+}
+
+void
+VCMReceiver::UpdateState(VCMEncodedFrame& frame)
+{
+ if (_jitterBuffer.GetNackMode() == kNoNack)
+ {
+ // Dual decoder mode has not been enabled.
+ return;
+ }
+ // Update the dual receiver state
+ if (frame.Complete() && frame.FrameType() == kVideoFrameKey)
+ {
+ UpdateState(kPassive);
+ }
+ if (State() == kWaitForPrimaryDecode &&
+ frame.Complete() && !frame.MissingFrame())
+ {
+ UpdateState(kPassive);
+ }
+ if (frame.MissingFrame() || !frame.Complete())
+ {
+ // State was corrupted, enable dual receiver.
+ UpdateState(kReceiving);
+ }
+}
+
+}
diff --git a/src/modules/video_coding/main/source/receiver.h b/src/modules/video_coding/main/source/receiver.h
new file mode 100644
index 0000000..0ca6994
--- /dev/null
+++ b/src/modules/video_coding/main/source/receiver.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_
+
+#include "critical_section_wrapper.h"
+#include "jitter_buffer.h"
+#include "timing.h"
+#include "packet.h"
+
+namespace webrtc
+{
+
+class VCMEncodedFrame;
+
+enum VCMNackStatus
+{
+ kNackOk,
+ kNackNeedMoreMemory,
+ kNackKeyFrameRequest
+};
+
+
+enum VCMReceiverState
+{
+ kReceiving,
+ kPassive,
+ kWaitForPrimaryDecode
+};
+
+class VCMReceiver
+{
+public:
+ VCMReceiver(VCMTiming& timing,
+ WebRtc_Word32 vcmId = -1,
+ WebRtc_Word32 receiverId = -1,
+ bool master = true);
+ ~VCMReceiver();
+
+ WebRtc_Word32 Initialize();
+ void UpdateRtt(WebRtc_UWord32 rtt);
+ WebRtc_Word32 InsertPacket(const VCMPacket& packet,
+ WebRtc_UWord16 frameWidth,
+ WebRtc_UWord16 frameHeight);
+ VCMEncodedFrame* FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs,
+ WebRtc_Word64& nextRenderTimeMs,
+ bool renderTiming = true,
+ VCMReceiver* dualReceiver = NULL);
+ void ReleaseFrame(VCMEncodedFrame* frame);
+ WebRtc_Word32 ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate);
+ WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const;
+
+ // NACK
+ void SetNackMode(VCMNackMode nackMode);
+ VCMNackMode NackMode() const;
+ VCMNackStatus NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size);
+
+ // Dual decoder
+ bool DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const;
+ VCMReceiverState State() const;
+
+private:
+ VCMEncodedFrame* FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs,
+ WebRtc_Word64 nextrenderTimeMs,
+ VCMReceiver* dualReceiver);
+ VCMEncodedFrame* FrameForRendering(WebRtc_UWord16 maxWaitTimeMs,
+ WebRtc_Word64 nextrenderTimeMs,
+ VCMReceiver* dualReceiver);
+ void CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver);
+ void UpdateState(VCMReceiverState newState);
+ void UpdateState(VCMEncodedFrame& frame);
+ static WebRtc_Word32 GenerateReceiverId();
+
+ CriticalSectionWrapper& _critSect;
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _receiverId;
+ bool _master;
+ VCMJitterBuffer _jitterBuffer;
+ VCMTiming& _timing;
+ VCMEvent& _renderWaitEvent;
+ VCMReceiverState _state;
+
+ static WebRtc_Word32 _receiverIdCounter;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_
diff --git a/src/modules/video_coding/main/source/rtt_filter.cc b/src/modules/video_coding/main/source/rtt_filter.cc
new file mode 100644
index 0000000..36f7660
--- /dev/null
+++ b/src/modules/video_coding/main/source/rtt_filter.cc
@@ -0,0 +1,214 @@
+/*
+ * 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 "trace.h"
+#include "internal_defines.h"
+#include "rtt_filter.h"
+
+#include <cmath>
+#include <stdlib.h>
+#include <string.h>
+
+namespace webrtc {
+
+VCMRttFilter::VCMRttFilter(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId)
+:
+_vcmId(vcmId),
+_receiverId(receiverId),
+_filtFactMax(35),
+_jumpStdDevs(2.5),
+_driftStdDevs(3.5),
+_detectThreshold(kMaxDriftJumpCount)
+{
+ Reset();
+}
+
+VCMRttFilter&
+VCMRttFilter::operator=(const VCMRttFilter& rhs)
+{
+ if (this != &rhs)
+ {
+ _gotNonZeroUpdate = rhs._gotNonZeroUpdate;
+ _avgRtt = rhs._avgRtt;
+ _varRtt = rhs._varRtt;
+ _maxRtt = rhs._maxRtt;
+ _filtFactCount = rhs._filtFactCount;
+ _jumpCount = rhs._jumpCount;
+ _driftCount = rhs._driftCount;
+ memcpy(_jumpBuf, rhs._jumpBuf, sizeof(_jumpBuf));
+ memcpy(_driftBuf, rhs._driftBuf, sizeof(_driftBuf));
+ }
+ return *this;
+}
+
+void
+VCMRttFilter::Reset()
+{
+ _gotNonZeroUpdate = false;
+ _avgRtt = 0;
+ _varRtt = 0;
+ _maxRtt = 0;
+ _filtFactCount = 1;
+ _jumpCount = 0;
+ _driftCount = 0;
+ memset(_jumpBuf, 0, kMaxDriftJumpCount);
+ memset(_driftBuf, 0, kMaxDriftJumpCount);
+}
+
+void
+VCMRttFilter::Update(WebRtc_UWord32 rttMs)
+{
+ if (!_gotNonZeroUpdate)
+ {
+ if (rttMs == 0)
+ {
+ return;
+ }
+ _gotNonZeroUpdate = true;
+ }
+
+ // Sanity check
+ if (rttMs > 3000)
+ {
+ rttMs = 3000;
+ }
+
+ double filtFactor = 0;
+ if (_filtFactCount > 1)
+ {
+ filtFactor = static_cast<double>(_filtFactCount - 1) / _filtFactCount;
+ }
+ _filtFactCount++;
+ if (_filtFactCount > _filtFactMax)
+ {
+ // This prevents filtFactor from going above
+ // (_filtFactMax - 1) / _filtFactMax,
+ // e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98
+ _filtFactCount = _filtFactMax;
+ }
+ double oldAvg = _avgRtt;
+ double oldVar = _varRtt;
+ _avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs;
+ _varRtt = filtFactor * _varRtt + (1 - filtFactor) *
+ (rttMs - _avgRtt) * (rttMs - _avgRtt);
+ _maxRtt = VCM_MAX(rttMs, _maxRtt);
+ if (!JumpDetection(rttMs) || !DriftDetection(rttMs))
+ {
+ // In some cases we don't want to update the statistics
+ _avgRtt = oldAvg;
+ _varRtt = oldVar;
+ }
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "RttFilter Update: sample=%u avgRtt=%f varRtt=%f maxRtt=%u",
+ rttMs, _avgRtt, _varRtt, _maxRtt);
+}
+
+bool
+VCMRttFilter::JumpDetection(WebRtc_UWord32 rttMs)
+{
+ double diffFromAvg = _avgRtt - rttMs;
+ if (abs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
+ {
+ int diffSign = (diffFromAvg >= 0) ? 1 : -1;
+ int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
+ if (diffSign != jumpCountSign)
+ {
+ // Since the signs differ the samples currently
+ // in the buffer is useless as they represent a
+ // jump in a different direction.
+ _jumpCount = 0;
+ }
+ if (abs(_jumpCount) < kMaxDriftJumpCount)
+ {
+ // Update the buffer used for the short time
+ // statistics.
+ // The sign of the diff is used for updating the counter since
+ // we want to use the same buffer for keeping track of when
+ // the RTT jumps down and up.
+ _jumpBuf[abs(_jumpCount)] = rttMs;
+ _jumpCount += diffSign;
+ }
+ if (abs(_jumpCount) >= _detectThreshold)
+ {
+ // Detected an RTT jump
+ ShortRttFilter(_jumpBuf, abs(_jumpCount));
+ _filtFactCount = _detectThreshold + 1;
+ _jumpCount = 0;
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Detected an RTT jump");
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ _jumpCount = 0;
+ }
+ return true;
+}
+
+bool
+VCMRttFilter::DriftDetection(WebRtc_UWord32 rttMs)
+{
+ if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt))
+ {
+ if (_driftCount < kMaxDriftJumpCount)
+ {
+ // Update the buffer used for the short time
+ // statistics.
+ _driftBuf[_driftCount] = rttMs;
+ _driftCount++;
+ }
+ if (_driftCount >= _detectThreshold)
+ {
+ // Detected an RTT drift
+ ShortRttFilter(_driftBuf, _driftCount);
+ _filtFactCount = _detectThreshold + 1;
+ _driftCount = 0;
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
+ "Detected an RTT drift");
+ }
+ }
+ else
+ {
+ _driftCount = 0;
+ }
+ return true;
+}
+
+void
+VCMRttFilter::ShortRttFilter(WebRtc_UWord32* buf, WebRtc_UWord32 length)
+{
+ if (length == 0)
+ {
+ return;
+ }
+ _maxRtt = 0;
+ _avgRtt = 0;
+ for (WebRtc_UWord32 i=0; i < length; i++)
+ {
+ if (buf[i] > _maxRtt)
+ {
+ _maxRtt = buf[i];
+ }
+ _avgRtt += buf[i];
+ }
+ _avgRtt = _avgRtt / static_cast<double>(length);
+}
+
+WebRtc_UWord32
+VCMRttFilter::RttMs() const
+{
+ return static_cast<WebRtc_UWord32>(_maxRtt + 0.5);
+}
+
+}
diff --git a/src/modules/video_coding/main/source/rtt_filter.h b/src/modules/video_coding/main/source/rtt_filter.h
new file mode 100644
index 0000000..5ec85fd
--- /dev/null
+++ b/src/modules/video_coding/main/source/rtt_filter.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+class VCMRttFilter
+{
+public:
+ VCMRttFilter(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0);
+
+ VCMRttFilter& operator=(const VCMRttFilter& rhs);
+
+ // Resets the filter.
+ void Reset();
+ // Updates the filter with a new sample.
+ void Update(WebRtc_UWord32 rttMs);
+ // A getter function for the current RTT level in ms.
+ WebRtc_UWord32 RttMs() const;
+
+private:
+ // The size of the drift and jump memory buffers
+ // and thus also the detection threshold for these
+ // detectors in number of samples.
+ enum { kMaxDriftJumpCount = 5 };
+ // Detects RTT jumps by comparing the difference between
+ // samples and average to the standard deviation.
+ // Returns true if the long time statistics should be updated
+ // and false otherwise
+ bool JumpDetection(WebRtc_UWord32 rttMs);
+ // Detects RTT drifts by comparing the difference between
+ // max and average to the standard deviation.
+ // Returns true if the long time statistics should be updated
+ // and false otherwise
+ bool DriftDetection(WebRtc_UWord32 rttMs);
+ // Computes the short time average and maximum of the vector buf.
+ void ShortRttFilter(WebRtc_UWord32* buf, WebRtc_UWord32 length);
+
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _receiverId;
+ bool _gotNonZeroUpdate;
+ double _avgRtt;
+ double _varRtt;
+ WebRtc_UWord32 _maxRtt;
+ WebRtc_UWord32 _filtFactCount;
+ const WebRtc_UWord32 _filtFactMax;
+ const double _jumpStdDevs;
+ const double _driftStdDevs;
+ WebRtc_Word32 _jumpCount;
+ WebRtc_Word32 _driftCount;
+ const WebRtc_Word32 _detectThreshold;
+ WebRtc_UWord32 _jumpBuf[kMaxDriftJumpCount];
+ WebRtc_UWord32 _driftBuf[kMaxDriftJumpCount];
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_
diff --git a/src/modules/video_coding/main/source/session_info.cc b/src/modules/video_coding/main/source/session_info.cc
new file mode 100644
index 0000000..7145b29
--- /dev/null
+++ b/src/modules/video_coding/main/source/session_info.cc
@@ -0,0 +1,833 @@
+/*
+ * 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 "packet.h"
+#include "session_info.h"
+
+#include <string.h>
+#include <cassert>
+
+namespace webrtc {
+
+VCMSessionInfo::VCMSessionInfo():
+ _haveFirstPacket(false),
+ _markerBit(false),
+ _sessionNACK(false),
+ _completeSession(false),
+ _frameType(kVideoFrameDelta),
+ _previousFrameLoss(false),
+ _lowSeqNum(-1),
+ _highSeqNum(-1),
+ _highestPacketIndex(0),
+ _emptySeqNumLow(-1),
+ _emptySeqNumHigh(-1),
+ _markerSeqNum(-1)
+{
+ memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes));
+ memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness));
+ memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte));
+}
+
+VCMSessionInfo::~VCMSessionInfo()
+{
+}
+
+WebRtc_Word32
+VCMSessionInfo::GetLowSeqNum() const
+{
+ return _lowSeqNum;
+}
+
+WebRtc_Word32
+VCMSessionInfo::GetHighSeqNum() const
+{
+ if (_emptySeqNumHigh != -1)
+ {
+ return _emptySeqNumHigh;
+ }
+ return _highSeqNum;
+}
+
+void
+VCMSessionInfo::Reset()
+{
+ _lowSeqNum = -1;
+ _highSeqNum = -1;
+ _emptySeqNumLow = -1;
+ _emptySeqNumHigh = -1;
+ _markerBit = false;
+ _haveFirstPacket = false;
+ _completeSession = false;
+ _frameType = kVideoFrameDelta;
+ _previousFrameLoss = false;
+ _sessionNACK = false;
+ _highestPacketIndex = 0;
+ _markerSeqNum = -1;
+ memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes));
+ memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness));
+ memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte));
+}
+
+WebRtc_UWord32 VCMSessionInfo::GetSessionLength()
+{
+ WebRtc_UWord32 length = 0;
+ for (WebRtc_Word32 i = 0; i <= _highestPacketIndex; ++i)
+ {
+ length += _packetSizeBytes[i];
+ }
+ return length;
+}
+
+void
+VCMSessionInfo::SetStartSeqNumber(WebRtc_UWord16 seqNumber)
+{
+ _lowSeqNum = seqNumber;
+ _highSeqNum = seqNumber;
+}
+
+bool
+VCMSessionInfo::HaveStartSeqNumber()
+{
+ if (_lowSeqNum == -1 || _highSeqNum == -1)
+ {
+ return false;
+ }
+ return true;
+}
+
+WebRtc_UWord32
+VCMSessionInfo::InsertBuffer(WebRtc_UWord8* ptrStartOfLayer,
+ WebRtc_Word32 packetIndex,
+ const VCMPacket& packet)
+{
+ WebRtc_UWord32 moveLength = 0;
+ WebRtc_UWord32 returnLength = 0;
+ int i = 0;
+
+ // need to calc offset before updating _packetSizeBytes
+ WebRtc_UWord32 offset = 0;
+ WebRtc_UWord32 packetSize = 0;
+
+ // Store this packet length. Add length since we could have data present
+ // already (e.g. multicall case).
+ if (packet.bits)
+ {
+ packetSize = packet.sizeBytes;
+ }
+ else
+ {
+ packetSize = packet.sizeBytes +
+ (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
+ }
+
+ _packetSizeBytes[packetIndex] += packetSize;
+
+ // count only the one in our layer
+ for (i = 0; i < packetIndex; ++i)
+ {
+ offset += _packetSizeBytes[i];
+ }
+ for (i = packetIndex + 1; i <= _highestPacketIndex; ++i)
+ {
+ moveLength += _packetSizeBytes[i];
+ }
+ if (moveLength > 0)
+ {
+ memmove((void*)(ptrStartOfLayer + offset + packetSize),
+ ptrStartOfLayer + offset, moveLength);
+ }
+
+ if (packet.bits)
+ {
+ // Add the packet without ORing end and start bytes together.
+ // This is done when the frame is fetched for decoding by calling
+ // GlueTogether().
+ _ORwithPrevByte[packetIndex] = true;
+ if (packet.dataPtr != NULL)
+ {
+ memcpy((void*)(ptrStartOfLayer + offset), packet.dataPtr,
+ packetSize);
+ }
+ returnLength = packetSize;
+ }
+ else
+ {
+ _ORwithPrevByte[packetIndex] = false;
+ if (packet.dataPtr != NULL)
+ {
+ const unsigned char startCode[] = {0, 0, 0, 1};
+ if(packet.insertStartCode)
+ {
+ memcpy((void*)(ptrStartOfLayer + offset), startCode,
+ kH264StartCodeLengthBytes);
+ }
+ memcpy((void*)(ptrStartOfLayer + offset
+ + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)),
+ packet.dataPtr,
+ packet.sizeBytes);
+ }
+ returnLength = packetSize;
+ }
+
+ if (packet.isFirstPacket)
+ {
+ _haveFirstPacket = true;
+ }
+ if (packet.markerBit)
+ {
+ _markerBit = true;
+ _markerSeqNum = packet.seqNum;
+ }
+ // Store information about if the packet is decodable as is or not.
+ _naluCompleteness[packetIndex] = packet.completeNALU;
+
+ UpdateCompleteSession();
+
+ return returnLength;
+}
+
+void
+VCMSessionInfo::UpdateCompleteSession()
+{
+ if (_haveFirstPacket && _markerBit)
+ {
+ // Do we have all the packets in this session?
+ bool completeSession = true;
+
+ for (int i = 0; i <= _highestPacketIndex; ++i)
+ {
+ if (_naluCompleteness[i] == kNaluUnset)
+ {
+ completeSession = false;
+ break;
+ }
+ }
+ _completeSession = completeSession;
+ }
+}
+
+bool VCMSessionInfo::IsSessionComplete()
+{
+ return _completeSession;
+}
+
+// Find the start and end index of packetIndex packet.
+// startIndex -1 if start not found endIndex = -1 if end index not found
+void
+VCMSessionInfo::FindNaluBorder(WebRtc_Word32 packetIndex,
+ WebRtc_Word32& startIndex,
+ WebRtc_Word32& endIndex)
+{
+ if (_naluCompleteness[packetIndex] == kNaluStart ||
+ _naluCompleteness[packetIndex] == kNaluComplete)
+ {
+ startIndex = packetIndex;
+ }
+ else // Need to find the start
+ {
+ for (startIndex = packetIndex - 1; startIndex >= 0; --startIndex)
+ {
+
+ if ((_naluCompleteness[startIndex] == kNaluComplete &&
+ _packetSizeBytes[startIndex] > 0) ||
+ // Found previous NALU.
+ (_naluCompleteness[startIndex] == kNaluEnd &&
+ startIndex > 0))
+ {
+ startIndex++;
+ break;
+ }
+ // This is where the NALU start.
+ if (_naluCompleteness[startIndex] == kNaluStart)
+ {
+ break;
+ }
+ }
+ }
+
+ if (_naluCompleteness[packetIndex] == kNaluEnd ||
+ _naluCompleteness[packetIndex] == kNaluComplete)
+ {
+ endIndex = packetIndex;
+ }
+ else
+ {
+ // Find the next NALU
+ for (endIndex = packetIndex + 1; endIndex <= _highestPacketIndex;
+ ++endIndex)
+ {
+ if ((_naluCompleteness[endIndex] == kNaluComplete &&
+ _packetSizeBytes[endIndex] > 0) ||
+ // Found next NALU.
+ _naluCompleteness[endIndex] == kNaluStart)
+ {
+ endIndex--;
+ break;
+ }
+ if ( _naluCompleteness[endIndex] == kNaluEnd)
+ {
+ // This is where the NALU end.
+ break;
+ }
+ }
+ if (endIndex > _highestPacketIndex)
+ {
+ endIndex = -1;
+ }
+ }
+}
+
+// Deletes all packets between startIndex and endIndex
+WebRtc_UWord32
+VCMSessionInfo::DeletePackets(WebRtc_UWord8* ptrStartOfLayer,
+ WebRtc_Word32 startIndex,
+ WebRtc_Word32 endIndex)
+{
+
+ //Get the number of bytes to delete.
+ //Clear the size of these packets.
+ WebRtc_UWord32 bytesToDelete = 0; /// The number of bytes to delete.
+ for (int j = startIndex;j <= endIndex; ++j)
+ {
+ bytesToDelete += _packetSizeBytes[j];
+ _packetSizeBytes[j] = 0;
+ }
+ if (bytesToDelete > 0)
+ {
+ // Get the offset we want to move to.
+ int destOffset = 0;
+ for (int j = 0;j < startIndex;j++)
+ {
+ destOffset += _packetSizeBytes[j];
+ }
+
+ //Get the number of bytes to move
+ WebRtc_UWord32 numberOfBytesToMove = 0;
+ for (int j = endIndex + 1; j <= _highestPacketIndex; ++j)
+ {
+ numberOfBytesToMove += _packetSizeBytes[j];
+ }
+
+ memmove((void*)(ptrStartOfLayer + destOffset),(void*)(ptrStartOfLayer +
+ destOffset+bytesToDelete), numberOfBytesToMove);
+
+ }
+
+ return bytesToDelete;
+}
+
+// Makes the layer decodable. Ie only contain decodable NALU
+// return the number of bytes deleted from the session. -1 if an error occurs
+WebRtc_UWord32
+VCMSessionInfo::MakeSessionDecodable(WebRtc_UWord8* ptrStartOfLayer)
+{
+ if (_lowSeqNum < 0) // No packets in this session
+ {
+ return 0;
+ }
+
+ WebRtc_Word32 startIndex = 0;
+ WebRtc_Word32 endIndex = 0;
+ int packetIndex = 0;
+ WebRtc_UWord32 returnLength = 0;
+ for (packetIndex = 0; packetIndex <= _highestPacketIndex; ++packetIndex)
+ {
+ if (_naluCompleteness[packetIndex] == kNaluUnset) // Found a lost packet
+ {
+ FindNaluBorder(packetIndex, startIndex, endIndex);
+ if (startIndex == -1)
+ {
+ startIndex = 0;
+ }
+ if (endIndex == -1)
+ {
+ endIndex = _highestPacketIndex;
+ }
+
+ returnLength += DeletePackets(ptrStartOfLayer,
+ packetIndex, endIndex);
+ packetIndex = endIndex;
+ }// end lost packet
+ }
+
+ // Make sure the first packet is decodable (Either complete nalu or start
+ // of NALU)
+ if (_packetSizeBytes[0] > 0)
+ {
+ switch (_naluCompleteness[0])
+ {
+ case kNaluComplete: // Packet can be decoded as is.
+ break;
+
+ case kNaluStart:
+ // Packet contain beginning of NALU- No need to do anything.
+ break;
+ case kNaluIncomplete: //Packet is not beginning or end of NALU
+ // Need to find the end of this NALU and delete all packets.
+ FindNaluBorder(0,startIndex,endIndex);
+ if (endIndex == -1) // No end found. Delete
+ {
+ endIndex = _highestPacketIndex;
+ }
+ // Delete this NALU.
+ returnLength += DeletePackets(ptrStartOfLayer, 0, endIndex);
+ break;
+ case kNaluEnd: // Packet is the end of a NALU
+ // Delete this NALU
+ returnLength += DeletePackets(ptrStartOfLayer, 0, 0);
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ return returnLength;
+}
+
+WebRtc_Word32
+VCMSessionInfo::ZeroOutSeqNum(WebRtc_Word32* list,
+ WebRtc_Word32 numberOfSeqNum)
+{
+ if ((NULL == list) || (numberOfSeqNum < 1))
+ {
+ return -1;
+ }
+ if (_lowSeqNum == -1)
+ {
+ // no packets in this frame
+ return 0;
+ }
+
+ // Find end point (index of entry that equals _lowSeqNum)
+ int index = 0;
+ for (; index < numberOfSeqNum; index++)
+ {
+ if (list[index] == _lowSeqNum)
+ {
+ list[index] = -1;
+ break;
+ }
+ }
+
+ // Zero out between first entry and end point
+ int i = 0;
+ while ( i <= _highestPacketIndex && index < numberOfSeqNum)
+ {
+ if (_naluCompleteness[i] != kNaluUnset)
+ {
+ list[index] = -1;
+ }
+ else
+ {
+ _sessionNACK = true;
+ }
+ i++;
+ index++;
+ }
+ if (!_haveFirstPacket)
+ {
+ _sessionNACK = true;
+ }
+ return 0;
+}
+
+WebRtc_Word32
+VCMSessionInfo::ZeroOutSeqNumHybrid(WebRtc_Word32* list,
+ WebRtc_Word32 numberOfSeqNum,
+ float rttScore)
+{
+ if ((NULL == list) || (numberOfSeqNum < 1))
+ {
+ return -1;
+ }
+ if (_lowSeqNum == -1)
+ {
+ // no media packets in this frame
+ return 0;
+ }
+
+ WebRtc_Word32 index = 0;
+ // Find end point (index of entry that equals _lowSeqNum)
+ for (; index < numberOfSeqNum; index++)
+ {
+ if (list[index] == _lowSeqNum)
+ {
+ list[index] = -1;
+ break;
+ }
+ }
+
+ // TODO(mikhal): 1. update score based on RTT value 2. add partition data
+ // use the previous available
+ bool isBaseAvailable = false;
+ if ((index > 0) && (list[index] == -1))
+ {
+ // found first packet, for now let's go only one back
+ if ((list[index - 1] == -1) || (list[index - 1] == -2))
+ {
+ // this is indeed the first packet, as previous packet was populated
+ isBaseAvailable = true;
+ }
+ }
+ bool allowNack = false;
+ if (!_haveFirstPacket || !isBaseAvailable)
+ {
+ allowNack = true;
+ }
+
+ // Zero out between first entry and end point
+ int i = 0;
+ // Score place holder - based on RTT and partition (when available).
+ const float nackScoreTh = 0.25f;
+
+ WebRtc_Word32 highMediaPacket;
+ if (_markerSeqNum != -1)
+ {
+ highMediaPacket = _markerSeqNum;
+ }
+ else
+ {
+ highMediaPacket = _emptySeqNumLow - 1 > _highSeqNum ?
+ _emptySeqNumLow - 1: _highSeqNum;
+ }
+
+ while (list[index] <= highMediaPacket && index < numberOfSeqNum)
+ {
+ if (_naluCompleteness[i] != kNaluUnset)
+ {
+ list[index] = -1;
+ }
+ else
+ {
+ // compute score of the packet
+ float score = 1.0f;
+ // multiply internal score (importance) by external score (RTT)
+ score *= rttScore;
+ if (score > nackScoreTh)
+ {
+ allowNack = true;
+ }
+ else
+ {
+ list[index] = -1;
+ }
+ }
+ i++;
+ index++;
+ }
+ // Empty packets follow the data packets, and therefore have a higher
+ // sequence number. We do not want to NACK empty packets.
+
+ if ((_emptySeqNumLow != -1) && (_emptySeqNumHigh != -1) &&
+ (index < numberOfSeqNum))
+ {
+ // first make sure that we are at least at the minimum value
+ // (if not we are missing last packet(s))
+ while (list[index] < _emptySeqNumLow && index < numberOfSeqNum)
+ {
+ index++;
+ }
+
+ // mark empty packets
+ while (list[index] <= _emptySeqNumHigh && index < numberOfSeqNum)
+ {
+ list[index] = -2;
+ index++;
+ }
+ }
+
+ _sessionNACK = allowNack;
+ return 0;
+}
+
+WebRtc_Word32
+VCMSessionInfo::GetHighestPacketIndex()
+{
+ return _highestPacketIndex;
+}
+
+bool
+VCMSessionInfo::HaveLastPacket()
+{
+ return _markerBit;
+}
+
+void
+VCMSessionInfo::ForceSetHaveLastPacket()
+{
+ _markerBit = true;
+ UpdateCompleteSession();
+}
+
+bool
+VCMSessionInfo::IsRetransmitted()
+{
+ return _sessionNACK;
+}
+
+void
+VCMSessionInfo::UpdatePacketSize(WebRtc_Word32 packetIndex,
+ WebRtc_UWord32 length)
+{
+ // sanity
+ if (packetIndex >= kMaxPacketsInJitterBuffer || packetIndex < 0)
+ {
+ // not allowed
+ assert(!"SessionInfo::UpdatePacketSize Error: invalid packetIndex");
+ return;
+ }
+ _packetSizeBytes[packetIndex] = length;
+}
+
+WebRtc_Word64
+VCMSessionInfo::InsertPacket(const VCMPacket& packet,
+ WebRtc_UWord8* ptrStartOfLayer)
+{
+ // not allowed
+ assert(!packet.insertStartCode || !packet.bits);
+ // Check if this is first packet (only valid for some codecs)
+ if (packet.isFirstPacket)
+ {
+ // the first packet in the frame always signals the frametype
+ _frameType = packet.frameType;
+ }
+ else if (_frameType == kFrameEmpty && packet.frameType != kFrameEmpty)
+ {
+ // Update the frame type with the first media packet
+ _frameType = packet.frameType;
+ }
+ if (packet.frameType == kFrameEmpty)
+ {
+ // update seq number as an empty packet
+ InformOfEmptyPacket(packet.seqNum);
+ return 0;
+ }
+
+ // Check sequence number and update highest and lowest sequence numbers
+ // received. Move data if this seq num is lower than previously lowest.
+
+ if (packet.seqNum > _highSeqNum)
+ {
+ // This packet's seq num is higher than previously highest seq num;
+ // normal case if we have a wrap, only update with wrapped values
+ if (!(_highSeqNum < 0x00ff && packet.seqNum > 0xff00))
+ {
+ _highSeqNum = packet.seqNum;
+ }
+ } else if (_highSeqNum > 0xff00 && packet.seqNum < 0x00ff)
+ {
+ // wrap
+ _highSeqNum = packet.seqNum;
+ }
+ int packetIndex = packet.seqNum - (WebRtc_UWord16)_lowSeqNum;
+ if (_lowSeqNum < 0x00ff && packet.seqNum > 0xff00)
+ {
+ // negative wrap
+ packetIndex = packet.seqNum - 0x10000 - _lowSeqNum;
+ }
+ if (packetIndex < 0)
+ {
+ if (_lowSeqNum > 0xff00 && packet.seqNum < 0x00ff)
+ {
+ // we have a false detect due to the wrap
+ packetIndex = (0xffff - (WebRtc_UWord16)_lowSeqNum) + packet.seqNum
+ + (WebRtc_UWord16)1;
+ } else
+ {
+ // This packet's seq num is lower than previously lowest seq num,
+ // but no wrap We need to move the data in all arrays indexed by
+ // packetIndex and insert the new packet's info
+ // How many packets should we leave room for (positions to shift)?
+ // Example - this seq num is 3 lower than previously lowest seq num
+ // Before: |--prev packet with lowest seq num--|--|...|
+ // After: |--new lowest seq num--|--|--|--prev packet with
+ // lowest seq num--|--|...|
+
+ WebRtc_UWord16 positionsToShift = (WebRtc_UWord16)_lowSeqNum -
+ packet.seqNum;
+ WebRtc_UWord16 numOfPacketsToMove = _highestPacketIndex + 1;
+
+ // sanity, do we have room for the shift?
+ if ((positionsToShift + numOfPacketsToMove) >
+ kMaxPacketsInJitterBuffer)
+ {
+ return -1;
+ }
+
+ // Shift _ORwithPrevByte array
+ memmove(&_ORwithPrevByte[positionsToShift],
+ &_ORwithPrevByte[0], numOfPacketsToMove*sizeof(bool));
+ memset(&_ORwithPrevByte[0], false, positionsToShift*sizeof(bool));
+
+ // Shift _packetSizeBytes array
+ memmove(&_packetSizeBytes[positionsToShift],
+ &_packetSizeBytes[0],
+ numOfPacketsToMove * sizeof(WebRtc_UWord32));
+ memset(&_packetSizeBytes[0], 0,
+ positionsToShift * sizeof(WebRtc_UWord32));
+
+ // Shift _naluCompleteness
+ memmove(&_naluCompleteness[positionsToShift],
+ &_naluCompleteness[0],
+ numOfPacketsToMove * sizeof(WebRtc_UWord8));
+ memset(&_naluCompleteness[0], kNaluUnset,
+ positionsToShift * sizeof(WebRtc_UWord8));
+
+ _highestPacketIndex += positionsToShift;
+ _lowSeqNum = packet.seqNum;
+ packetIndex = 0; // (seqNum - _lowSeqNum) = 0
+ }
+ } // if (_lowSeqNum > seqNum)
+
+ // sanity
+ if (packetIndex >= kMaxPacketsInJitterBuffer )
+ {
+ return -1;
+ }
+ if (packetIndex < 0 )
+ {
+ return -1;
+ }
+
+ // Check for duplicate packets
+ if (_packetSizeBytes[packetIndex] != 0)
+ {
+ // We have already received a packet with this seq number, ignore it.
+ return -2;
+ }
+
+ // update highest packet index
+ _highestPacketIndex = packetIndex > _highestPacketIndex ?
+ packetIndex :_highestPacketIndex;
+
+ return InsertBuffer(ptrStartOfLayer, packetIndex, packet);
+}
+
+
+WebRtc_Word32
+VCMSessionInfo::InformOfEmptyPacket(const WebRtc_UWord16 seqNum)
+{
+ // Empty packets may be FEC or filler packets. They are sequential and
+ // follow the data packets, therefore, we should only keep track of the high
+ // and low sequence numbers and may assume that the packets in between are
+ // empty packets belonging to the same frame (timestamp).
+
+ if (_emptySeqNumLow == -1 && _emptySeqNumHigh == -1)
+ {
+ _emptySeqNumLow = seqNum;
+ _emptySeqNumHigh = seqNum;
+ }
+ else
+ {
+ if (seqNum > _emptySeqNumHigh)
+ {
+ // This packet's seq num is higher than previously highest seq num;
+ // normal case if we have a wrap, only update with wrapped values
+ if (!(_emptySeqNumHigh < 0x00ff && seqNum > 0xff00))
+ {
+ _emptySeqNumHigh = seqNum;
+ }
+ }
+ else if (_emptySeqNumHigh > 0xff00 && seqNum < 0x00ff)
+ {
+ // wrap
+ _emptySeqNumHigh = seqNum;
+ }
+ if (_emptySeqNumLow < 0x00ff && seqNum > 0xff00)
+ {
+ // negative wrap
+ if (seqNum - 0x10000 - _emptySeqNumLow < 0)
+ {
+ _emptySeqNumLow = seqNum;
+ }
+ }
+ }
+ return 0;
+}
+
+WebRtc_UWord32
+VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
+ VideoCodecType codec)
+{
+ WebRtc_UWord32 currentPacketOffset = 0;
+ WebRtc_UWord32 length = GetSessionLength();
+ WebRtc_UWord32 idSum = 0;
+ WebRtc_UWord32 realDataBytes = 0;
+ if (length == 0)
+ {
+ return length;
+ }
+ bool previousLost = false;
+ for (int i = 0; i <= _highestPacketIndex; i++)
+ {
+ if (_ORwithPrevByte[i])
+ {
+ if (currentPacketOffset > 0)
+ {
+ WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer +
+ currentPacketOffset;
+
+ if (_packetSizeBytes[i-1] == 0 || previousLost)
+ {
+ // It is be better to throw away this packet if we are
+ // missing the previous packet.
+ memset(ptrFirstByte, 0, _packetSizeBytes[i]);
+ previousLost = true;
+ }
+ else if (_packetSizeBytes[i] > 0) // Ignore if empty packet
+ {
+ // Glue with previous byte
+ // Move everything from [this packet start + 1,
+ // end of buffer] one byte to the left
+ WebRtc_UWord8* ptrPrevByte = ptrFirstByte - 1;
+ *ptrPrevByte = (*ptrPrevByte) | (*ptrFirstByte);
+ WebRtc_UWord32 lengthToEnd = length -
+ (currentPacketOffset + 1);
+ memmove((void*)ptrFirstByte, (void*)(ptrFirstByte + 1),
+ lengthToEnd);
+ _packetSizeBytes[i]--;
+ length--;
+ previousLost = false;
+ realDataBytes += _packetSizeBytes[i];
+ }
+ }
+ else
+ {
+ memset(ptrStartOfLayer, 0, _packetSizeBytes[i]);
+ previousLost = true;
+ }
+ }
+ else if (_packetSizeBytes[i] == 0 && codec == kVideoCodecH263)
+ {
+ WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer + currentPacketOffset;
+ memmove(ptrFirstByte + 10, ptrFirstByte,
+ length - currentPacketOffset);
+ memset(ptrFirstByte, 0, 10);
+ _packetSizeBytes[i] = 10;
+ length += _packetSizeBytes[i];
+ previousLost = true;
+ }
+ else
+ {
+ realDataBytes += _packetSizeBytes[i];
+ previousLost = false;
+ }
+ currentPacketOffset += _packetSizeBytes[i];
+ }
+ if (realDataBytes == 0)
+ {
+ // Drop the frame since all it contains are zeros
+ length = 0;
+ memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes));
+ }
+ return length;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/session_info.h b/src/modules/video_coding/main/source/session_info.h
new file mode 100644
index 0000000..ff71def
--- /dev/null
+++ b/src/modules/video_coding/main/source/session_info.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_
+#define WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_
+
+#include "typedefs.h"
+#include "module_common_types.h"
+#include "packet.h"
+
+namespace webrtc
+{
+
+class VCMSessionInfo
+{
+public:
+ VCMSessionInfo();
+ virtual ~VCMSessionInfo();
+
+ VCMSessionInfo(const VCMSessionInfo& rhs);
+
+ WebRtc_Word32 ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 numberOfSeqNum);
+ // Hybrid version: Zero out seq num for NACK list
+ // apply a score based on the packet location and the external rttScore
+ WebRtc_Word32 ZeroOutSeqNumHybrid(WebRtc_Word32* list,
+ WebRtc_Word32 numberOfSeqNum,
+ float rttScore);
+ virtual void Reset();
+
+ WebRtc_Word64 InsertPacket(const VCMPacket& packet, WebRtc_UWord8* ptrStartOfLayer);
+ WebRtc_Word32 InformOfEmptyPacket(const WebRtc_UWord16 seqNum);
+
+ virtual bool IsSessionComplete();
+ WebRtc_UWord32 MakeSessionDecodable(WebRtc_UWord8* ptrStartOfLayer);
+
+ WebRtc_UWord32 GetSessionLength();
+ bool HaveLastPacket();
+ void ForceSetHaveLastPacket();
+ bool IsRetransmitted();
+ webrtc::FrameType FrameType() const { return _frameType; }
+
+ virtual WebRtc_Word32 GetHighestPacketIndex();
+ virtual void UpdatePacketSize(WebRtc_Word32 packetIndex, WebRtc_UWord32 length);
+
+ void SetStartSeqNumber(WebRtc_UWord16 seqNumber);
+
+ bool HaveStartSeqNumber();
+
+ WebRtc_Word32 GetLowSeqNum() const;
+ // returns highest seqNum, media or empty
+ WebRtc_Word32 GetHighSeqNum() const;
+
+ WebRtc_UWord32 PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer, VideoCodecType codec);
+
+ void SetPreviousFrameLoss() { _previousFrameLoss = true; }
+ bool PreviousFrameLoss() const { return _previousFrameLoss; }
+
+protected:
+ WebRtc_UWord32 InsertBuffer(WebRtc_UWord8* ptrStartOfLayer,
+ WebRtc_Word32 packetIndex,
+ const VCMPacket& packet);
+ void FindNaluBorder(WebRtc_Word32 packetIndex,
+ WebRtc_Word32& startIndex,
+ WebRtc_Word32& endIndex);
+ WebRtc_UWord32 DeletePackets(WebRtc_UWord8* ptrStartOfLayer,
+ WebRtc_Word32 startIndex,
+ WebRtc_Word32 endIndex);
+ void UpdateCompleteSession();
+
+ bool _haveFirstPacket; // If we have inserted the first packet into this frame
+ bool _markerBit; // If we have inserted a packet with markerbit into this frame
+ bool _sessionNACK; // If this session has been NACKed by JB
+ bool _completeSession;
+ webrtc::FrameType _frameType;
+ bool _previousFrameLoss;
+
+ WebRtc_Word32 _lowSeqNum; // Lowest packet sequence number in a session
+ WebRtc_Word32 _highSeqNum; // Highest packet sequence number in a session
+
+ // Highest packet index in this frame
+ WebRtc_UWord16 _highestPacketIndex;
+ // Length of packet (used for reordering)
+ WebRtc_UWord32 _packetSizeBytes[kMaxPacketsInJitterBuffer];
+ // Completeness of packets. Used for deciding if the frame is decodable.
+ WebRtc_UWord8 _naluCompleteness[kMaxPacketsInJitterBuffer];
+ WebRtc_Word32 _emptySeqNumLow;
+ WebRtc_Word32 _emptySeqNumHigh;
+ // Store the sequence number that marks the last media packet
+ WebRtc_Word32 _markerSeqNum;
+ bool _ORwithPrevByte[kMaxPacketsInJitterBuffer];
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_
diff --git a/src/modules/video_coding/main/source/tick_time.h b/src/modules/video_coding/main/source/tick_time.h
new file mode 100644
index 0000000..47ac9f4
--- /dev/null
+++ b/src/modules/video_coding/main/source/tick_time.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_
+
+#include "tick_util.h"
+
+#include <assert.h>
+
+namespace webrtc
+{
+
+//#define TICK_TIME_DEBUG
+
+class VCMTickTime : public TickTime
+{
+#ifdef TICK_TIME_DEBUG
+public:
+ /*
+ * Get current time
+ */
+ static TickTime Now() { assert(false); };
+
+ /*
+ * Get time in milli seconds
+ */
+ static WebRtc_Word64 MillisecondTimestamp() { return _timeNowDebug; };
+
+ /*
+ * Get time in micro seconds
+ */
+ static WebRtc_Word64 MicrosecondTimestamp() { return _timeNowDebug * 1000LL; };
+
+ static void IncrementDebugClock() { _timeNowDebug++; };
+
+private:
+ static WebRtc_Word64 _timeNowDebug;
+
+#else
+public:
+ static void IncrementDebugClock() { assert(false); };
+#endif
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_
diff --git a/src/modules/video_coding/main/source/timestamp_extrapolator.cc b/src/modules/video_coding/main/source/timestamp_extrapolator.cc
new file mode 100644
index 0000000..5f589e1
--- /dev/null
+++ b/src/modules/video_coding/main/source/timestamp_extrapolator.cc
@@ -0,0 +1,259 @@
+/*
+ * 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 "internal_defines.h"
+#include "timestamp_extrapolator.h"
+#include "tick_time.h"
+#include "trace.h"
+
+namespace webrtc {
+
+VCMTimestampExtrapolator::VCMTimestampExtrapolator(WebRtc_Word32 vcmId, WebRtc_Word32 id)
+:
+_rwLock(*RWLockWrapper::CreateRWLock()),
+_vcmId(vcmId),
+_id(id),
+_startMs(0),
+_firstTimestamp(0),
+_wrapArounds(0),
+_prevTs90khz(0),
+_lambda(1),
+_firstAfterReset(true),
+_packetCount(0),
+_startUpFilterDelayInPackets(2),
+_detectorAccumulatorPos(0),
+_detectorAccumulatorNeg(0),
+_alarmThreshold(60e3),
+_accDrift(6600), // in timestamp ticks, i.e. 15 ms
+_accMaxError(7000),
+_P11(1e10)
+{
+ Reset(VCMTickTime::MillisecondTimestamp());
+}
+
+VCMTimestampExtrapolator::~VCMTimestampExtrapolator()
+{
+ delete &_rwLock;
+}
+
+void
+VCMTimestampExtrapolator::Reset(const WebRtc_Word64 nowMs /* = -1 */)
+{
+ WriteLockScoped wl(_rwLock);
+ if (nowMs > -1)
+ {
+ _startMs = nowMs;
+ }
+ else
+ {
+ _startMs = VCMTickTime::MillisecondTimestamp();
+ }
+ _prevMs = _startMs;
+ _firstTimestamp = 0;
+ _w[0] = 90.0;
+ _w[1] = 0;
+ _P[0][0] = 1;
+ _P[1][1] = _P11;
+ _P[0][1] = _P[1][0] = 0;
+ _firstAfterReset = true;
+ _prevTs90khz = 0;
+ _wrapArounds = 0;
+ _packetCount = 0;
+ _detectorAccumulatorPos = 0;
+ _detectorAccumulatorNeg = 0;
+}
+
+void
+VCMTimestampExtrapolator::Update(WebRtc_Word64 tMs, WebRtc_UWord32 ts90khz, bool trace)
+{
+
+ _rwLock.AcquireLockExclusive();
+ if (tMs - _prevMs > 10e3)
+ {
+ // Ten seconds without a complete frame.
+ // Reset the extrapolator
+ _rwLock.ReleaseLockExclusive();
+ Reset();
+ _rwLock.AcquireLockExclusive();
+ }
+ else
+ {
+ _prevMs = tMs;
+ }
+
+ // Remove offset to prevent badly scaled matrices
+ tMs -= _startMs;
+
+ WebRtc_Word32 prevWrapArounds = _wrapArounds;
+ CheckForWrapArounds(ts90khz);
+ WebRtc_Word32 wrapAroundsSincePrev = _wrapArounds - prevWrapArounds;
+
+ if (wrapAroundsSincePrev == 0 && ts90khz < _prevTs90khz)
+ {
+ _rwLock.ReleaseLockExclusive();
+ return;
+ }
+
+ if (_firstAfterReset)
+ {
+ // Make an initial guess of the offset,
+ // should be almost correct since tMs - _startMs
+ // should about zero at this time.
+ _w[1] = -_w[0] * tMs;
+ _firstTimestamp = ts90khz;
+ _firstAfterReset = false;
+ }
+
+ // Compensate for wraparounds by changing the line offset
+ _w[1] = _w[1] - wrapAroundsSincePrev * ((static_cast<WebRtc_Word64>(1)<<32) - 1);
+
+ double residual = (static_cast<double>(ts90khz) - _firstTimestamp) - static_cast<double>(tMs) * _w[0] - _w[1];
+ if (DelayChangeDetection(residual, trace) &&
+ _packetCount >= _startUpFilterDelayInPackets)
+ {
+ // A sudden change of average network delay has been detected.
+ // Force the filter to adjust its offset parameter by changing
+ // the offset uncertainty. Don't do this during startup.
+ _P[1][1] = _P11;
+ }
+ //T = [t(k) 1]';
+ //that = T'*w;
+ //K = P*T/(lambda + T'*P*T);
+ double K[2];
+ K[0] = _P[0][0] * tMs + _P[0][1];
+ K[1] = _P[1][0] * tMs + _P[1][1];
+ double TPT = _lambda + tMs * K[0] + K[1];
+ K[0] /= TPT;
+ K[1] /= TPT;
+ //w = w + K*(ts(k) - that);
+ _w[0] = _w[0] + K[0] * residual;
+ _w[1] = _w[1] + K[1] * residual;
+ //P = 1/lambda*(P - K*T'*P);
+ double p00 = 1 / _lambda * (_P[0][0] - (K[0] * tMs * _P[0][0] + K[0] * _P[1][0]));
+ double p01 = 1 / _lambda * (_P[0][1] - (K[0] * tMs * _P[0][1] + K[0] * _P[1][1]));
+ _P[1][0] = 1 / _lambda * (_P[1][0] - (K[1] * tMs * _P[0][0] + K[1] * _P[1][0]));
+ _P[1][1] = 1 / _lambda * (_P[1][1] - (K[1] * tMs * _P[0][1] + K[1] * _P[1][1]));
+ _P[0][0] = p00;
+ _P[0][1] = p01;
+ if (_packetCount < _startUpFilterDelayInPackets)
+ {
+ _packetCount++;
+ }
+ if (trace)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "w[0]=%f w[1]=%f ts=%u tMs=%u", _w[0], _w[1], ts90khz, tMs);
+ }
+ _rwLock.ReleaseLockExclusive();
+}
+
+WebRtc_UWord32
+VCMTimestampExtrapolator::ExtrapolateTimestamp(WebRtc_Word64 tMs) const
+{
+ ReadLockScoped rl(_rwLock);
+ WebRtc_UWord32 timestamp = 0;
+ if (_packetCount == 0)
+ {
+ timestamp = 0;
+ }
+ else if (_packetCount < _startUpFilterDelayInPackets)
+ {
+ timestamp = static_cast<WebRtc_UWord32>(90.0 * (tMs - _prevMs) + _prevTs90khz + 0.5);
+ }
+ else
+ {
+ timestamp = static_cast<WebRtc_UWord32>(_w[0] * (tMs - _startMs) + _w[1] + _firstTimestamp + 0.5);
+ }
+ return timestamp;
+}
+
+WebRtc_Word64
+VCMTimestampExtrapolator::ExtrapolateLocalTime(WebRtc_UWord32 timestamp90khz) const
+{
+ ReadLockScoped rl(_rwLock);
+ WebRtc_Word64 localTimeMs = 0;
+ if (_packetCount == 0)
+ {
+ localTimeMs = -1;
+ }
+ else if (_packetCount < _startUpFilterDelayInPackets)
+ {
+ localTimeMs = _prevMs + static_cast<WebRtc_Word64>(static_cast<double>(timestamp90khz - _prevTs90khz) / 90.0 + 0.5);
+ }
+ else
+ {
+ if (_w[0] < 1e-3)
+ {
+ localTimeMs = _startMs;
+ }
+ else
+ {
+ double timestampDiff = static_cast<double>(timestamp90khz) - static_cast<double>(_firstTimestamp);
+ localTimeMs = static_cast<WebRtc_Word64>(static_cast<double>(_startMs) + (timestampDiff - _w[1]) / _w[0] + 0.5);
+ }
+ }
+ return localTimeMs;
+}
+
+// Investigates if the timestamp clock has overflowed since the last timestamp and
+// keeps track of the number of wrap arounds since reset.
+void
+VCMTimestampExtrapolator::CheckForWrapArounds(WebRtc_UWord32 ts90khz)
+{
+ if (_prevTs90khz == 0)
+ {
+ _prevTs90khz = ts90khz;
+ return;
+ }
+ if (ts90khz < _prevTs90khz)
+ {
+ // This difference will probably be less than -2^31 if we have had a wrap around
+ // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is casted to a Word32,
+ // it should be positive.
+ if (static_cast<WebRtc_Word32>(ts90khz - _prevTs90khz) > 0)
+ {
+ // Forward wrap around
+ _wrapArounds++;
+ }
+ }
+ // This difference will probably be less than -2^31 if we have had a backward wrap around.
+ // Since it is casted to a Word32, it should be positive.
+ else if (static_cast<WebRtc_Word32>(_prevTs90khz - ts90khz) > 0)
+ {
+ // Backward wrap around
+ _wrapArounds--;
+ }
+ _prevTs90khz = ts90khz;
+}
+
+bool
+VCMTimestampExtrapolator::DelayChangeDetection(double error, bool trace)
+{
+ // CUSUM detection of sudden delay changes
+ error = (error > 0) ? VCM_MIN(error, _accMaxError) : VCM_MAX(error, -_accMaxError);
+ _detectorAccumulatorPos = VCM_MAX(_detectorAccumulatorPos + error - _accDrift, (double)0);
+ _detectorAccumulatorNeg = VCM_MIN(_detectorAccumulatorNeg + error + _accDrift, (double)0);
+ if (_detectorAccumulatorPos > _alarmThreshold || _detectorAccumulatorNeg < -_alarmThreshold)
+ {
+ // Alarm
+ if (trace)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=1", _detectorAccumulatorPos, _detectorAccumulatorNeg);
+ }
+ _detectorAccumulatorPos = _detectorAccumulatorNeg = 0;
+ return true;
+ }
+ if (trace)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=0", _detectorAccumulatorPos, _detectorAccumulatorNeg);
+ }
+ return false;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/timestamp_extrapolator.h b/src/modules/video_coding/main/source/timestamp_extrapolator.h
new file mode 100644
index 0000000..a186504
--- /dev/null
+++ b/src/modules/video_coding/main/source/timestamp_extrapolator.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_
+
+#include "typedefs.h"
+#include "rw_lock_wrapper.h"
+
+namespace webrtc
+{
+
+class VCMTimestampExtrapolator
+{
+public:
+ VCMTimestampExtrapolator(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0);
+ ~VCMTimestampExtrapolator();
+ void Update(WebRtc_Word64 tMs, WebRtc_UWord32 ts90khz, bool trace = true);
+ WebRtc_UWord32 ExtrapolateTimestamp(WebRtc_Word64 tMs) const;
+ WebRtc_Word64 ExtrapolateLocalTime(WebRtc_UWord32 timestamp90khz) const;
+ void Reset(WebRtc_Word64 nowMs = -1);
+
+private:
+ void CheckForWrapArounds(WebRtc_UWord32 ts90khz);
+ bool DelayChangeDetection(double error, bool trace = true);
+ RWLockWrapper& _rwLock;
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _id;
+ bool _trace;
+ double _w[2];
+ double _P[2][2];
+ WebRtc_Word64 _startMs;
+ WebRtc_Word64 _prevMs;
+ WebRtc_UWord32 _firstTimestamp;
+ WebRtc_Word32 _wrapArounds;
+ WebRtc_UWord32 _prevTs90khz;
+ const double _lambda;
+ bool _firstAfterReset;
+ WebRtc_UWord32 _packetCount;
+ const WebRtc_UWord32 _startUpFilterDelayInPackets;
+
+ double _detectorAccumulatorPos;
+ double _detectorAccumulatorNeg;
+ const double _alarmThreshold;
+ const double _accDrift;
+ const double _accMaxError;
+ const double _P11;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_
diff --git a/src/modules/video_coding/main/source/timestamp_map.cc b/src/modules/video_coding/main/source/timestamp_map.cc
new file mode 100644
index 0000000..f19819b
--- /dev/null
+++ b/src/modules/video_coding/main/source/timestamp_map.cc
@@ -0,0 +1,99 @@
+/*
+ * 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 "timestamp_map.h"
+#include <stdlib.h>
+#include <assert.h>
+
+namespace webrtc {
+
+// Constructor. Optional parameter specifies maximum number of
+// coexisting timers.
+VCMTimestampMap::VCMTimestampMap(WebRtc_Word32 length):
+ _nextAddIx(0),
+ _nextPopIx(0)
+{
+ if (length <= 0)
+ {
+ // default
+ length = 10;
+ }
+
+ _map = new VCMTimestampDataTuple[length];
+ _length = length;
+}
+
+// Destructor.
+VCMTimestampMap::~VCMTimestampMap()
+{
+ delete [] _map;
+}
+
+// Empty the list of timers.
+void
+VCMTimestampMap::Reset()
+{
+ _nextAddIx = 0;
+ _nextPopIx = 0;
+}
+
+WebRtc_Word32
+VCMTimestampMap::Add(WebRtc_UWord32 timestamp, void* data)
+{
+ _map[_nextAddIx].timestamp = timestamp;
+ _map[_nextAddIx].data = data;
+ _nextAddIx = (_nextAddIx + 1) % _length;
+
+ if (_nextAddIx == _nextPopIx)
+ {
+ // Circular list full; forget oldest entry
+ _nextPopIx = (_nextPopIx + 1) % _length;
+ return -1;
+ }
+ return 0;
+}
+
+void*
+VCMTimestampMap::Pop(WebRtc_UWord32 timestamp)
+{
+ while (!IsEmpty())
+ {
+ if (_map[_nextPopIx].timestamp == timestamp)
+ {
+ // found start time for this timestamp
+ void* data = _map[_nextPopIx].data;
+ _map[_nextPopIx].data = NULL;
+ _nextPopIx = (_nextPopIx + 1) % _length;
+ return data;
+ }
+ else if (_map[_nextPopIx].timestamp > timestamp)
+ {
+ // the timestamp we are looking for is not in the list
+ assert(_nextPopIx < _length && _nextPopIx >= 0);
+ return NULL;
+ }
+
+ // not in this position, check next (and forget this position)
+ _nextPopIx = (_nextPopIx + 1) % _length;
+ }
+
+ // could not find matching timestamp in list
+ assert(_nextPopIx < _length && _nextPopIx >= 0);
+ return NULL;
+}
+
+// Check if no timers are currently running
+bool
+VCMTimestampMap::IsEmpty() const
+{
+ return (_nextAddIx == _nextPopIx);
+}
+
+}
diff --git a/src/modules/video_coding/main/source/timestamp_map.h b/src/modules/video_coding/main/source/timestamp_map.h
new file mode 100644
index 0000000..fd532bc
--- /dev/null
+++ b/src/modules/video_coding/main/source/timestamp_map.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_
+
+#include "typedefs.h"
+
+namespace webrtc
+{
+
+struct VCMTimestampDataTuple
+{
+ WebRtc_UWord32 timestamp;
+ void* data;
+};
+
+class VCMTimestampMap
+{
+public:
+ // Constructor. Optional parameter specifies maximum number of
+ // timestamps in map.
+ VCMTimestampMap(const WebRtc_Word32 length = 10);
+
+ // Destructor.
+ ~VCMTimestampMap();
+
+ // Empty the map
+ void Reset();
+
+ WebRtc_Word32 Add(WebRtc_UWord32 timestamp, void* data);
+ void* Pop(WebRtc_UWord32 timestamp);
+
+private:
+ bool IsEmpty() const;
+
+ VCMTimestampDataTuple* _map;
+ WebRtc_Word32 _nextAddIx;
+ WebRtc_Word32 _nextPopIx;
+ WebRtc_Word32 _length;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_
diff --git a/src/modules/video_coding/main/source/timing.cc b/src/modules/video_coding/main/source/timing.cc
new file mode 100644
index 0000000..67214ec
--- /dev/null
+++ b/src/modules/video_coding/main/source/timing.cc
@@ -0,0 +1,333 @@
+/*
+ * 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 "trace.h"
+#include "internal_defines.h"
+#include "jitter_buffer_common.h"
+#include "timing.h"
+#include "timestamp_extrapolator.h"
+
+namespace webrtc {
+
+VCMTiming::VCMTiming(WebRtc_Word32 vcmId, WebRtc_Word32 timingId, VCMTiming* masterTiming)
+:
+_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_vcmId(vcmId),
+_timingId(timingId),
+_master(false),
+_tsExtrapolator(),
+_codecTimer(),
+_renderDelayMs(kDefaultRenderDelayMs),
+_minTotalDelayMs(0),
+_requiredDelayMs(0),
+_currentDelayMs(0),
+_prevFrameTimestamp(0)
+{
+ if (masterTiming == NULL)
+ {
+ _master = true;
+ _tsExtrapolator = new VCMTimestampExtrapolator(vcmId, timingId);
+ }
+ else
+ {
+ _tsExtrapolator = masterTiming->_tsExtrapolator;
+ }
+}
+
+VCMTiming::~VCMTiming()
+{
+ if (_master)
+ {
+ delete _tsExtrapolator;
+ }
+ delete &_critSect;
+}
+
+void
+VCMTiming::Reset(WebRtc_Word64 nowMs /* = -1 */)
+{
+ CriticalSectionScoped cs(_critSect);
+ if (nowMs > -1)
+ {
+ _tsExtrapolator->Reset(nowMs);
+ }
+ else
+ {
+ _tsExtrapolator->Reset();
+ }
+ _codecTimer.Reset();
+ _renderDelayMs = kDefaultRenderDelayMs;
+ _minTotalDelayMs = 0;
+ _requiredDelayMs = 0;
+ _currentDelayMs = 0;
+ _prevFrameTimestamp = 0;
+}
+
+void VCMTiming::ResetDecodeTime()
+{
+ _codecTimer.Reset();
+}
+
+void
+VCMTiming::SetRenderDelay(WebRtc_UWord32 renderDelayMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ _renderDelayMs = renderDelayMs;
+}
+
+void
+VCMTiming::SetMinimumTotalDelay(WebRtc_UWord32 minTotalDelayMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ _minTotalDelayMs = minTotalDelayMs;
+}
+
+void
+VCMTiming::SetRequiredDelay(WebRtc_UWord32 requiredDelayMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ if (requiredDelayMs != _requiredDelayMs)
+ {
+ if (_master)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Desired jitter buffer level: %u ms", requiredDelayMs);
+ }
+ _requiredDelayMs = requiredDelayMs;
+ }
+}
+
+void VCMTiming::UpdateCurrentDelay(WebRtc_UWord32 frameTimestamp)
+{
+ CriticalSectionScoped cs(_critSect);
+ WebRtc_UWord32 targetDelayMs = TargetDelayInternal();
+
+ // Make sure we try to sync with audio
+ if (targetDelayMs < _minTotalDelayMs)
+ {
+ targetDelayMs = _minTotalDelayMs;
+ }
+
+ if (_currentDelayMs == 0)
+ {
+ // Not initialized, set current delay to target.
+ _currentDelayMs = targetDelayMs;
+ }
+ else if (targetDelayMs != _currentDelayMs)
+ {
+ WebRtc_Word64 delayDiffMs = static_cast<WebRtc_Word64>(targetDelayMs) -
+ _currentDelayMs;
+ // Never change the delay with more than 100 ms every second. If we're changing the
+ // delay in too large steps we will get noticable freezes. By limiting the change we
+ // can increase the delay in smaller steps, which will be experienced as the video is
+ // played in slow motion. When lowering the delay the video will be played at a faster
+ // pace.
+ WebRtc_Word64 maxChangeMs = 0;
+ if (frameTimestamp < 0x0000ffff && _prevFrameTimestamp > 0xffff0000)
+ {
+ // wrap
+ maxChangeMs = kDelayMaxChangeMsPerS * (frameTimestamp +
+ (static_cast<WebRtc_Word64>(1)<<32) - _prevFrameTimestamp) / 90000;
+ }
+ else
+ {
+ maxChangeMs = kDelayMaxChangeMsPerS *
+ (frameTimestamp - _prevFrameTimestamp) / 90000;
+ }
+ if (maxChangeMs <= 0)
+ {
+ // Any changes less than 1 ms are truncated and
+ // will be postponed. Negative change will be due
+ // to reordering and should be ignored.
+ return;
+ }
+ else if (delayDiffMs < -maxChangeMs)
+ {
+ delayDiffMs = -maxChangeMs;
+ }
+ else if (delayDiffMs > maxChangeMs)
+ {
+ delayDiffMs = maxChangeMs;
+ }
+ _currentDelayMs = _currentDelayMs + static_cast<WebRtc_Word32>(delayDiffMs);
+ }
+ _prevFrameTimestamp = frameTimestamp;
+}
+
+void VCMTiming::UpdateCurrentDelay(WebRtc_Word64 renderTimeMs,
+ WebRtc_Word64 actualDecodeTimeMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ WebRtc_UWord32 targetDelayMs = TargetDelayInternal();
+ // Make sure we try to sync with audio
+ if (targetDelayMs < _minTotalDelayMs)
+ {
+ targetDelayMs = _minTotalDelayMs;
+ }
+ WebRtc_Word64 delayedMs = actualDecodeTimeMs -
+ (renderTimeMs - MaxDecodeTimeMs() - _renderDelayMs);
+ if (delayedMs < 0)
+ {
+ return;
+ }
+ else if (_currentDelayMs + delayedMs <= targetDelayMs)
+ {
+ _currentDelayMs += static_cast<WebRtc_UWord32>(delayedMs);
+ }
+ else
+ {
+ _currentDelayMs = targetDelayMs;
+ }
+}
+
+WebRtc_Word32
+VCMTiming::StopDecodeTimer(WebRtc_UWord32 timeStamp,
+ WebRtc_Word64 startTimeMs,
+ WebRtc_Word64 nowMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ const WebRtc_Word32 maxDecTime = MaxDecodeTimeMs();
+ WebRtc_Word32 timeDiffMs = _codecTimer.StopTimer(startTimeMs, nowMs);
+ if (timeDiffMs < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Codec timer error: %d", timeDiffMs);
+ return timeDiffMs;
+ }
+
+ if (_master)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Frame decoded: timeStamp=%u decTime=%d maxDecTime=%u, at %u",
+ timeStamp, timeDiffMs, maxDecTime, MaskWord64ToUWord32(nowMs));
+ }
+ return 0;
+}
+
+void
+VCMTiming::IncomingTimestamp(WebRtc_UWord32 timeStamp, WebRtc_Word64 nowMs)
+{
+ CriticalSectionScoped cs(_critSect);
+ _tsExtrapolator->Update(nowMs, timeStamp, _master);
+}
+
+WebRtc_Word64
+VCMTiming::RenderTimeMs(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const
+{
+ CriticalSectionScoped cs(_critSect);
+ const WebRtc_Word64 renderTimeMs = RenderTimeMsInternal(frameTimestamp, nowMs);
+ if (renderTimeMs < 0)
+ {
+ return renderTimeMs;
+ }
+ if (_master)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Render frame %u at %u. Render delay %u, required delay %u,"
+ " max decode time %u, min total delay %u",
+ frameTimestamp, MaskWord64ToUWord32(renderTimeMs), _renderDelayMs,
+ _requiredDelayMs, MaxDecodeTimeMs(),_minTotalDelayMs);
+ }
+ return renderTimeMs;
+}
+
+WebRtc_Word64
+VCMTiming::RenderTimeMsInternal(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const
+{
+ WebRtc_Word64 estimatedCompleteTimeMs =
+ _tsExtrapolator->ExtrapolateLocalTime(frameTimestamp);
+ if (estimatedCompleteTimeMs - nowMs > kMaxVideoDelayMs)
+ {
+ if (_master)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Timestamp arrived 2 seconds early, reset statistics",
+ frameTimestamp, estimatedCompleteTimeMs);
+ }
+ return -1;
+ }
+ if (_master)
+ {
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "ExtrapolateLocalTime(%u)=%u ms",
+ frameTimestamp, MaskWord64ToUWord32(estimatedCompleteTimeMs));
+ }
+ if (estimatedCompleteTimeMs == -1)
+ {
+ estimatedCompleteTimeMs = nowMs;
+ }
+
+ return estimatedCompleteTimeMs + _currentDelayMs;
+}
+
+// Must be called from inside a critical section
+WebRtc_Word32
+VCMTiming::MaxDecodeTimeMs(FrameType frameType /*= kVideoFrameDelta*/) const
+{
+ const WebRtc_Word32 decodeTimeMs = _codecTimer.RequiredDecodeTimeMs(frameType);
+
+ if (decodeTimeMs < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId),
+ "Negative maximum decode time: %d", decodeTimeMs);
+ return -1;
+ }
+ return decodeTimeMs;
+}
+
+WebRtc_UWord32
+VCMTiming::MaxWaitingTime(WebRtc_Word64 renderTimeMs, WebRtc_Word64 nowMs) const
+{
+ CriticalSectionScoped cs(_critSect);
+
+ const WebRtc_Word64 maxWaitTimeMs = renderTimeMs - nowMs -
+ MaxDecodeTimeMs() - _renderDelayMs;
+
+ if (maxWaitTimeMs < 0)
+ {
+ return 0;
+ }
+ return static_cast<WebRtc_UWord32>(maxWaitTimeMs);
+}
+
+bool
+VCMTiming::EnoughTimeToDecode(WebRtc_UWord32 availableProcessingTimeMs) const
+{
+ CriticalSectionScoped cs(_critSect);
+ WebRtc_Word32 maxDecodeTimeMs = MaxDecodeTimeMs();
+ if (maxDecodeTimeMs < 0)
+ {
+ // Haven't decoded any frames yet, try decoding one to get an estimate
+ // of the decode time.
+ return true;
+ }
+ else if (maxDecodeTimeMs == 0)
+ {
+ // Decode time is less than 1, set to 1 for now since
+ // we don't have any better precision. Count ticks later?
+ maxDecodeTimeMs = 1;
+ }
+ return static_cast<WebRtc_Word32>(availableProcessingTimeMs) - maxDecodeTimeMs > 0;
+}
+
+WebRtc_UWord32
+VCMTiming::TargetVideoDelay() const
+{
+ CriticalSectionScoped cs(_critSect);
+ return TargetDelayInternal();
+}
+
+WebRtc_UWord32
+VCMTiming::TargetDelayInternal() const
+{
+ return _requiredDelayMs + MaxDecodeTimeMs() + _renderDelayMs;
+}
+
+}
diff --git a/src/modules/video_coding/main/source/timing.h b/src/modules/video_coding/main/source/timing.h
new file mode 100644
index 0000000..66d3ece
--- /dev/null
+++ b/src/modules/video_coding/main/source/timing.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMING_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TIMING_H_
+
+#include "typedefs.h"
+#include "critical_section_wrapper.h"
+#include "codec_timer.h"
+
+namespace webrtc
+{
+
+class VCMTimestampExtrapolator;
+
+class VCMTiming
+{
+public:
+ // The primary timing component should be passed
+ // if this is the dual timing component.
+ VCMTiming(WebRtc_Word32 vcmId = 0,
+ WebRtc_Word32 timingId = 0,
+ VCMTiming* masterTiming = NULL);
+ ~VCMTiming();
+
+ // Resets the timing to the initial state.
+ void Reset(WebRtc_Word64 nowMs = -1);
+ void ResetDecodeTime();
+
+ // The amount of time needed to render an image. Defaults to 10 ms.
+ void SetRenderDelay(WebRtc_UWord32 renderDelayMs);
+
+ // The minimum time the video must be delayed on the receiver to
+ // get the desired jitter buffer level.
+ void SetRequiredDelay(WebRtc_UWord32 requiredDelayMs);
+
+ // Minimum total delay required to sync video with audio.
+ void SetMinimumTotalDelay(WebRtc_UWord32 minTotalDelayMs);
+
+ // Increases or decreases the current delay to get closer to the target delay.
+ // Calculates how long it has been since the previous call to this function,
+ // and increases/decreases the delay in proportion to the time difference.
+ void UpdateCurrentDelay(WebRtc_UWord32 frameTimestamp);
+
+ // Increases or decreases the current delay to get closer to the target delay.
+ // Given the actual decode time in ms and the render time in ms for a frame, this
+ // function calculates how late the frame is and increases the delay accordingly.
+ void UpdateCurrentDelay(WebRtc_Word64 renderTimeMs, WebRtc_Word64 actualDecodeTimeMs);
+
+ // Stops the decoder timer, should be called when the decoder returns a frame
+ // or when the decoded frame callback is called.
+ WebRtc_Word32 StopDecodeTimer(WebRtc_UWord32 timeStamp,
+ WebRtc_Word64 startTimeMs,
+ WebRtc_Word64 nowMs);
+
+ // Used to report that a frame is passed to decoding. Updates the timestamp filter
+ // which is used to map between timestamps and receiver system time.
+ void IncomingTimestamp(WebRtc_UWord32 timeStamp, WebRtc_Word64 lastPacketTimeMs);
+
+ // Returns the receiver system time when the frame with timestamp frameTimestamp
+ // should be rendered, assuming that the system time currently is nowMs.
+ WebRtc_Word64 RenderTimeMs(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const;
+
+ // Returns the maximum time in ms that we can wait for a frame to become complete
+ // before we must pass it to the decoder.
+ WebRtc_UWord32 MaxWaitingTime(WebRtc_Word64 renderTimeMs, WebRtc_Word64 nowMs) const;
+
+ // Returns the current target delay which is required delay + decode time + render
+ // delay.
+ WebRtc_UWord32 TargetVideoDelay() const;
+
+ // Calculates whether or not there is enough time to decode a frame given a
+ // certain amount of processing time.
+ bool EnoughTimeToDecode(WebRtc_UWord32 availableProcessingTimeMs) const;
+
+ enum { kDefaultRenderDelayMs = 10 };
+ enum { kDelayMaxChangeMsPerS = 100 };
+
+protected:
+ WebRtc_Word32 MaxDecodeTimeMs(FrameType frameType = kVideoFrameDelta) const;
+ WebRtc_Word64 RenderTimeMsInternal(WebRtc_UWord32 frameTimestamp,
+ WebRtc_Word64 nowMs) const;
+ WebRtc_UWord32 TargetDelayInternal() const;
+
+private:
+ CriticalSectionWrapper& _critSect;
+ WebRtc_Word32 _vcmId;
+ WebRtc_Word32 _timingId;
+ bool _master;
+ VCMTimestampExtrapolator* _tsExtrapolator;
+ VCMCodecTimer _codecTimer;
+ WebRtc_UWord32 _renderDelayMs;
+ WebRtc_UWord32 _minTotalDelayMs;
+ WebRtc_UWord32 _requiredDelayMs;
+ WebRtc_UWord32 _currentDelayMs;
+ WebRtc_UWord32 _prevFrameTimestamp;
+ WebRtc_Word64 _startStoragePlaybackMs;
+ WebRtc_Word64 _firstStoredRenderTimeMs;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TIMING_H_
diff --git a/src/modules/video_coding/main/source/video_coding.gyp b/src/modules/video_coding/main/source/video_coding.gyp
new file mode 100644
index 0000000..f2431f4
--- /dev/null
+++ b/src/modules/video_coding/main/source/video_coding.gyp
@@ -0,0 +1,104 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ '../../../../common_settings.gypi', # Common settings
+ ],
+ 'targets': [
+ {
+ 'target_name': 'webrtc_video_coding',
+ 'type': '<(library)',
+ 'dependencies': [
+ '../../codecs/i420/main/source/i420.gyp:webrtc_i420',
+ '../../codecs/vp8/main/source/vp8.gyp:webrtc_vp8',
+ '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib',
+ '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
+ ],
+ 'include_dirs': [
+ '../interface',
+ '../../../interface',
+ '../../codecs/interface',
+ '../../../../common_video/interface',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../interface',
+ '../../codecs/interface',
+ ],
+ },
+ 'sources': [
+ # interfaces
+ '../interface/video_coding.h',
+ '../interface/video_coding_defines.h',
+
+ # headers
+ 'codec_database.h',
+ 'codec_timer.h',
+ 'content_metrics_processing.h',
+ 'encoded_frame.h',
+ 'er_tables_xor.h',
+ 'event.h',
+ 'exp_filter.h',
+ 'fec_tables_xor.h',
+ 'frame_buffer.h',
+ 'frame_dropper.h',
+ 'frame_list.h',
+ 'generic_decoder.h',
+ 'generic_encoder.h',
+ 'inter_frame_delay.h',
+ 'internal_defines.h',
+ 'jitter_buffer_common.h',
+ 'jitter_buffer.h',
+ 'jitter_estimator.h',
+ 'media_opt_util.h',
+ 'media_optimization.h',
+ 'nack_fec_tables.h',
+ 'packet.h',
+ 'qm_select_data.h',
+ 'qm_select.h',
+ 'receiver.h',
+ 'rtt_filter.h',
+ 'session_info.h',
+ 'tick_time.h',
+ 'timestamp_extrapolator.h',
+ 'timestamp_map.h',
+ 'timing.h',
+ 'video_coding_impl.h',
+
+ # sources
+ 'codec_database.cc',
+ 'codec_timer.cc',
+ 'content_metrics_processing.cc',
+ 'encoded_frame.cc',
+ 'exp_filter.cc',
+ 'frame_buffer.cc',
+ 'frame_dropper.cc',
+ 'frame_list.cc',
+ 'generic_decoder.cc',
+ 'generic_encoder.cc',
+ 'inter_frame_delay.cc',
+ 'jitter_buffer.cc',
+ 'jitter_estimator.cc',
+ 'media_opt_util.cc',
+ 'media_optimization.cc',
+ 'packet.cc',
+ 'qm_select.cc',
+ 'receiver.cc',
+ 'rtt_filter.cc',
+ 'session_info.cc',
+ 'timestamp_extrapolator.cc',
+ 'timestamp_map.cc',
+ 'timing.cc',
+ 'video_coding_impl.cc',
+ ], # source
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/modules/video_coding/main/source/video_coding_impl.cc b/src/modules/video_coding/main/source/video_coding_impl.cc
new file mode 100644
index 0000000..48d2947
--- /dev/null
+++ b/src/modules/video_coding/main/source/video_coding_impl.cc
@@ -0,0 +1,1346 @@
+/*
+ * 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 "video_coding_impl.h"
+#include "common_types.h"
+#include "encoded_frame.h"
+#include "jitter_buffer.h"
+#include "packet.h"
+#include "trace.h"
+#include "video_codec_interface.h"
+
+namespace webrtc
+{
+
+//#define DEBUG_DECODER_BIT_STREAM
+//#define DEBUG_ENCODER_INPUT
+
+WebRtc_UWord32
+VCMProcessTimer::Period() const
+{
+ return _periodMs;
+}
+
+WebRtc_UWord32
+VCMProcessTimer::TimeUntilProcess() const
+{
+ return static_cast<WebRtc_UWord32>(VCM_MAX(static_cast<WebRtc_Word64>(_periodMs) -
+ (VCMTickTime::MillisecondTimestamp() - _latestMs), 0));
+}
+
+void
+VCMProcessTimer::Processed()
+{
+ _latestMs = VCMTickTime::MillisecondTimestamp();
+}
+
+VideoCodingModuleImpl::VideoCodingModuleImpl(const WebRtc_Word32 id)
+:
+_id(id),
+_receiveCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_receiverInited(false),
+_timing(id, 1),
+_dualTiming(id, 2, &_timing),
+_receiver(_timing, id, 1),
+_dualReceiver(_dualTiming, id, 2, false),
+_decodedFrameCallback(_timing),
+_dualDecodedFrameCallback(_dualTiming),
+_frameTypeCallback(NULL),
+_frameStorageCallback(NULL),
+_receiveStatsCallback(NULL),
+_packetRequestCallback(NULL),
+_decoder(NULL),
+_dualDecoder(NULL),
+_bitStreamBeforeDecoder(NULL),
+_frameFromFile(),
+_keyRequestMode(kKeyOnError),
+_scheduleKeyRequest(false),
+
+_sendCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_encoder(),
+_encodedFrameCallback(),
+_nextFrameType(kVideoFrameDelta),
+_mediaOpt(id),
+_sendCodecType(kVideoCodecUnknown),
+_sendStatsCallback(NULL),
+_encoderInputFile(NULL),
+
+_codecDataBase(id),
+_receiveStatsTimer(1000),
+_sendStatsTimer(1000),
+_retransmissionTimer(10),
+_keyRequestTimer(500)
+{
+#ifdef DEBUG_DECODER_BIT_STREAM
+ _bitStreamBeforeDecoder = fopen("decoderBitStream.bit", "wb");
+#endif
+#ifdef DEBUG_ENCODER_INPUT
+ _encoderInputFile = fopen("encoderInput.yuv", "wb");
+#endif
+}
+
+VideoCodingModuleImpl::~VideoCodingModuleImpl()
+{
+ if (_dualDecoder != NULL)
+ {
+ _codecDataBase.ReleaseDecoder(_dualDecoder);
+ }
+ delete &_receiveCritSect;
+ delete &_sendCritSect;
+#ifdef DEBUG_DECODER_BIT_STREAM
+ fclose(_bitStreamBeforeDecoder);
+#endif
+#ifdef DEBUG_ENCODER_INPUT
+ fclose(_encoderInputFile);
+#endif
+}
+
+VideoCodingModule*
+VideoCodingModule::Create(const WebRtc_Word32 id)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(id),
+ "VideoCodingModule::Create()");
+ return new VideoCodingModuleImpl(id);
+}
+
+void
+VideoCodingModule::Destroy(VideoCodingModule* module)
+{
+ if (module != NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding,
+ static_cast<VideoCodingModuleImpl*>(module)->Id(),
+ "VideoCodingModule::Destroy()");
+ delete static_cast<VideoCodingModuleImpl*>(module);
+ }
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::Process()
+{
+ WebRtc_Word32 returnValue = VCM_OK;
+
+ // Receive-side statistics
+ if (_receiveStatsTimer.TimeUntilProcess() == 0)
+ {
+ _receiveStatsTimer.Processed();
+ if (_receiveStatsCallback != NULL)
+ {
+ WebRtc_UWord32 bitRate;
+ WebRtc_UWord32 frameRate;
+ const WebRtc_Word32 ret = _receiver.ReceiveStatistics(bitRate, frameRate);
+ if (ret == 0)
+ {
+ _receiveStatsCallback->ReceiveStatistics(bitRate, frameRate);
+ }
+ else if (returnValue == VCM_OK)
+ {
+ returnValue = ret;
+ }
+ }
+ }
+
+ // Send-side statistics
+ if (_sendStatsTimer.TimeUntilProcess() == 0)
+ {
+ _sendStatsTimer.Processed();
+ if (_sendStatsCallback != NULL)
+ {
+ WebRtc_UWord32 bitRate;
+ WebRtc_UWord32 frameRate;
+ {
+ CriticalSectionScoped cs(_sendCritSect);
+ bitRate = static_cast<WebRtc_UWord32>(_mediaOpt.SentBitRate() + 0.5f);
+ frameRate = static_cast<WebRtc_UWord32>(_mediaOpt.SentFrameRate() + 0.5f);
+ }
+ _sendStatsCallback->SendStatistics(bitRate, frameRate);
+ }
+ }
+
+ // Packet retransmission requests
+ if (_retransmissionTimer.TimeUntilProcess() == 0)
+ {
+ _retransmissionTimer.Processed();
+ if (_packetRequestCallback != NULL)
+ {
+ WebRtc_UWord16 nackList[kNackHistoryLength];
+ WebRtc_UWord16 length = kNackHistoryLength;
+ const WebRtc_Word32 ret = NackList(nackList, length);
+ if (ret != VCM_OK && returnValue == VCM_OK)
+ {
+ returnValue = ret;
+ }
+ if (length > 0)
+ {
+ _packetRequestCallback->ResendPackets(nackList, length);
+ }
+ }
+ }
+
+ // Key frame requests
+ if (_keyRequestTimer.TimeUntilProcess() == 0)
+ {
+ _keyRequestTimer.Processed();
+ if (_scheduleKeyRequest && _frameTypeCallback != NULL)
+ {
+ const WebRtc_Word32 ret = RequestKeyFrame();
+ if (ret != VCM_OK && returnValue == VCM_OK)
+ {
+ returnValue = ret;
+ }
+ }
+ }
+
+ return returnValue;
+}
+
+// Returns version of the module and its components
+WebRtc_Word32
+VideoCodingModuleImpl::Version(WebRtc_Word8* version,
+ WebRtc_UWord32& remainingBufferInBytes,
+ WebRtc_UWord32& position) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Version()");
+ if (version == NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Invalid buffer pointer in argument to Version()");
+ return VCM_PARAMETER_ERROR;
+ }
+ WebRtc_Word8 ourVersion[] = "VideoCodingModule 1.1.0\n";
+ WebRtc_UWord32 ourLength = (WebRtc_UWord32)strlen(ourVersion);
+ if (remainingBufferInBytes < ourLength)
+ {
+ return VCM_MEMORY;
+ }
+ memcpy(&version[position], ourVersion, ourLength);
+ remainingBufferInBytes -= ourLength;
+ position += ourLength;
+
+ // Safe to truncate here.
+ WebRtc_Word32 ret = _codecDataBase.Version(version, remainingBufferInBytes, position);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ // Ensure the strlen call is safe by terminating at the end of version.
+ version[position + remainingBufferInBytes - 1] = '\0';
+ ourLength = (WebRtc_UWord32)strlen(&version[position]);
+ remainingBufferInBytes -= (ourLength + 1); // include null termination.
+ position += (ourLength + 1);
+
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::Id() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Id()");
+ CriticalSectionScoped receiveCs(_receiveCritSect);
+ {
+ CriticalSectionScoped sendCs(_sendCritSect);
+ return _id;
+ }
+}
+
+// Change the unique identifier of this object
+WebRtc_Word32
+VideoCodingModuleImpl::ChangeUniqueId(const WebRtc_Word32 id)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ChangeUniqueId()");
+ CriticalSectionScoped receiveCs(_receiveCritSect);
+ {
+ CriticalSectionScoped sendCs(_sendCritSect);
+ _id = id;
+ return VCM_OK;
+ }
+}
+
+// Returns the number of milliseconds until the module wants a worker thread to call Process
+WebRtc_Word32
+VideoCodingModuleImpl::TimeUntilNextProcess()
+{
+ WebRtc_UWord32 timeUntilNextProcess = VCM_MIN(_receiveStatsTimer.TimeUntilProcess(),
+ _sendStatsTimer.TimeUntilProcess());
+ if ((_receiver.NackMode() != kNoNack) || (_dualReceiver.State() != kPassive))
+ {
+ // We need a Process call more often if we are relying on retransmissions
+ timeUntilNextProcess = VCM_MIN(timeUntilNextProcess,
+ _retransmissionTimer.TimeUntilProcess());
+ }
+ timeUntilNextProcess = VCM_MIN(timeUntilNextProcess,
+ _keyRequestTimer.TimeUntilProcess());
+
+ return timeUntilNextProcess;
+}
+
+// Get number of supported codecs
+WebRtc_UWord8
+VideoCodingModule::NumberOfCodecs()
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "NumberOfCodecs()");
+ return VCMCodecDataBase::NumberOfCodecs();
+}
+
+// Get supported codec with id
+WebRtc_Word32
+VideoCodingModule::Codec(WebRtc_UWord8 listId, VideoCodec* codec)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "Codec()");
+ if (codec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ return VCMCodecDataBase::Codec(listId, codec);
+}
+
+// Get supported codec with type
+WebRtc_Word32
+VideoCodingModule::Codec(VideoCodecType codecType, VideoCodec* codec)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "Codec()");
+ if (codec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ return VCMCodecDataBase::Codec(codecType, codec);
+}
+
+/*
+* Sender
+*/
+
+// Reset send side to initial state - all components
+WebRtc_Word32
+VideoCodingModuleImpl::InitializeSender()
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "InitializeSender()");
+ CriticalSectionScoped cs(_sendCritSect);
+ _codecDataBase.ResetSender();
+ _encoder = NULL;
+ _encodedFrameCallback.SetTransportCallback(NULL);
+ // setting default bitRate and frameRate to 0
+ _mediaOpt.SetEncodingData(kVideoCodecUnknown, 0, 0, 0, 0, 0);
+ _mediaOpt.Reset(); // Resetting frame dropper
+ return VCM_OK;
+}
+
+// Makes sure the encoder is in its initial state.
+WebRtc_Word32
+VideoCodingModuleImpl::ResetEncoder()
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ResetEncoder()");
+ CriticalSectionScoped cs(_sendCritSect);
+ if (_encoder != NULL)
+ {
+ return _encoder->Reset();
+ }
+ return VCM_OK;
+}
+
+// Register the send codec to be used.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterSendCodec(const VideoCodec* sendCodec,
+ WebRtc_UWord32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "RegisterSendCodec()");
+ CriticalSectionScoped cs(_sendCritSect);
+ if (sendCodec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ WebRtc_Word32 ret = _codecDataBase.RegisterSendCodec(sendCodec,
+ numberOfCores,
+ maxPayloadSize);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ _encoder = _codecDataBase.SetEncoder(sendCodec, &_encodedFrameCallback);
+ if (_encoder == NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to initialize encoder");
+ return VCM_CODEC_ERROR;
+ }
+ _sendCodecType = sendCodec->codecType;
+ _mediaOpt.SetEncodingData(_sendCodecType,
+ sendCodec->maxBitrate,
+ sendCodec->maxFramerate,
+ sendCodec->startBitrate,
+ sendCodec->width,
+ sendCodec->height);
+ _mediaOpt.SetMtu(maxPayloadSize);
+
+ return VCM_OK;
+}
+
+// Get current send codec
+WebRtc_Word32
+VideoCodingModuleImpl::SendCodec(VideoCodec* currentSendCodec) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec()");
+ CriticalSectionScoped cs(_sendCritSect);
+
+ if (currentSendCodec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ return _codecDataBase.SendCodec(currentSendCodec);
+}
+
+// Get the current send codec type
+VideoCodecType
+VideoCodingModuleImpl::SendCodec() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec()");
+ CriticalSectionScoped cs(_sendCritSect);
+
+ return _codecDataBase.SendCodec();
+}
+
+// Register an external decoder object.
+// This can not be used together with external decoder callbacks.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterExternalEncoder(VideoEncoder* externalEncoder,
+ WebRtc_UWord8 payloadType,
+ bool internalSource /*= false*/)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterExternalEncoder()");
+ CriticalSectionScoped cs(_sendCritSect);
+
+ if (externalEncoder == NULL)
+ {
+ bool wasSendCodec = false;
+ const WebRtc_Word32 ret = _codecDataBase.DeRegisterExternalEncoder(payloadType,
+ wasSendCodec);
+ if (wasSendCodec)
+ {
+ // Make sure the VCM doesn't use the de-registered codec
+ _encoder = NULL;
+ }
+ return ret;
+ }
+ return _codecDataBase.RegisterExternalEncoder(externalEncoder,
+ payloadType,
+ internalSource);
+}
+
+// Get codec config parameters
+WebRtc_Word32
+VideoCodingModuleImpl::CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "CodecConfigParameters()");
+ CriticalSectionScoped cs(_sendCritSect);
+ if (_encoder != NULL)
+ {
+ return _encoder->CodecConfigParameters(buffer, size);
+ }
+ return VCM_UNINITIALIZED;
+}
+
+// Get encode bitrate
+WebRtc_UWord32
+VideoCodingModuleImpl::Bitrate() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Bitrate()");
+ CriticalSectionScoped cs(_sendCritSect);
+ // return the bit rate which the encoder is set to
+ if (_encoder != NULL)
+ {
+ return _encoder->BitRate();
+ }
+ return VCM_UNINITIALIZED;
+}
+
+// Get encode frame rate
+WebRtc_UWord32
+VideoCodingModuleImpl::FrameRate() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "FrameRate()");
+ CriticalSectionScoped cs(_sendCritSect);
+ // input frame rate, not compensated
+ if (_encoder != NULL)
+ {
+ return _encoder->FrameRate();
+ }
+ return VCM_UNINITIALIZED;
+}
+
+// Set channel parameters
+WebRtc_Word32
+VideoCodingModuleImpl::SetChannelParameters(WebRtc_UWord32 availableBandWidth,
+ WebRtc_UWord8 lossRate,
+ WebRtc_UWord32 RTT)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetChannelParameters()");
+ WebRtc_Word32 ret = 0;
+ {
+ CriticalSectionScoped sendCs(_sendCritSect);
+ WebRtc_UWord32 targetRate = _mediaOpt.SetTargetRates(availableBandWidth,
+ lossRate,
+ RTT);
+ if (_encoder != NULL)
+ {
+ ret = _encoder->SetPacketLoss(lossRate);
+ if (ret < 0 )
+ {
+ return ret;
+ }
+ ret = (WebRtc_Word32)_encoder->SetRates(targetRate, _mediaOpt.InputFrameRate());
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ else
+ {
+ return VCM_UNINITIALIZED;
+ } // encoder
+ }// send side
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::SetReceiveChannelParameters(WebRtc_UWord32 RTT)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetReceiveChannelParameters()");
+ CriticalSectionScoped receiveCs(_receiveCritSect);
+ _receiver.UpdateRtt(RTT);
+ return 0;
+}
+
+// Register a transport callback which will be called to deliver the encoded buffers
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterTransportCallback(VCMPacketizationCallback* transport)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterTransportCallback()");
+ CriticalSectionScoped cs(_sendCritSect);
+ _encodedFrameCallback.SetMediaOpt(&_mediaOpt);
+ _encodedFrameCallback.SetTransportCallback(transport);
+ return VCM_OK;
+}
+
+// Register video output information callback which will be called to deliver information
+// about the video stream produced by the encoder, for instance the average frame rate and
+// bit rate.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterSendStatisticsCallback()");
+ CriticalSectionScoped cs(_sendCritSect);
+ _sendStatsCallback = sendStats;
+ return VCM_OK;
+}
+
+// Register a video quality settings callback which will be called when frame rate/dimensions
+// need to be updated for video quality optimization
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings)
+{
+ CriticalSectionScoped cs(_sendCritSect);
+ return _mediaOpt.RegisterVideoQMCallback(videoQMSettings);
+}
+
+
+// Register a video protection callback which will be called to deliver the requested FEC rate
+// and NACK status (on/off).
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterProtectionCallback(VCMProtectionCallback* protection)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterProtectionCallback()");
+ CriticalSectionScoped cs(_sendCritSect);
+ _mediaOpt.RegisterProtectionCallback(protection);
+ return VCM_OK;
+}
+
+// Enable or disable a video protection method.
+WebRtc_Word32
+VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection, bool enable)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetVideoProtection()");
+
+ switch (videoProtection)
+ {
+
+ case kProtectionNack:
+ {
+ // Both send-side and receive-side
+ SetVideoProtection(kProtectionNackSender, enable);
+ SetVideoProtection(kProtectionNackReceiver, enable);
+ break;
+ }
+
+ case kProtectionNackSender:
+ {
+ // Send-side only
+ CriticalSectionScoped cs(_sendCritSect);
+ _mediaOpt.EnableNack(enable);
+ break;
+ }
+
+ case kProtectionNackReceiver:
+ {
+ // Receive-side only
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (enable)
+ {
+ _receiver.SetNackMode(kNackInfinite);
+ }
+ else
+ {
+ _receiver.SetNackMode(kNoNack);
+ }
+ break;
+ }
+
+ case kProtectionDualDecoder:
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (enable)
+ {
+ _receiver.SetNackMode(kNoNack);
+ _dualReceiver.SetNackMode(kNackInfinite);
+ }
+ else
+ {
+ _dualReceiver.SetNackMode(kNoNack);
+ }
+ break;
+ }
+
+ case kProtectionKeyOnLoss:
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (enable)
+ {
+ _keyRequestMode = kKeyOnLoss;
+ }
+ else if (_keyRequestMode == kKeyOnLoss)
+ {
+ _keyRequestMode = kKeyOnError; // default mode
+ }
+ else
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ break;
+ }
+
+ case kProtectionKeyOnKeyLoss:
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (enable)
+ {
+ _keyRequestMode = kKeyOnKeyLoss;
+ }
+ else if (_keyRequestMode == kKeyOnKeyLoss)
+ {
+ _keyRequestMode = kKeyOnError; // default mode
+ }
+ else
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ break;
+ }
+
+ case kProtectionNackFEC:
+ {
+ {
+ // Receive side
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (enable)
+ {
+ _receiver.SetNackMode(kNackHybrid);
+ }
+ else
+ {
+ _receiver.SetNackMode(kNoNack);
+ }
+ }
+ // Send Side
+ {
+ CriticalSectionScoped cs(_sendCritSect);
+ _mediaOpt.EnableNackFEC(enable);
+ }
+ break;
+ }
+
+ case kProtectionFEC:
+ {
+ CriticalSectionScoped cs(_sendCritSect);
+ _mediaOpt.EnableFEC(enable);
+ break;
+ }
+
+ case kProtectionPeriodicKeyFrames:
+ {
+ CriticalSectionScoped cs(_sendCritSect);
+ return _codecDataBase.SetPeriodicKeyFrames(enable);
+ break;
+ }
+
+ default:
+ return VCM_PARAMETER_ERROR;
+ }
+ return VCM_OK;
+}
+
+// Add one raw video frame to the encoder, blocking.
+WebRtc_Word32
+VideoCodingModuleImpl::AddVideoFrame(const VideoFrame& videoFrame,
+ const VideoContentMetrics* _contentMetrics,
+ const CodecSpecificInfo* codecSpecificInfo)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "AddVideoFrame()");
+ CriticalSectionScoped cs(_sendCritSect);
+
+ if (_encoder == NULL)
+ {
+ return VCM_UNINITIALIZED;
+ }
+
+ if (_nextFrameType == kFrameEmpty)
+ {
+ return VCM_OK;
+ }
+
+ _mediaOpt.UpdateIncomingFrameRate();
+
+ if (_mediaOpt.DropFrame())
+ {
+ WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Drop frame due to bitrate");
+ }
+ else
+ {
+ _mediaOpt.updateContentData(_contentMetrics);
+ const FrameType requestedFrameType = _nextFrameType;
+ _nextFrameType = kVideoFrameDelta; // default frame type
+ WebRtc_Word32 ret = _encoder->Encode(videoFrame,
+ codecSpecificInfo,
+ requestedFrameType);
+ if (_encoderInputFile != NULL)
+ {
+ fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _encoderInputFile);
+ }
+ if (ret < 0)
+ {
+ _nextFrameType = requestedFrameType;
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding,VCMId(_id),
+ "Encode error: %d", ret);
+ return ret;
+ }
+ }
+
+ return VCM_OK;
+}
+
+// Next frame encoded should be of the type frameType
+// Good for only one frame
+WebRtc_Word32
+VideoCodingModuleImpl::FrameTypeRequest(FrameType frameType)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "FrameTypeRequest()");
+ CriticalSectionScoped cs(_sendCritSect);
+ _nextFrameType = frameType;
+ if (_encoder != NULL && _encoder->InternalSource())
+ {
+ // Try to request the frame if we have an external encoder with internal source
+ // since AddVideoFrame never will be called.
+ if (_encoder->RequestFrame(_nextFrameType) == WEBRTC_VIDEO_CODEC_OK)
+ {
+ _nextFrameType = kVideoFrameDelta;
+ }
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::EnableFrameDropper(bool enable)
+{
+ CriticalSectionScoped cs(_sendCritSect);
+ _mediaOpt.EnableFrameDropper(enable);
+ return VCM_OK;
+}
+
+
+WebRtc_Word32
+VideoCodingModuleImpl::SentFrameCount(VCMFrameCount &frameCount) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SentFrameCount()");
+ CriticalSectionScoped cs(_sendCritSect);
+ return _mediaOpt.SentFrameCount(frameCount);
+}
+
+// Initialize receiver, resets codec database etc
+WebRtc_Word32
+VideoCodingModuleImpl::InitializeReceiver()
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "InitializeReceiver()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ WebRtc_Word32 ret = _receiver.Initialize();
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = _dualReceiver.Initialize();
+ if (ret < 0)
+ {
+ return ret;
+ }
+ _codecDataBase.ResetReceiver();
+ _timing.Reset();
+
+ _decoder = NULL;
+ _decodedFrameCallback.SetUserReceiveCallback(NULL);
+ _receiverInited = true;
+ _frameTypeCallback = NULL;
+ _frameStorageCallback = NULL;
+ _receiveStatsCallback = NULL;
+ _packetRequestCallback = NULL;
+ _keyRequestMode = kKeyOnError;
+ _scheduleKeyRequest = false;
+
+ return VCM_OK;
+}
+
+// Register a receive callback. Will be called whenever there is a new frame ready
+// for rendering.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterReceiveCallback(VCMReceiveCallback* receiveCallback)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterReceiveCallback()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ _decodedFrameCallback.SetUserReceiveCallback(receiveCallback);
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterReceiveStatisticsCallback(
+ VCMReceiveStatisticsCallback* receiveStats)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterReceiveStatisticsCallback()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ _receiveStatsCallback = receiveStats;
+ return VCM_OK;
+}
+
+// Register an externally defined decoder/render object.
+// Can be a decoder only or a decoder coupled with a renderer.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterExternalDecoder(VideoDecoder* externalDecoder,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall,
+ webrtc::kTraceVideoCoding,
+ VCMId(_id),
+ "RegisterExternalDecoder()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (externalDecoder == NULL)
+ {
+ // Make sure the VCM updates the decoder next time it decodes.
+ _decoder = NULL;
+ return _codecDataBase.DeRegisterExternalDecoder(payloadType);
+ }
+ else
+ {
+ return _codecDataBase.RegisterExternalDecoder(externalDecoder,
+ payloadType,
+ internalRenderTiming);
+ }
+}
+
+// Register a frame type request callback.
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterFrameTypeCallback()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ _frameTypeCallback = frameTypeCallback;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterFrameStorageCallback(VCMFrameStorageCallback* frameStorageCallback)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterFrameStorageCallback()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ _frameStorageCallback = frameStorageCallback;
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterPacketRequestCallback(VCMPacketRequestCallback* callback)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterPacketRequestCallback()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ _packetRequestCallback = callback;
+ return VCM_OK;
+}
+
+// Decode next frame, blocking.
+// Should be called as often as possible to get the most out of the decoder.
+WebRtc_Word32
+VideoCodingModuleImpl::Decode(WebRtc_UWord16 maxWaitTimeMs)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Decode()");
+ WebRtc_Word64 nextRenderTimeMs;
+
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (!_receiverInited)
+ {
+ return VCM_UNINITIALIZED;
+ }
+ if (!_codecDataBase.DecoderRegistered())
+ {
+ return VCM_NO_CODEC_REGISTERED;
+ }
+ }
+
+ const bool dualReceiverEnabledNotReceiving = _dualReceiver.State() != kReceiving &&
+ _dualReceiver.NackMode() == kNackInfinite;
+
+ VCMEncodedFrame* frame = _receiver.FrameForDecoding(maxWaitTimeMs,
+ nextRenderTimeMs,
+ _codecDataBase.RenderTiming(),
+ &_dualReceiver);
+
+ if (dualReceiverEnabledNotReceiving && _dualReceiver.State() == kReceiving)
+ {
+ // Dual receiver is enabled (kNACK enabled), but was not receiving before the call to
+ // FrameForDecoding(). After the call the state changed to receiving, and therefore
+ // we must copy the primary decoder state to the dual decoder to make it possible
+ // for the dual decoder to start decoding retransmitted frames and recover.
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (_dualDecoder != NULL)
+ {
+ _codecDataBase.ReleaseDecoder(_dualDecoder);
+ }
+ _dualDecoder = _codecDataBase.CreateDecoderCopy();
+ if (_dualDecoder != NULL)
+ {
+ _dualDecoder->RegisterDecodeCompleteCallback(&_dualDecodedFrameCallback);
+ }
+ }
+
+ if (frame != NULL)
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+
+ const WebRtc_UWord32 timestamp = frame->TimeStamp();
+
+ // If this frame was too late, we should adjust the delay accordingly
+ _timing.UpdateCurrentDelay(frame->RenderTimeMs(), VCMTickTime::MillisecondTimestamp());
+
+ if (_bitStreamBeforeDecoder != NULL)
+ {
+ // Write bit stream to file for debugging purposes
+ fwrite(frame->Buffer(), 1, frame->Length(), _bitStreamBeforeDecoder);
+ }
+ if (_frameStorageCallback != NULL)
+ {
+ WebRtc_Word32 ret = frame->Store(*_frameStorageCallback);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ const WebRtc_Word32 ret = Decode(*frame);
+ _receiver.ReleaseFrame(frame);
+ frame = NULL;
+ if (ret != VCM_OK)
+ {
+ return ret;
+ }
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::RequestSliceLossIndication(const WebRtc_UWord64 pictureID) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterSliceLossIndication()");
+ if (_frameTypeCallback != NULL)
+ {
+ const WebRtc_Word32 ret = _frameTypeCallback->SliceLossIndicationRequest(pictureID);
+ if (ret < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to request key frame");
+ return ret;
+ }
+ } else
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id),
+ "No frame type request callback registered");
+ return VCM_MISSING_CALLBACK;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::RequestKeyFrame()
+{
+ if (_frameTypeCallback != NULL)
+ {
+ const WebRtc_Word32 ret = _frameTypeCallback->FrameTypeRequest(kVideoFrameKey);
+ if (ret < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to request key frame");
+ return ret;
+ }
+ _scheduleKeyRequest = false;
+ }
+ else
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id),
+ "No frame type request callback registered");
+ return VCM_MISSING_CALLBACK;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "DecodeDualFrame()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (_dualReceiver.State() != kReceiving || _dualReceiver.NackMode() != kNackInfinite)
+ {
+ // The dual receiver is currently not receiving or dual decoder mode is disabled.
+ return VCM_OK;
+ }
+ WebRtc_Word64 dummyRenderTime;
+ WebRtc_Word32 decodeCount = 0;
+ VCMEncodedFrame* dualFrame = _dualReceiver.FrameForDecoding(maxWaitTimeMs,
+ dummyRenderTime);
+ if (dualFrame != NULL && _dualDecoder != NULL)
+ {
+ WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Decoding frame %u with dual decoder", dualFrame->TimeStamp());
+ // Decode dualFrame and try to catch up
+ WebRtc_Word32 ret = _dualDecoder->Decode(*dualFrame);
+ if (ret != WEBRTC_VIDEO_CODEC_OK)
+ {
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to decode frame with dual decoder");
+ _dualReceiver.ReleaseFrame(dualFrame);
+ return VCM_CODEC_ERROR;
+ }
+ if (_receiver.DualDecoderCaughtUp(dualFrame, _dualReceiver))
+ {
+ // Copy the complete decoder state of the dual decoder
+ // to the primary decoder.
+ WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Dual decoder caught up");
+ _codecDataBase.CopyDecoder(*_dualDecoder);
+ _codecDataBase.ReleaseDecoder(_dualDecoder);
+ _dualDecoder = NULL;
+ }
+ decodeCount++;
+ }
+ _dualReceiver.ReleaseFrame(dualFrame);
+ return decodeCount;
+}
+
+
+// Must be called from inside the receive side critical section.
+WebRtc_Word32
+VideoCodingModuleImpl::Decode(const VCMEncodedFrame& frame)
+{
+ // Change decoder if payload type has changed
+ const bool renderTimingBefore = _codecDataBase.RenderTiming();
+ _decoder = _codecDataBase.SetDecoder(frame.PayloadType(), _decodedFrameCallback);
+ if (renderTimingBefore != _codecDataBase.RenderTiming())
+ {
+ // Make sure we reset the decode time estimate since it will
+ // be zero for codecs without render timing.
+ _timing.ResetDecodeTime();
+ }
+ if (_decoder == NULL)
+ {
+ return VCM_NO_CODEC_REGISTERED;
+ }
+ // Decode a frame
+ WebRtc_Word32 ret = _decoder->Decode(frame);
+
+ // Check for failed decoding, run frame type request callback if needed.
+ if (ret < 0)
+ {
+ if (ret == VCM_ERROR_REQUEST_SLI)
+ {
+ return RequestSliceLossIndication(
+ _decodedFrameCallback.LastReceivedPictureID() + 1);
+ }
+ else
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to decode frame %u, requesting key frame", frame.TimeStamp());
+ ret = RequestKeyFrame();
+ }
+ }
+ else if (ret == VCM_REQUEST_SLI)
+ {
+ ret = RequestSliceLossIndication(_decodedFrameCallback.LastReceivedPictureID() + 1);
+ }
+ if (!frame.Complete() || frame.MissingFrame())
+ {
+ switch (_keyRequestMode)
+ {
+ case kKeyOnKeyLoss:
+ {
+ if (frame.FrameType() == kVideoFrameKey)
+ {
+ _scheduleKeyRequest = true;
+ return VCM_OK;
+ }
+ break;
+ }
+ case kKeyOnLoss:
+ {
+ _scheduleKeyRequest = true;
+ return VCM_OK;
+ }
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::DecodeFromStorage(const EncodedVideoData& frameFromStorage)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "DecodeFromStorage()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ WebRtc_Word32 ret = _frameFromFile.ExtractFromStorage(frameFromStorage);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ return Decode(_frameFromFile);
+}
+
+// Reset the decoder state
+WebRtc_Word32
+VideoCodingModuleImpl::ResetDecoder()
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ResetDecoder()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (_decoder != NULL)
+ {
+ _receiver.Initialize();
+ _timing.Reset();
+ return _decoder->Reset();
+ _scheduleKeyRequest = false;
+ }
+ if (_dualReceiver.State() != kPassive)
+ {
+ _dualReceiver.Initialize();
+ }
+ if (_dualDecoder != NULL)
+ {
+ _codecDataBase.ReleaseDecoder(_dualDecoder);
+ _dualDecoder = NULL;
+ }
+ return VCM_OK;
+}
+
+// Register possible receive codecs, can be called multiple times
+WebRtc_Word32
+VideoCodingModuleImpl::RegisterReceiveCodec(const VideoCodec* receiveCodec,
+ WebRtc_Word32 numberOfCores,
+ bool requireKeyFrame)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "RegisterReceiveCodec()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (receiveCodec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ return _codecDataBase.RegisterReceiveCodec(receiveCodec, numberOfCores, requireKeyFrame);
+}
+
+// Get current received codec
+WebRtc_Word32
+VideoCodingModuleImpl::ReceiveCodec(VideoCodec* currentReceiveCodec) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ReceiveCodec()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ if (currentReceiveCodec == NULL)
+ {
+ return VCM_PARAMETER_ERROR;
+ }
+ return _codecDataBase.ReceiveCodec(currentReceiveCodec);
+}
+
+// Get current received codec
+VideoCodecType
+VideoCodingModuleImpl::ReceiveCodec() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ReceiveCodec()");
+ CriticalSectionScoped cs(_receiveCritSect);
+ return _codecDataBase.ReceiveCodec();
+}
+
+// Incoming packet from network parsed and ready for decode, non blocking.
+WebRtc_Word32
+VideoCodingModuleImpl::IncomingPacket(const WebRtc_UWord8* incomingPayload,
+ WebRtc_UWord32 payloadLength,
+ const WebRtcRTPHeader& rtpInfo)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "IncomingPacket()");
+ const VCMPacket packet(incomingPayload, payloadLength, rtpInfo);
+ WebRtc_Word32 ret;
+ if (_dualReceiver.State() != kPassive)
+ {
+ ret = _dualReceiver.InsertPacket(packet,
+ rtpInfo.type.Video.width,
+ rtpInfo.type.Video.height);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ ret = _receiver.InsertPacket(packet, rtpInfo.type.Video.width, rtpInfo.type.Video.height);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ return VCM_OK;
+}
+
+// Set codec config parameters
+WebRtc_Word32
+VideoCodingModuleImpl::SetCodecConfigParameters(WebRtc_UWord8 payloadType,
+ const WebRtc_UWord8* buffer,
+ WebRtc_Word32 length)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetCodecConfigParameters()");
+ CriticalSectionScoped cs(_receiveCritSect);
+
+ WebRtc_Word32 ret = _codecDataBase.SetCodecConfigParameters(payloadType, buffer, length);
+ if (ret < 0)
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetCodecConfigParameters() failed, %d", ret);
+ return ret;
+ }
+ return VCM_OK;
+}
+
+// Minimum playout delay (used for lip-sync). This is the minimum delay required
+// to sync with audio. Not included in VideoCodingModule::Delay()
+// Defaults to 0 ms.
+WebRtc_Word32
+VideoCodingModuleImpl::SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetMininumPlayoutDelay(%u)", minPlayoutDelayMs);
+ _timing.SetMinimumTotalDelay(minPlayoutDelayMs);
+ return VCM_OK;
+}
+
+// The estimated delay caused by rendering, defaults to
+// kDefaultRenderDelayMs = 10 ms
+WebRtc_Word32
+VideoCodingModuleImpl::SetRenderDelay(WebRtc_UWord32 timeMS)
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "SetRenderDelay(%u)", timeMS);
+ _timing.SetRenderDelay(timeMS);
+ return VCM_OK;
+}
+
+// Current video delay
+WebRtc_Word32
+VideoCodingModuleImpl::Delay() const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Delay()");
+ return _timing.TargetVideoDelay();
+}
+
+// Nack list
+WebRtc_Word32
+VideoCodingModuleImpl::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size)
+{
+ VCMNackStatus nackStatus = kNackOk;
+ // Collect sequence numbers from the default receiver
+ // if in normal nack mode. Otherwise collect them from
+ // the dual receiver if the dual receiver is receiving.
+ if (_receiver.NackMode() != kNoNack)
+ {
+ nackStatus = _receiver.NackList(nackList, size);
+ }
+ else if (_dualReceiver.State() != kPassive)
+ {
+ nackStatus = _dualReceiver.NackList(nackList, size);
+ }
+ else
+ {
+ size = 0;
+ }
+
+ switch (nackStatus)
+ {
+ case kNackNeedMoreMemory:
+ {
+ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Out of memory");
+ return VCM_MEMORY;
+ }
+ case kNackKeyFrameRequest:
+ {
+ CriticalSectionScoped cs(_receiveCritSect);
+ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id),
+ "Failed to get NACK list, requesting key frame");
+ return RequestKeyFrame();
+ }
+ default:
+ break;
+ }
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VideoCodingModuleImpl::ReceivedFrameCount(VCMFrameCount& frameCount) const
+{
+ WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id),
+ "ReceivedFrameCount()");
+ return _receiver.ReceivedFrameCount(frameCount);
+}
+
+}
diff --git a/src/modules/video_coding/main/source/video_coding_impl.h b/src/modules/video_coding/main/source/video_coding_impl.h
new file mode 100644
index 0000000..69f4493
--- /dev/null
+++ b/src/modules/video_coding/main/source/video_coding_impl.h
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_
+#define WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_
+
+#include "video_coding.h"
+#include "critical_section_wrapper.h"
+#include "frame_buffer.h"
+#include "receiver.h"
+#include "timing.h"
+#include "jitter_buffer.h"
+#include "codec_database.h"
+#include "generic_decoder.h"
+#include "generic_encoder.h"
+#include "media_optimization.h"
+
+#include <stdio.h>
+
+namespace webrtc
+{
+
+class VCMProcessTimer
+{
+public:
+ VCMProcessTimer(WebRtc_UWord32 periodMs) :
+ _periodMs(periodMs), _latestMs(VCMTickTime::MillisecondTimestamp()) {}
+ WebRtc_UWord32 Period() const;
+ WebRtc_UWord32 TimeUntilProcess() const;
+ void Processed();
+
+private:
+ WebRtc_UWord32 _periodMs;
+ WebRtc_Word64 _latestMs;
+};
+
+enum VCMKeyRequestMode
+{
+ kKeyOnError, // Normal mode, request key frames on decoder error
+ kKeyOnKeyLoss, // Request key frames on decoder error and on packet loss in key frames
+ kKeyOnLoss, // Request key frames on decoder error and on packet loss in any frame
+};
+
+class VideoCodingModuleImpl : public VideoCodingModule
+{
+public:
+ VideoCodingModuleImpl(const WebRtc_Word32 id);
+
+ virtual ~VideoCodingModuleImpl();
+
+ // Returns version of the module and its components
+ WebRtc_Word32 Version(WebRtc_Word8* version,
+ WebRtc_UWord32& remainingBufferInBytes,
+ WebRtc_UWord32& position) const;
+
+ WebRtc_Word32 Id() const;
+
+ // Change the unique identifier of this object
+ virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id);
+
+ // Returns the number of milliseconds until the module want a worker thread to call Process
+ virtual WebRtc_Word32 TimeUntilNextProcess();
+
+ virtual WebRtc_Word32 Process();
+
+ /*
+ * Sender
+ */
+
+ // Initialize send codec
+ virtual WebRtc_Word32 InitializeSender();
+
+ // Makes sure the encoder is in its initial state.
+ virtual WebRtc_Word32 ResetEncoder();
+
+ // Register the send codec to be used.
+ virtual WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec,
+ WebRtc_UWord32 numberOfCores,
+ WebRtc_UWord32 maxPayloadSize);
+
+ // Get current send codec
+ virtual WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const;
+
+ // Get current send codec type
+ virtual VideoCodecType SendCodec() const;
+
+ // Register an external encoder object.
+ virtual WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder,
+ WebRtc_UWord8 payloadType,
+ bool internalSource = false);
+
+ // Get codec config parameters
+ virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size);
+
+ // Get encode bitrate
+ virtual WebRtc_UWord32 Bitrate() const;
+
+ // Get encode frame rate
+ virtual WebRtc_UWord32 FrameRate() const;
+
+ // Set channel parameters
+ virtual WebRtc_Word32 SetChannelParameters(WebRtc_UWord32 availableBandWidth,
+ WebRtc_UWord8 lossRate,
+ WebRtc_UWord32 RTT);
+ // Set recieve channel parameters
+ virtual WebRtc_Word32 SetReceiveChannelParameters(WebRtc_UWord32 RTT);
+
+ // Register a transport callback which will be called to deliver the encoded buffers
+ virtual WebRtc_Word32 RegisterTransportCallback(VCMPacketizationCallback* transport);
+
+ // Register a send statistics callback which will be called to deliver information
+ // about the video stream produced by the encoder,
+ // for instance the average frame rate and bit rate.
+ virtual WebRtc_Word32 RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats);
+
+ // Register a video quality settings callback which will be called when
+ // frame rate/dimensions need to be updated for video quality optimization
+ virtual WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings);
+
+ // Register a video protection callback which will be called to deliver
+ // the requested FEC rate and NACK status (on/off).
+ virtual WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protection);
+
+ // Enable or disable a video protection method.
+ virtual WebRtc_Word32 SetVideoProtection(VCMVideoProtection videoProtection, bool enable);
+
+ // Add one raw video frame to the encoder, blocking.
+ virtual WebRtc_Word32 AddVideoFrame(
+ const VideoFrame& videoFrame,
+ const VideoContentMetrics* _contentMetrics = NULL,
+ const CodecSpecificInfo* codecSpecificInfo = NULL);
+
+ // Next frame encoded should be of the type frameType.
+ virtual WebRtc_Word32 FrameTypeRequest(FrameType frameType);
+
+ //Enable frame dropper
+ virtual WebRtc_Word32 EnableFrameDropper(bool enable);
+
+ // Sent frame counters
+ virtual WebRtc_Word32 SentFrameCount(VCMFrameCount& frameCount) const;
+
+ /*
+ * Receiver
+ */
+
+ // Initialize receiver, resets codec database etc
+ virtual WebRtc_Word32 InitializeReceiver();
+
+ // Register possible reveive codecs, can be called multiple times
+ virtual WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec,
+ WebRtc_Word32 numberOfCores,
+ bool requireKeyFrame = false);
+
+ // Register an externally defined decoder/render object.
+ // Can be a decoder only or a decoder coupled with a renderer.
+ virtual WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder,
+ WebRtc_UWord8 payloadType,
+ bool internalRenderTiming);
+
+ // Register a receive callback. Will be called whenever there are a new frame ready
+ // for rendering.
+ virtual WebRtc_Word32 RegisterReceiveCallback(VCMReceiveCallback* receiveCallback);
+
+ // Register a receive statistics callback which will be called to deliver information
+ // about the video stream received by the receiving side of the VCM, for instance
+ // the average frame rate and bit rate.
+ virtual WebRtc_Word32 RegisterReceiveStatisticsCallback(
+ VCMReceiveStatisticsCallback* receiveStats);
+
+ // Register a frame type request callback.
+ virtual WebRtc_Word32 RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback);
+
+ // Register a frame storage callback.
+ virtual WebRtc_Word32 RegisterFrameStorageCallback(
+ VCMFrameStorageCallback* frameStorageCallback);
+
+ // Nack callback
+ virtual WebRtc_Word32 RegisterPacketRequestCallback(VCMPacketRequestCallback* callback);
+
+ // Decode next frame, blocks for a maximum of maxWaitTimeMs milliseconds.
+ // Should be called as often as possible to get the most out of the decoder.
+ virtual WebRtc_Word32 Decode(WebRtc_UWord16 maxWaitTimeMs = 200);
+
+ // Decode next dual frame, blocks for a maximum of maxWaitTimeMs milliseconds.
+ virtual WebRtc_Word32 DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs = 200);
+
+ // Reset the decoder state
+ virtual WebRtc_Word32 ResetDecoder();
+
+ // Get current received codec
+ virtual WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const;
+
+ // Get current received codec type
+ virtual VideoCodecType ReceiveCodec() const;
+
+ // Incoming packet from network parsed and ready for decode, non blocking.
+ virtual WebRtc_Word32 IncomingPacket(const WebRtc_UWord8* incomingPayload,
+ WebRtc_UWord32 payloadLength,
+ const WebRtcRTPHeader& rtpInfo);
+
+ // A part of an encoded frame to be decoded.
+ // Used in conjunction with VCMFrameStorageCallback.
+ virtual WebRtc_Word32 DecodeFromStorage(const EncodedVideoData& frameFromStorage);
+
+ // Set codec config parameters
+ virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType,
+ const WebRtc_UWord8* buffer,
+ WebRtc_Word32 length);
+
+ // Minimum playout delay (Used for lip-sync). This is the minimum delay required
+ // to sync with audio. Not included in VideoCodingModule::Delay()
+ // Defaults to 0 ms.
+ virtual WebRtc_Word32 SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs);
+
+ // The estimated delay caused by rendering
+ virtual WebRtc_Word32 SetRenderDelay(WebRtc_UWord32 timeMS);
+
+ // Current delay
+ virtual WebRtc_Word32 Delay() const;
+
+ // Received frame counters
+ virtual WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const;
+
+protected:
+ WebRtc_Word32 Decode(const webrtc::VCMEncodedFrame& frame);
+ WebRtc_Word32 RequestKeyFrame();
+ WebRtc_Word32 RequestSliceLossIndication(const WebRtc_UWord64 pictureID) const;
+ WebRtc_Word32 NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size);
+
+private:
+ WebRtc_Word32 _id;
+ CriticalSectionWrapper& _receiveCritSect; // Critical section for receive side
+ bool _receiverInited;
+ VCMTiming _timing;
+ VCMTiming _dualTiming;
+ VCMReceiver _receiver;
+ VCMReceiver _dualReceiver;
+ VCMDecodedFrameCallback _decodedFrameCallback;
+ VCMDecodedFrameCallback _dualDecodedFrameCallback;
+ VCMFrameTypeCallback* _frameTypeCallback;
+ VCMFrameStorageCallback* _frameStorageCallback;
+ VCMReceiveStatisticsCallback* _receiveStatsCallback;
+ VCMPacketRequestCallback* _packetRequestCallback;
+ VCMGenericDecoder* _decoder;
+ VCMGenericDecoder* _dualDecoder;
+ FILE* _bitStreamBeforeDecoder;
+ VCMFrameBuffer _frameFromFile;
+ VCMKeyRequestMode _keyRequestMode;
+ bool _scheduleKeyRequest;
+
+ CriticalSectionWrapper& _sendCritSect; // Critical section for send side
+ VCMGenericEncoder* _encoder;
+ VCMEncodedFrameCallback _encodedFrameCallback;
+ FrameType _nextFrameType;
+ VCMMediaOptimization _mediaOpt;
+ VideoCodecType _sendCodecType;
+ VCMSendStatisticsCallback* _sendStatsCallback;
+ FILE* _encoderInputFile;
+
+ VCMCodecDataBase _codecDataBase;
+ VCMProcessTimer _receiveStatsTimer;
+ VCMProcessTimer _sendStatsTimer;
+ VCMProcessTimer _retransmissionTimer;
+ VCMProcessTimer _keyRequestTimer;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_
diff --git a/src/modules/video_coding/main/source/video_coding_test.gyp b/src/modules/video_coding/main/source/video_coding_test.gyp
new file mode 100644
index 0000000..89e62ef
--- /dev/null
+++ b/src/modules/video_coding/main/source/video_coding_test.gyp
@@ -0,0 +1,78 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ '../../../../common_settings.gypi', # Common settings
+ ],
+ 'targets': [
+ {
+ 'target_name': 'video_coding_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'video_coding.gyp:webrtc_video_coding',
+ '../../../rtp_rtcp/source/rtp_rtcp.gyp:rtp_rtcp',
+ '../../../utility/source/utility.gyp:webrtc_utility',
+ '../../../video_processing/main/source/video_processing.gyp:video_processing',
+ '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib',
+ ],
+ 'include_dirs': [
+ '../../../interface',
+ '../../codecs/vp8/main/interface',
+ '../../../../system_wrappers/interface',
+ '../../../../common_video/interface',
+ '../source',
+ ],
+ 'sources': [
+
+ # headers
+ '../test/codec_database_test.h',
+ '../test/generic_codec_test.h',
+ '../test/jitter_estimate_test.h',
+ '../test/media_opt_test.h',
+ '../test/normal_test.h',
+ '../test/quality_modes_test.h',
+ '../test/receiver_tests.h',
+ '../test/release_test.h',
+ '../test/rtp_player.h',
+ '../test/test_util.h',
+ '../test/video_source.h',
+
+ # sources
+ '../test/codec_database_test.cc',
+ '../test/decode_from_storage_test.cc',
+ '../test/generic_codec_test.cc',
+ '../test/jitter_buffer_test.cc',
+ '../test/media_opt_test.cc',
+ '../test/mt_rx_tx_test.cc',
+ '../test/normal_test.cc',
+ '../test/quality_modes_test.cc',
+ '../test/receiver_timing_tests.cc',
+ '../test/rtp_player.cc',
+ '../test/test_util.cc',
+ '../test/tester_main.cc',
+ '../test/video_rtp_play_mt.cc',
+ '../test/video_rtp_play.cc',
+ '../test/video_source.cc',
+
+ ], # source
+
+ 'conditions': [
+
+ ['OS=="linux"', {
+ 'cflags': [
+ '-fexceptions',
+ ],
+ }],
+
+ ], # conditions
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/modules/video_coding/main/test/codec_database_test.cc b/src/modules/video_coding/main/test/codec_database_test.cc
new file mode 100644
index 0000000..3faf0dd
--- /dev/null
+++ b/src/modules/video_coding/main/test/codec_database_test.cc
@@ -0,0 +1,396 @@
+/*
+ * 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.
+ */
+
+// Implementation of codec data base test
+// testing is done via the VCM module, no specific CodecDataBase module functionality.
+
+#include "codec_database_test.h"
+#include "vp8.h" // for external codecs test
+#include "../source/event.h"
+#include "test_util.h"
+#include "../../../../engine_configurations.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+using namespace webrtc;
+
+int CodecDataBaseTest::RunTest(CmdArgs& args)
+{
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ CodecDataBaseTest* cdbt = new CodecDataBaseTest(vcm);
+ cdbt->Perform(args);
+ VideoCodingModule::Destroy(vcm);
+ delete cdbt;
+ return 0;
+
+}
+
+CodecDataBaseTest::CodecDataBaseTest(VideoCodingModule* vcm):
+_width(0),
+_height(0),
+_timeStamp(0),
+_lengthSourceFrame(0),
+vcmMacrosTests(0),
+vcmMacrosErrors(0),
+_vcm(vcm)
+{
+ //
+}
+CodecDataBaseTest::~CodecDataBaseTest()
+{
+ //
+}
+void
+CodecDataBaseTest::Setup(CmdArgs& args)
+{
+ _inname= args.inputFile;
+ _width = args.width;
+ _height = args.height;
+ _frameRate = args.frameRate;
+ _lengthSourceFrame = 3*_width*_height/2;
+ if (args.outputFile.compare(""))
+ _outname = "CDBtest_decoded.yuv";
+ else
+ _outname = args.outputFile;
+ _outname = args.outputFile;
+ _encodedName = "../CDBtest_encoded.vp8";
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+
+ if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ return;
+}
+
+
+
+WebRtc_Word32
+CodecDataBaseTest::Perform(CmdArgs& args)
+{
+#ifndef VIDEOCODEC_VP8
+ assert(false);
+#endif
+ Setup(args);
+ EventWrapper* waitEvent = EventWrapper::Create();
+
+ /**************************/
+ /* General Sanity Checks */
+ /************************/
+ VideoCodec sendCodec, receiveCodec;
+ TEST(VideoCodingModule::NumberOfCodecs() > 0);
+ WebRtc_Word8 version[512];
+ WebRtc_UWord32 length = 512;
+ WebRtc_UWord32 position = 0;
+ TEST(_vcm->Version(version, length, position) == VCM_OK);
+ printf("%s", version);
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ VCMDecodeCompleteCallback *_decodeCallback = new VCMDecodeCompleteCallback(_decodedFile);
+ VCMEncodeCompleteCallback *_encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile);
+ _vcm->RegisterReceiveCallback(_decodeCallback);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ _encodeCompleteCallback->SetFrameDimensions(_width, _height);
+ // registering the callback - encode and decode with the same vcm (could be later changed)
+ _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
+ // preparing a frame to be encoded
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ // Encoder registration
+ TEST (VideoCodingModule::NumberOfCodecs() > 0);
+ TEST(VideoCodingModule::Codec(-1, &sendCodec) == VCM_PARAMETER_ERROR);
+ TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1, &sendCodec) == VCM_PARAMETER_ERROR);
+ VideoCodingModule::Codec(1, &sendCodec);
+ sendCodec.plType = 0; // random value
+ TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0);
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ _vcm->RegisterReceiveCallback(_decodeCallback);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ printf(" \nNumber of Registered Codecs: %d \n\n", VideoCodingModule::NumberOfCodecs());
+ printf("Registered codec names: ");
+ for (int i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
+ {
+ VideoCodingModule::Codec(i, &sendCodec);
+ printf("%s ", sendCodec.plName);
+ }
+ printf("\n\nVerify that all requested codecs are used\n \n \n");
+
+ // testing with first codec registered
+ VideoCodingModule::Codec(0, &sendCodec);
+ _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
+ _vcm->InitializeReceiver();
+ TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK );
+ _vcm->InitializeSender();
+ TEST (_vcm->AddVideoFrame(sourceFrame) < 0 );
+
+ // Test changing frame size while keeping the same payload type
+ VideoCodingModule::Codec(0, &sendCodec);
+ sendCodec.width = 352;
+ sendCodec.height = 288;
+ VideoCodec currentSendCodec;
+ _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
+ _vcm->SendCodec(¤tSendCodec);
+ TEST(currentSendCodec.width == sendCodec.width &&
+ currentSendCodec.height == sendCodec.height);
+ sendCodec.width = 352/2;
+ sendCodec.height = 288/2;
+ _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
+ _vcm->SendCodec(¤tSendCodec);
+ TEST(currentSendCodec.width == sendCodec.width &&
+ currentSendCodec.height == sendCodec.height);
+
+ delete _decodeCallback;
+ _decodeCallback = NULL;
+ delete _encodeCompleteCallback;
+ _encodeCompleteCallback = NULL;
+
+ VCMEncodeCompleteCallback *_encodeCallback = new VCMEncodeCompleteCallback(_encodedFile);
+
+ /*************************/
+ /* External codecs */
+ /*************************/
+
+
+ _vcm->InitializeReceiver();
+ VP8Decoder* decoder = new VP8Decoder;
+ VideoCodec vp8DecSettings;
+ VideoCodingModule::Codec(kVideoCodecVP8, &vp8DecSettings);
+ TEST(_vcm->RegisterExternalDecoder(decoder, vp8DecSettings.plType, false) == VCM_OK);
+ TEST(_vcm->RegisterReceiveCodec(&vp8DecSettings, 1, false) == VCM_OK);
+ VP8Encoder* encoder = new VP8Encoder;
+ VideoCodec vp8EncSettings;
+ VideoCodingModule::Codec(kVideoCodecVP8, &vp8EncSettings);
+ _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized
+ _encodeCallback->RegisterReceiverVCM(_vcm);
+ _encodeCallback->SetCodecType(kRTPVideoVP8);
+ TEST(_vcm->RegisterExternalEncoder(encoder, vp8EncSettings.plType) == VCM_OK);
+ TEST(_vcm->RegisterSendCodec(&vp8EncSettings, 4, 1440) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+ waitEvent->Wait(33);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+
+ // De-register and try again.
+ TEST(_vcm->RegisterExternalDecoder(NULL, vp8DecSettings.plType, false) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() < 0); // Expect an error since we have de-registered the decoder
+ TEST(_vcm->RegisterExternalEncoder(NULL, vp8DecSettings.plType) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) < 0); // No send codec registered
+
+ delete decoder;
+ decoder = NULL;
+ delete encoder;
+ encoder = NULL;
+
+ /***************************************
+ * Test the "require key frame" setting*
+ ***************************************/
+
+ TEST(_vcm->InitializeSender() == VCM_OK);
+ TEST(_vcm->InitializeReceiver() == VCM_OK);
+ VideoCodingModule::Codec(kVideoCodecVP8, &receiveCodec);
+ receiveCodec.height = _height;
+ receiveCodec.width = _width;
+ TEST(_vcm->RegisterSendCodec(&receiveCodec, 4, 1440) == VCM_OK);
+ TEST(_vcm->RegisterReceiveCodec(&receiveCodec, 1, true) == VCM_OK); // Require key frame
+ _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized
+ _encodeCallback->RegisterReceiverVCM(_vcm);
+ _encodeCallback->SetCodecType(kRTPVideoVP8);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+ TEST(_vcm->ResetDecoder() == VCM_OK);
+ waitEvent->Wait(33);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ // Try to decode a delta frame. Should get a warning since we have enabled the "require key frame" setting
+ // and because no frame type request callback has been registered.
+ TEST(_vcm->Decode() == VCM_MISSING_CALLBACK);
+ TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+
+ // Make sure we can register another codec with the same
+ // payload type without crash.
+ _vcm->InitializeReceiver();
+ sendCodec.width = _width;
+ sendCodec.height = _height;
+ TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
+ TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK);
+ waitEvent->Wait(33);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+ TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
+ waitEvent->Wait(33);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+ TEST(_vcm->ResetDecoder() == VCM_OK);
+
+ delete _encodeCallback;
+
+ /*************************/
+ /* Send/Receive Control */
+ /***********************/
+ /*
+ 1. check available codecs (N)
+ 2. register all corresponding decoders
+ 3. encode 300/N frames with each encoder, and hope to properly decode
+ 4. encode without a matching decoder - expect an error
+ */
+ rewind(_sourceFile);
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ sourceFrame.Free();
+ VCMDecodeCompleteCallback* decodeCallCDT = new VCMDecodeCompleteCallback(_decodedFile);
+ VCMEncodeCompleteCallback* encodeCallCDT = new VCMEncodeCompleteCallback(_encodedFile);
+ _vcm->RegisterReceiveCallback(decodeCallCDT);
+ _vcm->RegisterTransportCallback(encodeCallCDT);
+ encodeCallCDT->RegisterReceiverVCM(_vcm);
+ if (VideoCodingModule::NumberOfCodecs() > 0)
+ {
+ // registrating all available decoders
+ int i, j;
+ //double psnr;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ _vcm->RegisterReceiveCallback(decodeCallCDT);
+ for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
+ {
+ VideoCodingModule::Codec(i, &receiveCodec);
+ if (strcmp(receiveCodec.plName, "I420") == 0)
+ {
+ receiveCodec.height = _height;
+ receiveCodec.width = _width;
+ }
+ _vcm->RegisterReceiveCodec(&receiveCodec, 1);
+ }
+ // start encoding - iterating over available encoders
+ _vcm->RegisterTransportCallback(encodeCallCDT);
+ encodeCallCDT->RegisterReceiverVCM(_vcm);
+ encodeCallCDT->Initialize();
+ int frameCnt = 0;
+ for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
+ {
+ encodeCallCDT->ResetByteCount();
+ VideoCodingModule::Codec(i, &sendCodec);
+ sendCodec.height = _height;
+ sendCodec.width = _width;
+ sendCodec.startBitrate = 1000;
+ sendCodec.maxBitrate = 8000;
+ encodeCallCDT->SetFrameDimensions(_width, _height);
+ encodeCallCDT->SetCodecType(ConvertCodecType(sendCodec.plName));
+ TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) == VCM_OK);
+
+ printf("Encoding with %s \n\n", sendCodec.plName);
+ for (j=0; j < int(300/VideoCodingModule::NumberOfCodecs()); j++)// assuming 300 frames, NumberOfCodecs <= 10
+ {
+ frameCnt++;
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ // building source frame
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ sourceFrame.SetLength(_lengthSourceFrame);
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+ // send frame to the encoder
+ TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ waitEvent->Wait(33); // was 100
+
+ int ret =_vcm->Decode();
+ TEST(ret == 0);
+ if (ret < 0)
+ {
+ printf("Error #%d in frame number %d \n",ret, frameCnt);
+ }
+ // verifying matching payload types:
+ _vcm->SendCodec(&sendCodec);
+ _vcm->ReceiveCodec(&receiveCodec);
+ TEST(sendCodec.plType == receiveCodec.plType);
+ if (sendCodec.plType != receiveCodec.plType)
+ {
+ printf("frame number:%d\n",frameCnt);
+ }
+ } // end for:encode-decode
+ // byte count for codec specific
+
+ printf("Total bytes encoded: %f \n\n",(8.0/1000)*(encodeCallCDT->EncodedBytes()/((int)10/VideoCodingModule::NumberOfCodecs())));
+ // decode what's left in the buffer....
+ _vcm->Decode();
+ _vcm->Decode();
+ } // end: iterate codecs
+ rewind(_sourceFile);
+ sourceFrame.Free();
+ delete tmpBuffer;
+ delete decodeCallCDT;
+ delete encodeCallCDT;
+ // closing and calculating PSNR for prior encoder-decoder test
+ TearDown(); // closing open files
+ double psnr = 0;
+ PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr);
+ printf(" \n @ %d KBPS: ", sendCodec.startBitrate);
+ printf("PSNR from encoder-decoder send-receive control test is %f \n \n", psnr);
+ } // end of #codecs >1
+
+ delete waitEvent;
+ Print();
+ return 0;
+}
+void
+CodecDataBaseTest::Print()
+{
+ printf("\nVCM Codec DataBase Test: \n\n%i tests completed\n", vcmMacrosTests);
+ if (vcmMacrosErrors > 0)
+ {
+ printf("%i FAILED\n\n", vcmMacrosErrors);
+ }
+ else
+ {
+ printf("ALL PASSED\n\n");
+ }
+}
+
+void
+CodecDataBaseTest::TearDown()
+{
+ fclose(_sourceFile);
+ fclose(_decodedFile);
+ fclose(_encodedFile);
+ return;
+}
diff --git a/src/modules/video_coding/main/test/codec_database_test.h b/src/modules/video_coding/main/test/codec_database_test.h
new file mode 100644
index 0000000..0272b14
--- /dev/null
+++ b/src/modules/video_coding/main/test/codec_database_test.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
+
+#include "video_coding.h"
+#include "test_macros.h"
+#include "test_util.h"
+
+#include <string.h>
+
+/*
+Test consists of:
+1. Sanity chacks: Send and Receive side (bad input, etc. )
+2. Send-side control (encoder registration etc.)
+3. Decoder-side control - encode with various encoders, and verify correct decoding
+*/
+
+class CodecDataBaseTest
+{
+public:
+ CodecDataBaseTest(webrtc::VideoCodingModule* vcm);
+ ~CodecDataBaseTest();
+ static int RunTest(CmdArgs& args);
+ WebRtc_Word32 Perform(CmdArgs& args);
+private:
+ void TearDown();
+ void Setup(CmdArgs& args);
+ void Print();
+ webrtc::VideoCodingModule* _vcm;
+ std::string _inname;
+ std::string _outname;
+ std::string _encodedName;
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ FILE* _encodedFile;
+ WebRtc_UWord16 _width;
+ WebRtc_UWord16 _height;
+ WebRtc_UWord32 _lengthSourceFrame;
+ WebRtc_UWord32 _timeStamp;
+ int vcmMacrosTests;
+ int vcmMacrosErrors;
+ float _frameRate;
+}; // end of codecDBTest class definition
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
diff --git a/src/modules/video_coding/main/test/decode_from_storage_test.cc b/src/modules/video_coding/main/test/decode_from_storage_test.cc
new file mode 100644
index 0000000..5ba8055
--- /dev/null
+++ b/src/modules/video_coding/main/test/decode_from_storage_test.cc
@@ -0,0 +1,183 @@
+/*
+ * 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 "receiver_tests.h"
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "trace.h"
+#include "tick_time.h"
+#include "../source/event.h"
+#include "test_macros.h"
+#include "rtp_player.h"
+
+using namespace webrtc;
+
+class FrameStorageCallback : public VCMFrameStorageCallback
+{
+public:
+ FrameStorageCallback(VideoCodingModule* vcm) : _vcm(vcm) {}
+
+ WebRtc_Word32 StoreReceivedFrame(const EncodedVideoData& frameToStore)
+ {
+ _vcm->DecodeFromStorage(frameToStore);
+ return VCM_OK;
+ }
+
+private:
+ VideoCodingModule* _vcm;
+};
+
+int DecodeFromStorageTest(CmdArgs& args)
+{
+ // Make sure this test isn't executed without simulated clocks
+#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
+ return -1;
+#endif
+ // BEGIN Settings
+
+ bool protectionEnabled = false;
+ VCMVideoProtection protectionMethod = kProtectionNack;
+ WebRtc_UWord32 rttMS = 100;
+ float lossRate = 0.00f;
+ bool reordering = false;
+ WebRtc_UWord32 renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+ const WebRtc_Word64 MAX_RUNTIME_MS = -1;
+ std::string rtpFilename = args.inputFile;
+ std::string outFilename = args.outputFile;
+ if (outFilename == "")
+ outFilename = "DecodeFromStorage.yuv";
+
+ FrameReceiveCallback receiveCallback(outFilename.c_str());
+
+ // END Settings
+
+ Trace::CreateTrace();
+ Trace::SetTraceFile("decodeFromStorageTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+
+
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ VideoCodingModule* vcmPlayback = VideoCodingModule::Create(2);
+ FrameStorageCallback storageCallback(vcmPlayback);
+ RtpDataCallback dataCallback(vcm);
+ WebRtc_Word32 ret = vcm->InitializeReceiver();
+ if (ret < 0)
+ {
+ return -1;
+ }
+ ret = vcmPlayback->InitializeReceiver();
+ if (ret < 0)
+ {
+ return -1;
+ }
+ vcm->RegisterFrameStorageCallback(&storageCallback);
+ vcmPlayback->RegisterReceiveCallback(&receiveCallback);
+ RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback);
+ ListWrapper payloadTypes;
+ payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8));
+
+ // Register receive codecs in VCM
+ ListItem* item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ VideoCodec codec;
+ memset(&codec, 0, sizeof(codec));
+ strncpy(codec.plName, payloadType->name.c_str(), payloadType->name.length());
+ codec.plName[payloadType->name.length()] = '\0';
+ codec.plType = payloadType->payloadType;
+ codec.codecType = payloadType->codecType;
+ if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
+ {
+ return -1;
+ }
+ if (vcmPlayback->RegisterReceiveCodec(&codec, 1) < 0)
+ {
+ return -1;
+ }
+ }
+ item = payloadTypes.Next(item);
+ }
+ if (rtpStream.Initialize(payloadTypes) < 0)
+ {
+ return -1;
+ }
+ bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack ||
+ protectionMethod == kProtectionDualDecoder);
+ rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
+ rtpStream.SetReordering(reordering);
+ vcm->SetChannelParameters(0, 0, rttMS);
+ vcm->SetVideoProtection(protectionMethod, protectionEnabled);
+ vcm->SetRenderDelay(renderDelayMs);
+ vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
+
+ ret = 0;
+
+ // RTP stream main loop
+ while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0)
+ {
+ if (VCMTickTime::MillisecondTimestamp() % 5 == 0)
+ {
+ ret = vcm->Decode();
+ if (ret < 0)
+ {
+ return -1;
+ }
+ }
+ if (vcm->TimeUntilNextProcess() <= 0)
+ {
+ vcm->Process();
+ }
+ if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS)
+ {
+ break;
+ }
+ VCMTickTime::IncrementDebugClock();
+ }
+
+ switch (ret)
+ {
+ case 1:
+ printf("Success\n");
+ break;
+ case -1:
+ printf("Failed\n");
+ break;
+ case 0:
+ printf("Timeout\n");
+ break;
+ }
+
+ rtpStream.Print();
+
+ // Tear down
+ item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ delete payloadType;
+ }
+ ListItem* itemToRemove = item;
+ item = payloadTypes.Next(item);
+ payloadTypes.Erase(itemToRemove);
+ }
+ VideoCodingModule::Destroy(vcm);
+ vcm = NULL;
+ VideoCodingModule::Destroy(vcmPlayback);
+ vcmPlayback = NULL;
+ Trace::ReturnTrace();
+
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/generic_codec_test.cc b/src/modules/video_coding/main/test/generic_codec_test.cc
new file mode 100644
index 0000000..7513b56
--- /dev/null
+++ b/src/modules/video_coding/main/test/generic_codec_test.cc
@@ -0,0 +1,584 @@
+/*
+ * 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 "generic_codec_test.h"
+#include <cmath>
+#include <stdio.h>
+#include "tick_time.h"
+#include "../source/event.h"
+#include "rtp_rtcp.h"
+#include "module_common_types.h"
+#include "test_util.h"
+
+using namespace webrtc;
+
+int GenericCodecTest::RunTest(CmdArgs& args)
+{
+ // Don't run this test with debug time
+#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
+ return -1;
+#endif
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ GenericCodecTest* get = new GenericCodecTest(vcm);
+ Trace::CreateTrace();
+ Trace::SetTraceFile("genericCodecTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ get->Perform(args);
+ Trace::ReturnTrace();
+ delete get;
+ VideoCodingModule::Destroy(vcm);
+ return 0;
+}
+
+GenericCodecTest::GenericCodecTest(VideoCodingModule* vcm):
+_width(0),
+_height(0),
+_timeStamp(0),
+_lengthSourceFrame(0),
+_frameRate(0),
+vcmMacrosTests(0),
+vcmMacrosErrors(0),
+_vcm(vcm)
+{
+}
+
+GenericCodecTest::~GenericCodecTest()
+{
+}
+
+void
+GenericCodecTest::Setup(CmdArgs& args)
+{
+ _timeStamp = 0;
+
+ /* Test Sequence parameters */
+
+ _inname= args.inputFile;
+ if (args.outputFile.compare(""))
+ _outname = "GCTest_decoded.yuv";
+ else
+ _outname = args.outputFile;
+ _encodedName = "../GCTest_encoded.vp8";
+ _width = args.width;
+ _height = args.height;
+ _frameRate = args.frameRate;
+ _lengthSourceFrame = 3*_width*_height/2;
+
+ /* File settings */
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+ if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ return;
+}
+WebRtc_Word32
+GenericCodecTest::Perform(CmdArgs& args)
+{
+ WebRtc_Word32 ret;
+ Setup(args);
+ /*
+ 1. sanity checks
+ 2. encode/decoder individuality
+ 3. API testing
+ 4. Target bitrate (within a specific timespan)
+ 5. Pipeline Delay
+ */
+
+ /*******************************/
+ /* sanity checks on inputs */
+ /*****************************/
+ VideoCodec sendCodec, receiveCodec;
+ sendCodec.maxBitrate = 8000;
+ TEST(_vcm->NumberOfCodecs() > 0); // This works since we now initialize the list in the constructor
+ TEST(_vcm->Codec(0, &sendCodec) == VCM_OK);
+ _vcm->InitializeSender();
+ _vcm->InitializeReceiver();
+ WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs();
+ // registration of first codec in the list
+ int i = 0;
+ _vcm->Codec(0, &_sendCodec);
+ TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1440) == VCM_OK);
+ // sanity on encoder registration
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ _vcm->InitializeSender();
+ TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0);
+ TEST(_vcm->RegisterSendCodec(&sendCodec, -1, 1440) < 0); // bad number of cores
+ sendCodec.maxBitrate = 8000;
+ _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
+ _vcm->InitializeSender();
+ _vcm->Codec(kVideoCodecVP8, &sendCodec);
+ sendCodec.height = 0;
+ TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height
+ _vcm->Codec(kVideoCodecVP8, &sendCodec);
+ sendCodec.startBitrate = -2;
+ TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad bit rate
+ _vcm->Codec(kVideoCodecVP8, &sendCodec);
+ _vcm->InitializeSender();
+ TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rate when encoder uninitialized
+ // register all availbale decoders -- need to have more for this test
+ for (i=0; i< NumberOfCodecs; i++)
+ {
+ _vcm->Codec(i, &receiveCodec);
+ _vcm->RegisterReceiveCodec(&receiveCodec, 1);
+ }
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ // building source frame
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ sourceFrame.SetTimeStamp(_timeStamp++);
+ // encode/decode
+ TEST(_vcm->AddVideoFrame(sourceFrame) < 0 ); // encoder uninitialized
+ _vcm->InitializeReceiver();
+ TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rtt when receiver uninitialized
+
+ /**************************************/
+ /* encoder/decoder individuality test */
+ /**************************************/
+ //Register both encoder and decoder, reset decoder - encode, set up decoder, reset encoder - decode.
+ rewind(_sourceFile);
+ sourceFrame.Free();
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ NumberOfCodecs = _vcm->NumberOfCodecs();
+ // Register VP8
+ _vcm->Codec(kVideoCodecVP8, &_sendCodec);
+ _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
+ _vcm->SendCodec(&sendCodec);
+ sendCodec.startBitrate = 2000;
+
+ // Set target frame rate to half of the incoming frame rate
+ // to test the frame rate control in the VCM
+ sendCodec.maxFramerate = (WebRtc_UWord8)(_frameRate / 2);
+ sendCodec.width = _width;
+ sendCodec.height = _height;
+ TEST(strncmp(_sendCodec.plName, "VP8", 3) == 0); // was VP8
+
+ _decodeCallback = new VCMDecodeCompleteCallback(_decodedFile);
+ _encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile);
+ _vcm->RegisterReceiveCallback(_decodeCallback);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
+
+ _vcm->RegisterSendCodec(&sendCodec, 4, 1440);
+ _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName));
+
+ _vcm->InitializeReceiver();
+ _vcm->Process();
+
+ //encoding 1 second of video
+ for (i = 0; i < _frameRate; i++)
+ {
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ IncrementDebugClock(_frameRate);
+ _vcm->Process();
+ }
+ sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate;
+ _vcm->InitializeSender();
+ TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); // same codec for encode and decode
+ ret = 0;
+ i = 0;
+ while ((i < 25) && (ret == 0) )
+ {
+ ret = _vcm->Decode();
+ TEST(ret == VCM_OK);
+ if (ret < 0)
+ {
+ printf("error in frame # %d \n", i);
+ }
+ IncrementDebugClock(_frameRate);
+ i++;
+ }
+ //TEST((ret == 0) && (i = 50));
+ if (ret == 0)
+ {
+ printf("Encoder/Decoder individuality test complete - View output files \n");
+ }
+ // last frame - not decoded
+ _vcm->InitializeReceiver();
+ TEST(_vcm->Decode() < 0); // frame to be encoded exists, decoder uninitialized
+
+
+ // Test key frame request on packet loss mode.
+ // This a frame as a key frame and fooling the receiver
+ // that the last packet was lost. The decoding will succeed,
+ // but the VCM will see a packet loss and request a new key frame.
+ VCMEncComplete_KeyReqTest keyReqTest_EncCompleteCallback(*_vcm);
+ KeyFrameReqTest frameTypeCallback;
+ _vcm->RegisterTransportCallback(&keyReqTest_EncCompleteCallback);
+ _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
+ _vcm->RegisterSendCodec(&sendCodec, 4, 1440);
+ _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName));
+ TEST(_vcm->SetVideoProtection(kProtectionKeyOnKeyLoss, true) == VCM_OK);
+ TEST(_vcm->RegisterFrameTypeCallback(&frameTypeCallback) == VCM_OK);
+ TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+
+ printf("API tests complete \n");
+
+ /*******************/
+ /* Bit Rate Tests */
+ /*****************/
+ /* Requirements:
+ * 1. OneSecReq = 15 % above/below target over a time period of 1s (_frameRate number of frames)
+ * 3. FullReq = 10% for total seq. (for 300 frames/seq. coincides with #1)
+ * 4. Test will go over all registered codecs
+ //NOTE: time requirements are not part of the release tests
+ */
+ double FullReq = 0.1;
+ double OneSecReq = 0.15;
+ printf("\n RATE CONTROL TEST\n");
+ // initializing....
+ _vcm->InitializeSender();
+ _vcm->InitializeReceiver();
+ rewind(_sourceFile);
+ sourceFrame.Free();
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ const float bitRate[] = {100, 400, 600, 1000, 2000, 3000};
+ const float nBitrates = sizeof(bitRate)/sizeof(*bitRate);
+ float _bitRate;
+ int _frameCnt = 0;
+ WebRtc_Word64 startTime, currentTime, oneSecTime;
+ float totalBytesOneSec;//, totalBytesTenSec;
+ float totalBytes, actualBitrate;
+ VCMFrameCount frameCount; // testing frame type counters
+ // start test
+ NumberOfCodecs = _vcm->NumberOfCodecs();
+ // going over all available codecs
+ _encodeCompleteCallback->SetFrameDimensions(_width, _height);
+ SendStatsTest sendStats;
+ for (int k = 0; k < NumberOfCodecs; k++)
+ //for (int k = NumberOfCodecs - 1; k >=0; k--)
+ {// static list starts from 0
+ //just checking
+ _vcm->InitializeSender();
+ _sendCodec.maxBitrate = 8000;
+ TEST(_vcm->Codec(k, &_sendCodec)== VCM_OK);
+ _vcm->RegisterSendCodec(&_sendCodec, 1, 1440);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ _encodeCompleteCallback->SetCodecType(ConvertCodecType(_sendCodec.plName));
+ printf (" \n\n Codec type = %s \n\n",_sendCodec.plName);
+ for (i = 0; i < nBitrates; i++)
+ {
+ _bitRate = static_cast<float>(bitRate[i]);
+ // just testing
+ _vcm->InitializeSender();
+ _sendCodec.startBitrate = (int)_bitRate;
+ _sendCodec.maxBitrate = 8000;
+ _vcm->RegisterSendCodec(&_sendCodec, 1, 1440);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ // up to here
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20);
+ _frameCnt = 0;
+ totalBytes = 0;
+ startTime = VCMTickTime::MicrosecondTimestamp();
+ _encodeCompleteCallback->Initialize();
+ sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
+ _vcm->RegisterSendStatisticsCallback(&sendStats);
+ while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0)
+ {
+ _frameCnt++;
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+
+ ret = _vcm->AddVideoFrame(sourceFrame);
+ IncrementDebugClock(_frameRate);
+ // The following should be uncommneted for timing tests. Release tests only include
+ // compliance with full sequence bit rate.
+
+
+ //totalBytes = WaitForEncodedFrame();
+ //currentTime = VCMTickTime::MillisecondTimestamp();//clock()/(double)CLOCKS_PER_SEC;
+ if (_frameCnt == _frameRate)// @ 1sec
+ {
+ oneSecTime = VCMTickTime::MicrosecondTimestamp();
+ totalBytesOneSec = _encodeCompleteCallback->EncodedBytes();//totalBytes;
+ }
+ TEST(_vcm->TimeUntilNextProcess() >= 0);
+ } // video seq. encode done
+ TEST(_vcm->TimeUntilNextProcess() == 0);
+ _vcm->Process(); // Let the module calculate its send bit rate estimate
+ // estimating rates
+ // complete sequence
+ // bit rate assumes input frame rate is as specified
+ currentTime = VCMTickTime::MicrosecondTimestamp();
+ totalBytes = _encodeCompleteCallback->EncodedBytes();
+ actualBitrate = (float)(8.0/1000)*(totalBytes / (_frameCnt / _frameRate));
+ WebRtc_Word64 timeDiff = (currentTime - startTime)/1000;
+ //actualBitrate = (float)(8.0*totalBytes)/timeDiff;
+ printf("Complete Seq.: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate);
+ TEST((fabs(actualBitrate - _bitRate) < FullReq * _bitRate) ||
+ (strncmp(_sendCodec.plName, "I420", 4) == 0));
+
+ // 1 Sec.
+ actualBitrate = (float)(8.0/1000)*(totalBytesOneSec);
+ //actualBitrate = (float)(8.0*totalBytesOneSec)/(oneSecTime - startTime);
+ //printf("First 1Sec: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate);
+ //TEST(fabs(actualBitrate - _bitRate) < OneSecReq * _bitRate);
+ rewind(_sourceFile);
+
+ //checking key/delta frame count
+ _vcm->SentFrameCount(frameCount);
+ printf("frame count: %d delta, %d key\n", frameCount.numDeltaFrames, frameCount.numKeyFrames);
+ }// end per codec
+
+ } // end rate control test
+ /********************************/
+ /* Encoder Pipeline Delay Test */
+ /******************************/
+ WebRtc_Word32 retVal;
+ _vcm->InitializeSender();
+ sourceFrame.Free();
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ NumberOfCodecs = _vcm->NumberOfCodecs();
+ bool encodeComplete = false;
+ // going over all available codecs
+ for (int k = 0; k < NumberOfCodecs; k++)
+ {
+ retVal = _vcm->Codec(k, &_sendCodec);
+ retVal = _vcm->InitializeSender();
+ _sendCodec.maxBitrate = 8000;
+ retVal = _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
+ retVal = _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+
+ _frameCnt = 0;
+ encodeComplete = false;
+ while (encodeComplete == false)
+ {
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ _frameCnt++;
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+ retVal = _vcm->AddVideoFrame(sourceFrame);
+ encodeComplete = _encodeCompleteCallback->EncodeComplete();
+ } // first frame encoded
+ printf ("\n Codec type = %s \n", _sendCodec.plName);
+ printf(" Encoder pipeline delay = %d frames\n", _frameCnt - 1);
+ } // end for all codecs
+
+ /********************************/
+ /* Encoder Packet Size Test */
+ /********************************/
+ RtpRtcp& rtpModule = *RtpRtcp::CreateRtpRtcp(1, false);
+ TEST(rtpModule.InitSender() == 0);
+ RTPSendCallback_SizeTest sendCallback;
+ rtpModule.RegisterSendTransport(&sendCallback);
+
+ VCMRTPEncodeCompleteCallback encCompleteCallback(&rtpModule);
+ _vcm->InitializeSender();
+
+ // TEST DISABLED FOR NOW SINCE VP8 DOESN'T HAVE THIS FEATURE
+
+// sourceFrame.Free();
+// sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+// NumberOfCodecs = _vcm->NumberOfCodecs();
+// WebRtc_UWord32 targetPayloadSize = 500;
+// rtpModule.SetMaxTransferUnit(targetPayloadSize);
+// // going over all available codecs
+// for (int k = 0; k < NumberOfCodecs; k++)
+// {
+// _vcm->Codec(k, &_sendCodec);
+// if (strncmp(_sendCodec.plName, "VP8", 3) == 0)
+// {
+// // Only test with VP8
+// continue;
+// }
+// rtpModule.RegisterSendPayload(_sendCodec.plName, _sendCodec.plType);
+// // Make sure we only get one NAL unit per packet
+// _vcm->InitializeSender();
+// _vcm->RegisterSendCodec(&_sendCodec, 4, targetPayloadSize);
+// sendCallback.SetMaxPayloadSize(targetPayloadSize);
+// _vcm->RegisterTransportCallback(&encCompleteCallback);
+// sendCallback.Reset();
+// _frameCnt = 0;
+// rewind(_sourceFile);
+// while (!feof(_sourceFile))
+// {
+// fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+// _frameCnt++;
+// sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+// sourceFrame.SetHeight(_height);
+// sourceFrame.SetWidth(_width);
+// _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+// sourceFrame.SetTimeStamp(_timeStamp);
+// ret = _vcm->AddVideoFrame(sourceFrame);
+// } // first frame encoded
+// printf ("\n Codec type = %s \n",_sendCodec.plName);
+// printf(" Average payload size = %f bytes, target = %u bytes\n", sendCallback.AveragePayloadSize(), targetPayloadSize);
+// } // end for all codecs
+
+
+ // Test temporal decimation settings
+ for (int k = 0; k < NumberOfCodecs; k++)
+ {
+ _vcm->Codec(k, &_sendCodec);
+ if (strncmp(_sendCodec.plName, "I420", 4) == 0)
+ {
+ // Only test with I420
+ break;
+ }
+ }
+ TEST(strncmp(_sendCodec.plName, "I420", 4) == 0);
+ _vcm->InitializeSender();
+ _sendCodec.maxFramerate = static_cast<WebRtc_UWord8>(_frameRate / 2.0 + 0.5f);
+ _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
+ _vcm->SetChannelParameters(2000, 0, 0);
+ _vcm->RegisterTransportCallback(_encodeCompleteCallback);
+ // up to here
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20);
+ _encodeCompleteCallback->Initialize();
+ sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
+ _vcm->RegisterSendStatisticsCallback(&sendStats);
+ rewind(_sourceFile);
+ while (!feof(_sourceFile))
+ {
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+ ret = _vcm->AddVideoFrame(sourceFrame);
+ if (_vcm->TimeUntilNextProcess() <= 0)
+ {
+ _vcm->Process();
+ }
+ IncrementDebugClock(_frameRate);
+ } // first frame encoded
+
+ RtpRtcp::DestroyRtpRtcp(&rtpModule);
+ Print();
+ delete tmpBuffer;
+ delete _decodeCallback;
+ delete _encodeCompleteCallback;
+ return 0;
+}
+
+
+void
+GenericCodecTest::Print()
+{
+ printf(" \n\n VCM Generic Encoder Test: \n\n%i tests completed\n", vcmMacrosTests);
+ if (vcmMacrosErrors > 0)
+ {
+ printf("%i FAILED\n\n", vcmMacrosErrors);
+ }
+ else
+ {
+ printf("ALL PASSED\n\n");
+ }
+}
+
+float
+GenericCodecTest::WaitForEncodedFrame() const
+{
+ WebRtc_Word64 startTime = TickTime::MillisecondTimestamp();
+ while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitEncTimeMs*10)
+ {
+ if (_encodeCompleteCallback->EncodeComplete())
+ {
+ return _encodeCompleteCallback->EncodedBytes();
+ }
+ }
+ return 0;
+}
+
+void
+GenericCodecTest::IncrementDebugClock(float frameRate)
+{
+ for (int t= 0; t < 1000/frameRate; t++)
+ {
+ VCMTickTime::IncrementDebugClock();
+ }
+ return;
+}
+
+int
+RTPSendCallback_SizeTest::SendPacket(int channel, const void *data, int len)
+{
+ _nPackets++;
+ _payloadSizeSum += len;
+ // Make sure no payloads (len - header size) are larger than maxPayloadSize
+ TEST(len > 0 && static_cast<WebRtc_UWord32>(len - 12) <= _maxPayloadSize);
+ return 0;
+}
+
+void
+RTPSendCallback_SizeTest::SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize)
+{
+ _maxPayloadSize = maxPayloadSize;
+}
+
+void
+RTPSendCallback_SizeTest::Reset()
+{
+ _nPackets = 0;
+ _payloadSizeSum = 0;
+}
+
+float
+RTPSendCallback_SizeTest::AveragePayloadSize() const
+{
+ if (_nPackets > 0)
+ {
+ return _payloadSizeSum / static_cast<float>(_nPackets);
+ }
+ return 0;
+}
+
+WebRtc_Word32
+VCMEncComplete_KeyReqTest::SendData(const FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const RTPFragmentationHeader& fragmentationHeader)
+{
+ WebRtcRTPHeader rtpInfo;
+ rtpInfo.header.markerBit = true; // end of frame
+ rtpInfo.type.Video.codec = kRTPVideoVP8;
+ rtpInfo.header.payloadType = payloadType;
+ rtpInfo.header.sequenceNumber = _seqNo;
+ _seqNo += 2;
+ rtpInfo.header.ssrc = 0;
+ rtpInfo.header.timestamp = _timeStamp;
+ _timeStamp += 3000;
+ rtpInfo.type.Video.isFirstPacket = false;
+ rtpInfo.frameType = kVideoFrameKey;
+ return _vcm.IncomingPacket(payloadData, payloadSize, rtpInfo);
+}
diff --git a/src/modules/video_coding/main/test/generic_codec_test.h b/src/modules/video_coding/main/test/generic_codec_test.h
new file mode 100644
index 0000000..fa1473a
--- /dev/null
+++ b/src/modules/video_coding/main/test/generic_codec_test.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
+
+#include "video_coding.h"
+#include "test_macros.h"
+#include "test_util.h"
+
+#include <string.h>
+#include <fstream>
+
+/*
+Test consists of:
+1. Sanity checks
+2. Bit rate validation
+3. Encoder control test / General API functionality
+4. Decoder control test / General API functionality
+
+*/
+int VCMGenericCodecTest(CmdArgs& args);
+
+class GenericCodecTest
+{
+public:
+ GenericCodecTest(webrtc::VideoCodingModule* vcm);
+ ~GenericCodecTest();
+ static int RunTest(CmdArgs& args);
+ WebRtc_Word32 Perform(CmdArgs& args);
+ float WaitForEncodedFrame() const;
+
+private:
+ void Setup(CmdArgs& args);
+ void Print();
+ WebRtc_Word32 TearDown();
+ void IncrementDebugClock(float frameRate);
+
+ webrtc::VideoCodingModule* _vcm;
+ webrtc::VideoCodec _sendCodec;
+ webrtc::VideoCodec _receiveCodec;
+ std::string _inname;
+ std::string _outname;
+ std::string _encodedName;
+ WebRtc_Word32 _sumEncBytes;
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ FILE* _encodedFile;
+ WebRtc_UWord16 _width;
+ WebRtc_UWord16 _height;
+ float _frameRate;
+ WebRtc_UWord32 _lengthSourceFrame;
+ WebRtc_UWord32 _timeStamp;
+ int vcmMacrosTests;
+ int vcmMacrosErrors;
+ VCMDecodeCompleteCallback* _decodeCallback;
+ VCMEncodeCompleteCallback* _encodeCompleteCallback;
+
+}; // end of GenericCodecTest class definition
+
+class RTPSendCallback_SizeTest : public webrtc::Transport
+{
+public:
+ // constructor input: (receive side) rtp module to send encoded data to
+ RTPSendCallback_SizeTest() : _maxPayloadSize(0), _payloadSizeSum(0), _nPackets(0) {}
+ virtual int SendPacket(int channel, const void *data, int len);
+ virtual int SendRTCPPacket(int channel, const void *data, int len) {return 0;}
+ void SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize);
+ void Reset();
+ float AveragePayloadSize() const;
+private:
+ WebRtc_UWord32 _maxPayloadSize;
+ WebRtc_UWord32 _payloadSizeSum;
+ WebRtc_UWord32 _nPackets;
+};
+
+class VCMEncComplete_KeyReqTest : public webrtc::VCMPacketizationCallback
+{
+public:
+ VCMEncComplete_KeyReqTest(webrtc::VideoCodingModule &vcm) : _vcm(vcm), _seqNo(0), _timeStamp(0) {}
+ WebRtc_Word32 SendData(const webrtc::FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const webrtc::RTPFragmentationHeader& fragmentationHeader);
+private:
+ webrtc::VideoCodingModule& _vcm;
+ WebRtc_UWord16 _seqNo;
+ WebRtc_UWord32 _timeStamp;
+}; // end of VCMEncodeCompleteCallback
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
diff --git a/src/modules/video_coding/main/test/jitter_buffer_test.cc b/src/modules/video_coding/main/test/jitter_buffer_test.cc
new file mode 100644
index 0000000..87d708e
--- /dev/null
+++ b/src/modules/video_coding/main/test/jitter_buffer_test.cc
@@ -0,0 +1,2252 @@
+/*
+ * 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 "common_types.h"
+#include "jitter_buffer.h"
+#include "jitter_estimator.h"
+#include "inter_frame_delay.h"
+#include "packet.h"
+#include "tick_time.h"
+#include "../source/event.h"
+#include "frame_buffer.h"
+#include "jitter_estimate_test.h"
+#include "test_macros.h"
+#include "test_util.h"
+#include <stdio.h>
+#include <math.h>
+
+using namespace webrtc;
+
+void CheckOutFrame(VCMEncodedFrame* frameOut, int size, bool startCode)
+{
+ TEST(frameOut != 0);
+
+ const WebRtc_UWord8* outData = frameOut->Buffer();
+
+ int i = 0;
+
+ if(startCode)
+ {
+ TEST(outData[0] == 0);
+ TEST(outData[1] == 0);
+ TEST(outData[2] == 0);
+ TEST(outData[3] == 1);
+ i+= 4;
+ }
+
+ // check the frame data
+ int count = 3;
+ int layer = 0;
+
+ // check the frame length
+ TEST(frameOut->Length() == size);
+
+ for(; i < size; i++)
+ {
+ if (outData[i] == 0 && outData[i+1] == 0 && outData[i+2] == 0x80)
+ {
+ i += 2;
+ }
+ else if(startCode && outData[i] == 0 && outData[i+1] == 0)
+ {
+ TEST(outData[i] == 0);
+ TEST(outData[i+1] == 0);
+ TEST(outData[i+2] == 0);
+ TEST(outData[i+3] == 1);
+ i += 3;
+ }
+ else
+ {
+ if (outData[i] != count)
+ {
+ int a=0;
+ }
+ TEST(outData[i] == count);
+ count++;
+ if(count == 10)
+ {
+ count = 3;
+ }
+ }
+ }
+}
+
+
+int JitterBufferTest(CmdArgs& args)
+{
+ // Don't run these tests with debug time
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ return -1;
+#endif
+
+ // Start test
+ WebRtc_UWord16 seqNum = 1234;
+ WebRtc_UWord32 timeStamp = 0;
+ int size = 1400;
+ WebRtc_UWord8 data[1500];
+ VCMPacket packet(data, size, seqNum, timeStamp, true);
+
+ VCMFrameListTimestampOrderAsc frameList;
+ VCMFrameBuffer* fb = NULL;
+ for (int i=0; i < 100; i++)
+ {
+ fb = new VCMFrameBuffer();
+ fb->SetState(kStateEmpty);
+ packet.timestamp = 0xfffffff0 + i;
+ packet.seqNum = seqNum;
+ packet.payloadType = 126;
+ seqNum++;
+ fb->InsertPacket(packet, VCMTickTime::MillisecondTimestamp());
+ TEST(frameList.Insert(fb) == 0);
+ }
+ VCMFrameListItem* item = NULL;
+ WebRtc_UWord32 prevTimestamp = 0;
+ int i = 0;
+ for (i=0; !frameList.Empty(); i++)
+ {
+ item = frameList.First();
+ fb = static_cast<VCMFrameBuffer*>(item->GetItem());
+ TEST(i > 0 || fb->TimeStamp() == 0xfffffff0); // Frame 0 has no prev
+ TEST(prevTimestamp - fb->TimeStamp() == -1 || i == 0);
+ prevTimestamp = fb->TimeStamp();
+ frameList.Erase(item);
+ delete fb;
+ }
+ TEST(i == 100);
+
+ //printf("DONE timestamp ordered frame list\n");
+
+ VCMJitterBuffer jb;
+
+ seqNum = 1234;
+ timeStamp = 123*90;
+ VCMFrameBufferEnum retVal(kNoError);
+ FrameType incomingFrameType(kVideoFrameKey);
+ VCMEncodedFrame* frameOut=NULL;
+ WebRtc_Word64 renderTimeMs = 0;
+ WebRtc_UWord8 payloadType = 0;
+ packet.timestamp = timeStamp;
+ packet.seqNum = seqNum;
+
+ // build a data vector with 0, 0, 0x80, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0x80, 3....
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0x80;
+ int count = 3;
+ for(int i = 3; i < sizeof(data) - 3; ++i )
+ {
+ data[i] = count;
+ count++;
+ if(count == 10)
+ {
+ data[i+1] = 0;
+ data[i+2] = 0;
+ data[i+3] = 0x80;
+ count = 3;
+ i += 3;
+ }
+ }
+
+ // Test out of range inputs
+ TEST(kSizeError == jb.InsertPacket(0, packet));
+ jb.ReleaseFrame(0);
+
+ // Not started
+ TEST(0 == jb.GetFrame(packet));
+ TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(0 == jb.GetCompleteFrameForDecoding(10));
+ TEST(0 == jb.GetFrameForDecoding());
+
+ // Start
+ jb.Start();
+
+ // Get frame to use for this timestamp
+ VCMEncodedFrame* frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // No packets inserted
+ TEST(0 == jb.GetCompleteFrameForDecoding(10));
+
+
+ //
+ // TEST single packet frame
+ //
+ // --------
+ // | 1234 |
+ // --------
+
+ // packet.frameType;
+ // packet.dataPtr;
+ // packet.sizeBytes;
+ // packet.timestamp;
+ // packet.seqNum;
+ // packet.isFirstPacket;
+ // packet.markerBit;
+ //
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 1 packet\n");
+
+ //
+ // TEST dual packet frame
+ //
+ // -----------------
+ // | 1235 | 1236 |
+ // -----------------
+ //
+
+ seqNum++;
+ timeStamp += 33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 2 packets\n");
+
+
+ //
+ // TEST 100 packets frame Key frame
+ //
+ // ----------------------------------
+ // | 1237 | 1238 | .... | 1336 |
+ // ----------------------------------
+
+ // insert first packet
+ timeStamp += 33*90;
+ seqNum++;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameKey);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 frames
+ int loop = 0;
+ do
+ {
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*100, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameKey);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE key frame 100 packets\n");
+
+ //
+ // TEST 100 packets frame Delta frame
+ //
+ // ----------------------------------
+ // | 1337 | 1238 | .... | 1436 |
+ // ----------------------------------
+
+ // insert first packet
+ timeStamp += 33*90;
+ seqNum++;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 frames
+ loop = 0;
+ do
+ {
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*100, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 100 packets\n");
+
+ //
+ // TEST packet re-ordering reverse order
+ //
+ // ----------------------------------
+ // | 1437 | 1238 | .... | 1536 |
+ // ----------------------------------
+ // <----------
+
+ // insert "first" packet last seqnum
+ timeStamp += 33*90;
+ seqNum += 100;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 packets
+ loop = 0;
+ do
+ {
+ seqNum--;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum--;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*100, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 100 packets reverse order\n");
+
+ seqNum+= 100;
+
+ //
+ // TEST frame re-ordering 2 frames 2 packets each
+ //
+ // ----------------- -----------------
+ // | 1539 | 1540 | | 1537 | 1538 |
+ // ----------------- -----------------
+
+ seqNum += 2;
+ timeStamp += 2* 33 * 90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // check that we fail to get frame since seqnum is not continuous
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(frameOut == 0);
+
+ seqNum -= 3;
+ timeStamp -= 33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ seqNum += 2;
+ //printf("DONE frame re-ordering 2 frames 2 packets\n");
+
+ //
+ // TEST H.263 bits
+ //
+ // -----------------
+ // | 1541 | 1542 |
+ // -----------------
+ // sBits
+
+ seqNum++;
+ timeStamp += 2*33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.bits = false;
+ packet.codec = kVideoCodecH263;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.bits = true;
+ packet.dataPtr = &(data[9]);
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, (size*2)-1, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ // restore
+ packet.dataPtr = data;
+ packet.bits = false;
+ packet.codec = kVideoCodecUnknown;
+ //printf("DONE H.263 frame 2 packets with bits\n");
+
+ //
+ // TEST duplicate packets
+ //
+ // -----------------
+ // | 1543 | 1543 |
+ // -----------------
+ //
+
+ seqNum++;
+ timeStamp += 33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kDuplicatePacket == jb.InsertPacket(frameIn, packet));
+
+ seqNum++;
+ packet.seqNum = seqNum;
+
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE test duplicate packets\n");
+
+ //
+ // TEST H.264 insert start code
+ //
+ // -----------------
+ // | 1544 | 1545 |
+ // -----------------
+ // insert start code, both packets
+
+ seqNum++;
+ timeStamp += 33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.insertStartCode = true;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2+4*2, true);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ // reset
+ packet.insertStartCode = false;
+ //printf("DONE H.264 insert start code test 2 packets\n");
+
+
+ // Temporarily do this to make the rest of the test work:
+ timeStamp += 33*90;
+ seqNum += 4;
+
+ //
+ // TEST statistics
+ //
+ WebRtc_UWord32 numDeltaFrames = 0;
+ WebRtc_UWord32 numKeyFrames = 0;
+ TEST(jb.GetFrameStatistics(numDeltaFrames, numKeyFrames) == 0);
+
+ TEST(numDeltaFrames == 9);
+ TEST(numKeyFrames == 1);
+
+ WebRtc_UWord32 frameRate;
+ WebRtc_UWord32 bitRate;
+ TEST(jb.GetUpdate(frameRate, bitRate) == 0);
+
+ // these depend on CPU speed works on a T61
+ TEST(frameRate > 30);
+ TEST(bitRate > 10000000);
+
+ //printf("DONE Statistics\n");
+
+ //
+ // TEST delta frame 100 packets with seqNum wrap
+ //
+ // ---------------------------------------
+ // | 65520 | 65521 | ... | 82 | 83 |
+ // ---------------------------------------
+ //
+
+ // test flush
+ jb.Flush();
+
+ // insert first packet
+ timeStamp += 33*90;
+ seqNum = 0xfff0;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 packets
+ loop = 0;
+ do
+ {
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(2, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(2);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*100, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 100 packets with wrap in seqNum\n");
+
+ //
+ // TEST packet re-ordering reverse order with neg seqNum warp
+ //
+ // ----------------------------------------
+ // | 65447 | 65448 | ... | 9 | 10 |
+ // ----------------------------------------
+ // <-------------
+
+ // test flush
+ jb.Flush();
+
+ // insert "first" packet last seqnum
+ timeStamp += 33*90;
+ seqNum = 10;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 frames
+ loop = 0;
+ do
+ {
+ seqNum--;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(2, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(2);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum--;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*100, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 100 packets reverse order with wrap in seqNum \n");
+
+ // test flush
+ jb.Flush();
+
+ //
+ // TEST packet re-ordering with seqNum wrap
+ //
+ // -----------------------
+ // | 1 | 65535 | 0 |
+ // -----------------------
+
+ // insert "first" packet last seqnum
+ timeStamp += 33*90;
+ seqNum = 1;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert last packet
+ seqNum -= 2;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*3, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE delta frame 3 packets re-ordering with wrap in seqNum \n");
+
+ // test flush
+ jb.Flush();
+
+ //
+ // TEST insert old frame
+ //
+ // ------- -------
+ // | 2 | | 1 |
+ // ------- -------
+ // t = 3000 t = 2000
+
+ seqNum = 2;
+ timeStamp = 3000;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(3000 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(3000 == frameOut->TimeStamp());
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ seqNum--;
+ timeStamp = 2000;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ // Changed behavior, never insert packets into frames older than the
+ // last decoded frame.
+ TEST(frameIn == 0);
+
+ //printf("DONE insert old frame\n");
+
+ jb.Flush();
+
+ //
+ // TEST insert old frame with wrap in timestamp
+ //
+ // ------- -------
+ // | 2 | | 1 |
+ // ------- -------
+ // t = 3000 t = 0xffffff00
+
+ seqNum = 2;
+ timeStamp = 3000;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(timeStamp == frameOut->TimeStamp());
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ seqNum--;
+ timeStamp = 0xffffff00;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ // This timestamp is old
+ TEST(frameIn == 0);
+
+ jb.Flush();
+
+ //
+ // TEST wrap in timeStamp
+ //
+ // --------------- ---------------
+ // | 1 | 2 | | 3 | 4 |
+ // --------------- ---------------
+ // t = 0xffffff00 t = 33*90
+
+ seqNum = 1;
+ timeStamp = 0xffffff00;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ seqNum++;
+ timeStamp += 33*90;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size*2, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ //printf("DONE time stamp wrap 2 frames 2 packets\n");
+
+ jb.Flush();
+
+ //
+ // TEST insert 2 frames with wrap in timeStamp
+ //
+ // ------- -------
+ // | 1 | | 2 |
+ // ------- -------
+ // t = 0xffffff00 t = 2700
+
+ seqNum = 1;
+ timeStamp = 0xffffff00;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert first frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // Get packet notification
+ TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Insert next frame
+ seqNum++;
+ timeStamp = 2700;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // Get packet notification
+ TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(0xffffff00 == frameOut->TimeStamp());
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // Get packet notification
+ TEST(2700 == jb.GetNextTimeStamp(0, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get frame
+ VCMEncodedFrame* frameOut2 = jb.GetCompleteFrameForDecoding(10);
+ TEST(2700 == frameOut2->TimeStamp());
+
+ CheckOutFrame(frameOut2, size, false);
+
+ // check the frame type
+ TEST(frameOut2->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+ jb.ReleaseFrame(frameOut2);
+
+ //printf("DONE insert 2 frames (1 packet) with wrap in timestamp\n");
+
+ jb.Flush();
+
+ //
+ // TEST insert 2 frames re-ordered with wrap in timeStamp
+ //
+ // ------- -------
+ // | 2 | | 1 |
+ // ------- -------
+ // t = 2700 t = 0xffffff00
+
+ seqNum = 2;
+ timeStamp = 2700;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert first frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // Get packet notification
+ TEST(2700 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Insert second frame
+ seqNum--;
+ timeStamp = 0xffffff00;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // Get packet notification
+ TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(0xffffff00 == frameOut->TimeStamp());
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameDelta);
+
+ // get packet notification
+ TEST(2700 == jb.GetNextTimeStamp(0, incomingFrameType, renderTimeMs));
+ TEST(kVideoFrameDelta == incomingFrameType);
+
+ // Get frame
+ frameOut2 = jb.GetCompleteFrameForDecoding(10);
+ TEST(2700 == frameOut2->TimeStamp());
+
+ CheckOutFrame(frameOut2, size, false);
+
+ // check the frame type
+ TEST(frameOut2->FrameType() == kVideoFrameDelta);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+ jb.ReleaseFrame(frameOut2);
+
+ //printf("DONE insert 2 frames (1 packet) re-ordered with wrap in timestamp\n");
+
+ //
+ // TEST NACK
+ //
+ // ---------------------------------------------------------------------------------------------
+ // | 3 | 4 | 5 | 6 | 7 | 9 | x | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | x | 21 |.....| 102 |
+ // ---------------------------------------------------------------------------------------------
+ jb.SetNackMode(kNackInfinite);
+
+ TEST(jb.GetNackMode() == kNackInfinite);
+
+ // insert first packet
+ timeStamp += 33*90;
+ seqNum += 2;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameKey);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 packets
+ loop = 0;
+ do
+ {
+ seqNum++;
+ if(seqNum % 10 != 0)
+ {
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ }
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // try to get the frame, should fail
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(frameOut == 0);
+
+ // try to get the frame, should fail
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == 0);
+
+ WebRtc_UWord16 nackSize = 0;
+ bool extended = false;
+ WebRtc_UWord16* list = jb.GetNackList(nackSize, extended);
+
+ TEST(nackSize == 10);
+
+ for(int i = 0; i < nackSize; i++)
+ {
+ TEST(list[i] == (1+i)*10);
+ }
+
+ jb.Stop();
+
+ //printf("DONE NACK\n");
+
+ //
+ // TEST NACK with wrap in seqNum
+ //
+ // ------- -----------------------------------------------------------------------------------
+ // | 65532 | | 65533 | 65534 | 65535 | x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | x | 11 |.....| 96 |
+ // ------- -----------------------------------------------------------------------------------
+
+ jb.Flush();
+ jb.Start();
+
+ // insert first frame
+ timeStamp = 33*90;
+ seqNum = 65532;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameKey);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameKey);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+
+ // insert first packet
+ timeStamp += 33*90;
+ seqNum++;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+
+ // it should not be complete
+ TEST(frameOut == 0);
+
+ // insert 98 packets
+ loop = 0;
+ do
+ {
+ seqNum++;
+ if (seqNum % 10 != 0)
+ {
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // try to get the frame, should fail
+ frameOut = jb.GetCompleteFrameForDecoding(1);
+ TEST(frameOut == 0);
+
+ // try to get the frame, should fail
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == 0);
+ }
+ loop++;
+ } while (loop < 98);
+
+ // insert last packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // try to get the frame, should fail
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(frameOut == 0);
+
+ // try to get the frame, should fail
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == 0);
+
+ nackSize = 0;
+ list = jb.GetNackList(nackSize, extended);
+
+ TEST(nackSize == 10);
+
+ for(int i = 0; i < nackSize; i++)
+ {
+ TEST(list[i] == i*10);
+ }
+
+ jb.Stop();
+
+ //printf("DONE NACK with wrap in seqNum\n");
+
+ //
+ // TEST delta frame with more than max number of packets
+ //
+
+ jb.Start();
+
+ loop = 0;
+ packet.timestamp += 33*90;
+ bool firstPacket = true;
+ // insert kMaxPacketsInJitterBuffer into frame
+ do
+ {
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert frame
+ if (firstPacket)
+ {
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ firstPacket = false;
+ }
+ else
+ {
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ }
+
+ // get packet notification
+ TEST(packet.timestamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ loop++;
+ } while (loop < kMaxPacketsInJitterBuffer);
+
+ // Max number of packets inserted
+
+ // Insert one more packet
+ seqNum++;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert the packet -> frame recycled
+ TEST(kSizeError == jb.InsertPacket(frameIn, packet));
+
+ // should fail
+ TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(0 == jb.GetCompleteFrameForDecoding(10));
+
+ //printf("DONE fill frame - packets > max number of packets\n");
+
+ //
+ // TEST fill JB with more than max number of delta frame
+ //
+
+ loop = 0;
+ WebRtc_UWord32 timeStampStart = timeStamp + 33*90;
+ // insert MAX_NUMBER_OF_FRAMES frames
+ do
+ {
+ timeStamp += 33*90;
+ seqNum++;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ // Insert frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // get packet notification
+ TEST(timeStampStart == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ loop++;
+ } while (loop < kMaxNumberOfFrames);
+
+ // Max number of frames inserted
+
+ // Insert one more frame
+ timeStamp += 33*90;
+ seqNum++;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+
+ // Now, no free frame - frames will be recycled until first key frame
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn); // no key, so we have recycled all frames
+
+ //printf("DONE fill JB - number of delta frames > max number of frames\n");
+
+ //
+ // TEST fill JB with more than max number of frame (50 delta frames +
+ // 51 key frames) with wrap in seqNum
+ //
+ // --------------------------------------------------------------
+ // | 65485 | 65486 | 65487 | .... | 65535 | 0 | 1 | 2 | .....| 50 |
+ // --------------------------------------------------------------
+ // |<-----------delta frames------------->|<------key frames----->|
+
+ jb.Flush();
+
+ loop = 0;
+ seqNum = 65485;
+ timeStampStart = timeStamp + 33*90;
+ WebRtc_UWord32 timeStampFirstKey = 0;
+ VCMEncodedFrame* ptrLastDeltaFrame;
+ VCMEncodedFrame* ptrFirstKeyFrame;
+ // insert MAX_NUMBER_OF_FRAMES frames
+ do
+ {
+ timeStamp += 33*90;
+ seqNum++;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ if (loop == 49) // last delta
+ {
+ ptrLastDeltaFrame = frameIn;
+ }
+ if (loop == 50) // first key
+ {
+ ptrFirstKeyFrame = frameIn;
+ packet.frameType = kVideoFrameKey;
+ timeStampFirstKey = packet.timestamp;
+ }
+
+ // Insert frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // Get packet notification, should be first inserted frame
+ TEST(timeStampStart == jb.GetNextTimeStamp(10, incomingFrameType,
+ renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameDelta);
+
+ loop++;
+ } while (loop < kMaxNumberOfFrames);
+
+ // Max number of frames inserted
+
+ // Insert one more frame
+ timeStamp += 33*90;
+ seqNum++;
+ packet.isFirstPacket = true;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+
+ // Now, no free frame - frames will be recycled until first key frame
+ frameIn = jb.GetFrame(packet);
+ // ptr to last inserted delta frame should be returned
+ TEST(frameIn != 0 && frameIn && ptrLastDeltaFrame);
+
+ // Insert frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ // First inserted key frame should be oldest in buffer
+ TEST(timeStampFirstKey == jb.GetNextTimeStamp(10, incomingFrameType,
+ renderTimeMs));
+
+ // check incoming frame type
+ TEST(incomingFrameType == kVideoFrameKey);
+
+ // get the first key frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ TEST(ptrFirstKeyFrame == frameOut);
+
+ CheckOutFrame(frameOut, size, false);
+
+ // check the frame type
+ TEST(frameOut->FrameType() == kVideoFrameKey);
+
+ // Release frame (when done with decoding)
+ jb.ReleaseFrame(frameOut);
+
+ jb.Flush();
+
+ // printf("DONE fill JB - nr of delta + key frames (w/ wrap in seqNum) >
+ // max nr of frames\n");
+
+ // Test handling empty packets
+ // first insert 2 empty packets
+ jb.ReleaseFrame(frameIn);
+ timeStamp = 33 * 90;
+ seqNum = 5;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kFrameEmpty;
+ frameIn = jb.GetFrame(packet);
+ TEST(frameIn);
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ seqNum = 6;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kFrameEmpty;
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ // now insert the first data packet
+ seqNum = 1;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kVideoFrameDelta;
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ // insert an additional data packet
+ seqNum = 2;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kVideoFrameDelta;
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ // insert the last packet and verify frame completness
+ // (even though packet 4 (empty) is missing)
+ seqNum = 3;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kVideoFrameDelta;
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+ jb.Flush();
+
+ // testing that empty packets do not clog the jitter buffer
+ // Set hybrid mode
+ jb.SetNackMode(kNackHybrid);
+ TEST(jb.GetNackMode() == kNackHybrid);
+
+ int maxSize = 100;
+ seqNum = 3;
+ VCMEncodedFrame* testFrame;
+ for (int i = 0; i < maxSize + 10; i++)
+ {
+ timeStamp += 33 * 90;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kFrameEmpty;
+ testFrame = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+ TEST(kFirstPacket == jb.InsertPacket(testFrame, packet));
+ }
+ // verify insertion of a data packet (old empty frames will be flushed)
+ timeStamp += 33 * 90;
+ packet.isFirstPacket = true;
+ packet.markerBit = false;
+ packet.seqNum = seqNum;
+ packet.timestamp = timeStamp;
+ packet.frameType = kFrameEmpty;
+ testFrame = jb.GetFrame(packet);
+ TEST(frameIn != 0);
+
+ jb.SetNackMode(kNoNack);
+
+
+ // printf(DONE testing inserting empty packets to the JB)
+
+
+ // H.264 tests
+ //Test incomplete NALU frames
+
+ jb.Flush();
+ jb.SetNackMode(kNoNack);
+ seqNum ++;
+ timeStamp += 33*90;
+ int insertedLength=0;
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.completeNALU=kNaluStart;
+ packet.markerBit=false;
+
+ frameIn=jb.GetFrame(packet);
+
+ // Insert a packet into a frame
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ seqNum+=2; // Skip one packet
+ packet.seqNum=seqNum;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluIncomplete;
+ packet.markerBit=false;
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ seqNum++;
+ packet.seqNum=seqNum;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluEnd;
+ packet.markerBit=false;
+
+ // Insert a packet into a frame
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+ seqNum++;
+ packet.seqNum=seqNum;
+ packet.completeNALU=kNaluComplete;
+ packet.markerBit=true; // Last packet
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+
+
+ // get packet notification
+ TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ frameOut = jb.GetFrameForDecoding();
+
+ // We can decode everything from a NALU until a packet has been lost.
+ // Thus we can decode the first packet of the first NALU and the second NALU
+ // which consists of one packet.
+ CheckOutFrame(frameOut, packet.sizeBytes * 2, false);
+ jb.ReleaseFrame(frameOut);
+
+ // Test reordered start frame + 1 lost
+ seqNum +=2; // Reoreder 1 frame
+ timeStamp += 33*90;
+ insertedLength=0;
+
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluEnd;
+ packet.markerBit=false;
+
+ TEST(frameIn=jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ insertedLength+=packet.sizeBytes; // This packet should be decoded
+
+ seqNum--;
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.completeNALU=kNaluStart;
+ packet.markerBit=false;
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ insertedLength+=packet.sizeBytes; // This packet should be decoded
+
+ seqNum+=3; // One packet drop
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluComplete;
+ packet.markerBit=false;
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ insertedLength+=packet.sizeBytes; // This packet should be decoded
+
+ seqNum+=1;
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluStart;
+ packet.markerBit=false;
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ // This packet should be decoded since it's the beginning of a NAL
+ insertedLength+=packet.sizeBytes;
+
+ seqNum+=2;
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = false;
+ packet.completeNALU=kNaluEnd;
+ packet.markerBit=true;
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ // This packet should not be decoded because it is an incomplete NAL if it
+ // is the last
+ insertedLength+=0;
+
+ frameOut = jb.GetFrameForDecoding();
+ // Only last NALU is complete
+ CheckOutFrame(frameOut, insertedLength, false);
+ jb.ReleaseFrame(frameOut);
+
+
+ //Test to insert empty packet
+ seqNum+=1;
+ timeStamp += 33*90;
+ VCMPacket emptypacket(data, 0, seqNum, timeStamp, true);
+ emptypacket.seqNum=seqNum;
+ emptypacket.timestamp=timeStamp;
+ emptypacket.frameType = kVideoFrameKey;
+ emptypacket.isFirstPacket = true;
+ emptypacket.completeNALU=kNaluComplete;
+ emptypacket.markerBit=true;
+ TEST(frameIn=jb.GetFrame(emptypacket));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, emptypacket));
+ // This packet should not be decoded because it is an incomplete NAL if it
+ // is the last
+ insertedLength+=0;
+
+ TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
+ TEST(NULL==jb.GetFrameForDecoding());
+
+
+ // Test that a frame can include an empty packet.
+ seqNum+=1;
+ timeStamp += 33*90;
+
+ packet.seqNum=seqNum;
+ packet.timestamp=timeStamp;
+ packet.frameType = kVideoFrameKey;
+ packet.isFirstPacket = true;
+ packet.completeNALU=kNaluComplete;
+ packet.markerBit=false;
+ TEST(frameIn=jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ seqNum+=1;
+ emptypacket.seqNum=seqNum;
+ emptypacket.timestamp=timeStamp;
+ emptypacket.frameType = kVideoFrameKey;
+ emptypacket.isFirstPacket = true;
+ emptypacket.completeNALU=kNaluComplete;
+ emptypacket.markerBit=true;
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, emptypacket));
+
+ // get the frame
+ frameOut = jb.GetCompleteFrameForDecoding(10);
+ // Only last NALU is complete
+ CheckOutFrame(frameOut, packet.sizeBytes, false);
+ jb.Flush();
+
+ // Three reordered H263 packets with bits.
+
+ packet.codec = kVideoCodecH263;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+ packet.bits = true;
+ packet.seqNum += 1;
+ WebRtc_UWord8 oldData1 = data[0];
+ WebRtc_UWord8 oldData2 = data[packet.sizeBytes - 1];
+ unsigned char startByte = 0x07;
+ unsigned char endByte = 0xF8;
+ data[0] = startByte;
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == NULL);
+
+ packet.seqNum -= 1;
+ packet.isFirstPacket = true;
+ packet.bits = false;
+ data[0] = oldData1;
+ data[packet.sizeBytes - 1] = endByte;
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == NULL);
+
+ packet.seqNum += 2;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ data[packet.sizeBytes - 1] = oldData2;
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut != NULL);
+ //CheckOutFrame(frameOut, packet.sizeBytes * 3 - 1, false);
+ const WebRtc_UWord8* buf = frameOut->Buffer();
+ TEST(buf[packet.sizeBytes - 1] == (startByte | endByte));
+
+ // First packet lost, second packet with bits.
+
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+ packet.bits = true;
+ packet.seqNum += 2;
+ packet.timestamp += 33*90;
+ data[0] = 0x07;
+ data[packet.sizeBytes - 1] = 0xF8;
+ //unsigned char startByte = packet.dataPtr[0];
+ //unsigned char endByte = packet.dataPtr[packet.sizeBytes-1];
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut != NULL);
+ TEST(frameOut->Length() == 0);
+
+ data[0] = oldData1;
+ data[packet.sizeBytes - 1] = oldData2;
+ packet.codec = kVideoCodecUnknown;
+ jb.Flush();
+
+ // Test that a we cannot get incomplete frames from the JB if we haven't received
+ // the marker bit, unless we have received a packet from a later timestamp.
+
+ packet.seqNum +=2;
+ packet.bits = false;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = false;
+
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut == NULL);
+
+ packet.seqNum += 2;
+ packet.timestamp += 33*90;
+
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ frameOut = jb.GetFrameForDecoding();
+
+ TEST(frameOut != NULL);
+ CheckOutFrame(frameOut, packet.sizeBytes, false);
+
+ jb.Flush();
+
+ // Test that a we can get incomplete frames from the JB if we have received
+ // the marker bit.
+ packet.seqNum +=2;
+ packet.frameType = kVideoFrameDelta;
+ packet.isFirstPacket = false;
+ packet.markerBit = true;
+
+ TEST(frameIn = jb.GetFrame(packet));
+ TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
+
+ frameOut = jb.GetFrameForDecoding();
+ TEST(frameOut != NULL);
+
+ // ---
+ jb.Stop();
+
+ printf("DONE !!!\n");
+ EventWrapper* waitEvent = EventWrapper::Create();
+ waitEvent->Wait(5000);
+
+ return 0;
+
+}
diff --git a/src/modules/video_coding/main/test/jitter_estimate_test.cc b/src/modules/video_coding/main/test/jitter_estimate_test.cc
new file mode 100644
index 0000000..419ca35
--- /dev/null
+++ b/src/modules/video_coding/main/test/jitter_estimate_test.cc
@@ -0,0 +1,110 @@
+/*
+ * 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 <stdio.h>
+#include <ctime>
+#include "JitterEstimateTest.h"
+#include "tick_time.h"
+
+using namespace webrtc;
+
+JitterEstimateTest::JitterEstimateTest(unsigned int frameRate) :
+_frameRate(frameRate),
+_capacity(2000),
+_rate(500),
+_jitter(5, 0),
+_keyFrameRate(1.0),
+_deltaFrameSize(10000, 1e6),
+_counter(0),
+_lossrate(0.0)
+{
+ // Assign to random value between 0 and max of unsigned int
+ _seed = static_cast<unsigned>(std::time(0));
+ std::srand(_seed);
+ _prevTimestamp = static_cast<unsigned int>((std::rand() + 1.0)/(RAND_MAX + 1.0)*(pow((float) 2, (long) sizeof(unsigned int)*8)-1));
+ _prevWallClock = VCMTickTime::MillisecondTimestamp();
+}
+
+FrameSample
+JitterEstimateTest::GenerateFrameSample()
+{
+ double increment = 1.0/_frameRate;
+ unsigned int frameSize = static_cast<unsigned int>(_deltaFrameSize.RandValue());
+ bool keyFrame = false;
+ bool resent = false;
+ _prevTimestamp += static_cast<unsigned int>(90000*increment + 0.5);
+ double deltaFrameRate = _frameRate - _keyFrameRate;
+ double ratio = deltaFrameRate/static_cast<double>(_keyFrameRate);
+ if (ratio < 1.0)
+ {
+ ratio = 1.0/ratio;
+ if (_counter >= ratio)
+ _counter = 0;
+ else
+ {
+ _counter++;
+ frameSize += static_cast<unsigned int>(3*_deltaFrameSize.GetAverage());
+ keyFrame = true;
+ }
+ }
+ else
+ {
+ if (_counter >= ratio)
+ {
+ frameSize += static_cast<unsigned int>(3*_deltaFrameSize.GetAverage());
+ _counter = 0;
+ keyFrame = true;
+ }
+ else
+ _counter++;
+ }
+ WebRtc_Word64 jitter = static_cast<WebRtc_Word64>(_jitter.RandValue() + 1.0/_capacity * frameSize + 0.5);
+ _prevWallClock += static_cast<WebRtc_Word64>(1000*increment + 0.5);
+ double rndValue = RandUniform();
+ resent = (rndValue < _lossrate);
+ //printf("rndValue = %f\n", rndValue);
+ return FrameSample(_prevTimestamp, _prevWallClock + jitter, frameSize, keyFrame, resent);
+}
+
+void
+JitterEstimateTest::SetCapacity(unsigned int c)
+{
+ _capacity = c;
+}
+
+void
+JitterEstimateTest::SetRate(unsigned int r)
+{
+ _rate = r;
+}
+
+void
+JitterEstimateTest::SetJitter(double m, double v)
+{
+ _jitter.SetParams(m, v);
+}
+
+void
+JitterEstimateTest::SetFrameSizeStats(double m, double v)
+{
+ _deltaFrameSize.SetParams(m, v);
+}
+
+void
+JitterEstimateTest::SetKeyFrameRate(int rate)
+{
+ _keyFrameRate = rate;
+}
+
+void
+JitterEstimateTest::SetLossRate(double rate)
+{
+ _lossrate = rate;
+}
diff --git a/src/modules/video_coding/main/test/jitter_estimate_test.h b/src/modules/video_coding/main/test/jitter_estimate_test.h
new file mode 100644
index 0000000..cd7338a
--- /dev/null
+++ b/src/modules/video_coding/main/test/jitter_estimate_test.h
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
+
+#include "typedefs.h"
+#include "jitter_buffer.h"
+#include "jitter_estimator.h"
+#include <cstdlib>
+#include <cmath>
+
+double const pi = 4*std::atan(1.0);
+
+class GaussDist
+{
+public:
+ GaussDist(double m, double v): _mu(m), _sigma(sqrt(v)) {}
+
+ double RandValue() // returns a single normally distributed number
+ {
+ double r1 = (std::rand() + 1.0)/(RAND_MAX + 1.0); // gives equal distribution in (0, 1]
+ double r2 = (std::rand() + 1.0)/(RAND_MAX + 1.0);
+ return _mu + _sigma * std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2);
+ }
+
+ double GetAverage()
+ {
+ return _mu;
+ }
+
+ double GetVariance()
+ {
+ return _sigma*_sigma;
+ }
+
+ void SetParams(double m, double v)
+ {
+ _mu = m;
+ _sigma = sqrt(v);
+ }
+
+private:
+ double _mu, _sigma;
+};
+
+class JitterEstimateTestWrapper : public webrtc::VCMJitterEstimator
+{
+public:
+ JitterEstimateTestWrapper() : VCMJitterEstimator() {}
+ double GetTheta() { return _theta[0]; }
+ double GetVarNoise() { return _varNoise; }
+};
+
+class FrameSample
+{
+public:
+ FrameSample() {FrameSample(0, 0, 0, false, false);}
+ FrameSample(unsigned int ts, WebRtc_Word64 wallClk, unsigned int fs, bool _keyFrame, bool _resent):
+ timestamp90Khz(ts), wallClockMs(wallClk), frameSize(fs), keyFrame(_keyFrame), resent(_resent) {}
+
+ unsigned int timestamp90Khz;
+ WebRtc_Word64 wallClockMs;
+ unsigned int frameSize;
+ bool keyFrame;
+ bool resent;
+};
+
+class JitterEstimateTest
+{
+public:
+ JitterEstimateTest(unsigned int frameRate);
+ FrameSample GenerateFrameSample();
+ void SetCapacity(unsigned int c);
+ void SetRate(unsigned int r);
+ void SetJitter(double m, double v);
+ void SetFrameSizeStats(double m, double v);
+ void SetKeyFrameRate(int rate);
+ void SetLossRate(double rate);
+
+private:
+ double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); }
+ unsigned int _frameRate;
+ unsigned int _capacity;
+ unsigned int _rate;
+ GaussDist _jitter;
+ //GaussDist _noResend;
+ GaussDist _deltaFrameSize;
+ unsigned int _prevTimestamp;
+ WebRtc_Word64 _prevWallClock;
+ unsigned int _nextDelay;
+ double _keyFrameRate;
+ unsigned int _counter;
+ unsigned int _seed;
+ double _lossrate;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
diff --git a/src/modules/video_coding/main/test/media_opt_test.cc b/src/modules/video_coding/main/test/media_opt_test.cc
new file mode 100644
index 0000000..f3a34d8
--- /dev/null
+++ b/src/modules/video_coding/main/test/media_opt_test.cc
@@ -0,0 +1,614 @@
+/*
+ * 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.
+ */
+
+// Implementation of Media Optimization Test
+// testing is done via the VCM module, no specific Media opt functionality.
+
+#include "receiver_tests.h" // receive side callbacks
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "test_util.h" // send side callback
+#include "media_opt_test.h"
+#include "../source/event.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <vector>
+
+//#include <Windows.h>
+#include <time.h>
+
+using namespace webrtc;
+
+int MediaOptTest::RunTest(int testNum, CmdArgs& args)
+{
+ Trace::CreateTrace();
+ Trace::SetTraceFile("mediaOptTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ MediaOptTest* mot = new MediaOptTest(vcm);
+ if (testNum == 0)
+ { // regular
+ mot->Setup(0, args);
+ mot->GeneralSetup();
+ mot->Perform();
+ mot->Print(1);// print to screen
+ mot->TearDown();
+ }
+ if (testNum == 1)
+ { // release test
+ mot->Setup(0, args);
+ mot->RTTest();
+ }
+ if (testNum == 2)
+ { // release test, running from script
+ mot->Setup(1, args);
+ mot->GeneralSetup();
+ mot->Perform();
+ mot->Print(1);// print to screen
+ mot->TearDown();
+ }
+
+ VideoCodingModule::Destroy(vcm);
+ delete mot;
+ Trace::ReturnTrace();
+ return 0;
+
+}
+
+
+MediaOptTest::MediaOptTest(VideoCodingModule* vcm):
+_vcm(vcm),
+_width(0),
+_height(0),
+_lengthSourceFrame(0),
+_timeStamp(0),
+_frameRate(30.0f),
+_nackEnabled(false),
+_fecEnabled(false),
+_rttMS(0),
+_renderDelayMs(0),
+_bitRate(300.0f),
+_lossRate(0.0f),
+_frameCnt(0),
+_sumEncBytes(0),
+_numFramesDropped(0),
+_numberOfCores(4),
+vcmMacrosTests(0),
+vcmMacrosErrors(0)
+{
+ _rtp = RtpRtcp::CreateRtpRtcp(1, false);
+}
+
+MediaOptTest::~MediaOptTest()
+{
+ RtpRtcp::DestroyRtpRtcp(_rtp);
+}
+void
+MediaOptTest::Setup(int testType, CmdArgs& args)
+{
+ /*TEST USER SETTINGS*/
+ // test parameters
+ _inname = args.inputFile;
+ if (args.outputFile == "")
+ _outname = "../MOTest_out.vp8";
+ else
+ _outname = args.outputFile;
+ _actualSourcename = "../MOTestSource.yuv"; // actual source after frame dropping
+ _codecName = args.codecName;
+ _sendCodecType = args.codecType;
+ _width = args.width;
+ _height = args.height;
+ _frameRate = args.frameRate;
+ _bitRate = args.bitRate;
+ _numberOfCores = 4;
+
+ // error resilience
+ _nackEnabled = false;
+ _fecEnabled = true;
+ _nackFecEnabled = false;
+
+ _rttMS = 100;
+ _lossRate = 0.00*255; // no packet loss
+
+ _testType = testType;
+
+ //For multiple runs with script
+ if (_testType == 1)
+ {
+ float rateTest,lossTest;
+ int numRuns;
+ _fpinp = fopen("dat_inp","rb");
+ _fpout = fopen("test_runs/dat_out","ab");
+ _fpout2 = fopen("test_runs/dat_out2","ab");
+ fscanf(_fpinp,"%f %f %d \n",&rateTest,&lossTest,&numRuns);
+ _bitRate = rateTest;
+ _lossRate = lossTest;
+ _testNum = 0;
+
+ // for bit rates: 500, 1000, 2000, 3000,4000
+ // for loss rates: 0, 1, 3, 5, 10%
+ _numParRuns = 25;
+
+ _testNum = numRuns + 1;
+ if (rateTest == 0.0) _lossRate = 0.0;
+ else
+ {
+ if (rateTest == 4000) //final bit rate
+ {
+ if (lossTest == 0.1*255) _lossRate = 0.0; //start at 1%
+ else
+ if (lossTest == 0.05*255) _lossRate = 0.1*255; //final loss rate
+ else
+ if (lossTest == 0.0) _lossRate = 0.01*255;
+ else _lossRate = lossTest + 0.02*255;
+ }
+ }
+
+ if (rateTest == 0.0 || rateTest == 4000) _bitRate = 500; //starting bit rate
+ else
+ if (rateTest == 500) _bitRate = 1000;
+ else _bitRate = rateTest + 1000;
+ }
+ //
+
+ _renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+ /* test settings end*/
+
+ _lengthSourceFrame = 3*_width*_height/2;
+ _log.open("../VCM_MediaOptLog.txt", std::fstream::out | std::fstream::app);
+ return;
+}
+
+void
+MediaOptTest::GeneralSetup()
+{
+
+ WebRtc_UWord8 deltaFECRate = 0;
+ WebRtc_UWord8 keyFECRate = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+
+ if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ if ((_actualSourceFile = fopen(_actualSourcename.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _actualSourcename.c_str());
+ exit(1);
+ }
+
+ if (_rtp->InitReceiver() < 0)
+ {
+ exit(1);
+ }
+ if (_rtp->InitSender() < 0)
+ {
+ exit(1);
+ }
+ if (_vcm->InitializeReceiver() < 0)
+ {
+ exit(1);
+ }
+ if (_vcm->InitializeSender())
+ {
+ exit(1);
+ }
+
+ // Registering codecs for the RTP module
+
+ // Register receive payload
+ _rtp->RegisterReceivePayload("VP8", VCM_VP8_PAYLOAD_TYPE);
+ _rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE);
+ _rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE);
+
+ // Register send payload
+ _rtp->RegisterSendPayload("VP8", VCM_VP8_PAYLOAD_TYPE);
+ _rtp->RegisterSendPayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE);
+ _rtp->RegisterSendPayload("RED", VCM_RED_PAYLOAD_TYPE);
+
+ if (_nackFecEnabled == 1)
+ _rtp->SetGenericFECStatus(_nackFecEnabled, VCM_RED_PAYLOAD_TYPE,
+ VCM_ULPFEC_PAYLOAD_TYPE);
+ else
+ _rtp->SetGenericFECStatus(_fecEnabled, VCM_RED_PAYLOAD_TYPE,
+ VCM_ULPFEC_PAYLOAD_TYPE);
+
+ // VCM: Registering codecs
+ VideoCodec sendCodec;
+ _vcm->InitializeSender();
+ _vcm->InitializeReceiver();
+ WebRtc_Word32 numberOfCodecs = _vcm->NumberOfCodecs();
+ if (numberOfCodecs < 1)
+ {
+ exit(1);
+ }
+
+ WebRtc_UWord8 i= 0;
+ if (_vcm->Codec(_sendCodecType, &sendCodec) != 0)
+ {
+ printf("Unknown codec\n");
+ exit(1);
+ }
+ // register codec
+ sendCodec.startBitrate = (int) _bitRate;
+ sendCodec.height = _height;
+ sendCodec.width = _width;
+ sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate;
+ _vcm->RegisterSendCodec(&sendCodec, _numberOfCores, 1440);
+ _vcm->RegisterReceiveCodec(&sendCodec, _numberOfCores); // same settings for encode and decode
+
+ _vcm->SetRenderDelay(_renderDelayMs);
+ _vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
+
+ return;
+}
+// The following test shall be conducted under release tests
+
+
+
+WebRtc_Word32
+MediaOptTest::Perform()
+{
+ //Setup();
+ EventWrapper* waitEvent = EventWrapper::Create();
+
+ // callback settings
+ VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(_rtp);
+ _vcm->RegisterTransportCallback(encodeCompleteCallback);
+ encodeCompleteCallback->SetCodecType(ConvertCodecType(_codecName.c_str()));
+ encodeCompleteCallback->SetFrameDimensions(_width, _height);
+ // frame ready to be sent to network
+ RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(_rtp);
+ _rtp->RegisterSendTransport(outgoingTransport);
+ //FrameReceiveCallback
+ VCMDecodeCompleteCallback receiveCallback(_decodedFile);
+ RtpDataCallback dataCallback(_vcm);
+ _rtp->RegisterIncomingDataCallback(&dataCallback);
+
+ VCMTestProtectionCallback protectionCallback;
+ _vcm->RegisterProtectionCallback(&protectionCallback);
+
+ // set error resilience / test parameters:
+ outgoingTransport->SetLossPct(_lossRate);
+ if (_nackFecEnabled == 1)
+ _vcm->SetVideoProtection(kProtectionNackFEC, _nackFecEnabled);
+ else
+ {
+ _vcm->SetVideoProtection(kProtectionNack, _nackEnabled);
+ _vcm->SetVideoProtection(kProtectionFEC, _fecEnabled);
+ }
+
+ // START TEST
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, (WebRtc_UWord8)_lossRate, _rttMS);
+ _vcm->RegisterReceiveCallback(&receiveCallback);
+
+ // inform RTP Module of error resilience features
+ _rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate());
+ _rtp->SetNACKStatus(protectionCallback.NACKMethod());
+
+ _frameCnt = 0;
+ _sumEncBytes = 0.0;
+ _numFramesDropped = 0;
+
+ while (feof(_sourceFile)== 0)
+ {
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ _frameCnt++;
+
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ // inform RTP Module of error resilience features
+ //_rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate());
+ //_rtp->SetNACKStatus(protectionCallback.NACKMethod());
+
+ WebRtc_Word32 ret = _vcm->Decode();
+ if (ret < 0 )
+ {
+ TEST(ret == 0);
+ printf ("Decode error in frame # %d",_frameCnt);
+ }
+
+ float encBytes = encodeCompleteCallback->EncodedBytes();
+ if (encBytes == 0)
+ {
+ _numFramesDropped += 1;
+ //printf("frame #%d dropped \n", _frameCnt );
+ }
+ else
+ {
+ // write frame to file
+ fwrite(sourceFrame.Buffer(), 1, sourceFrame.Length(), _actualSourceFile);
+ }
+
+ _sumEncBytes += encBytes;
+ //waitEvent->Wait(33);
+ }
+
+ //END TEST
+ delete waitEvent;
+ delete encodeCompleteCallback;
+ delete outgoingTransport;
+ delete tmpBuffer;
+
+return 0;
+
+}
+
+void
+MediaOptTest::RTTest()
+{
+ // will only calculate PSNR - not create output files for all
+ // SET UP
+ // Set bit rates
+ const float bitRateVec[] = {500, 1000, 2000,3000, 4000};
+ //const float bitRateVec[] = {1000};
+ // Set Packet loss values ([0,255])
+ const double lossPctVec[] = {0.0*255, 0.0*255, 0.01*255, 0.01*255, 0.03*255, 0.03*255, 0.05*255, 0.05*255, 0.1*255, 0.1*255};
+ const bool nackEnabledVec[] = {false , false, false, false, false, false, false, false , false, false};
+ const bool fecEnabledVec[] = {false , true, false, true , false, true , false, true , false, true};
+ // fec and nack are set according to the packet loss values
+
+ const float nBitrates = sizeof(bitRateVec)/sizeof(*bitRateVec);
+ const float nlossPct = sizeof(lossPctVec)/sizeof(*lossPctVec);
+
+ std::vector<const VideoSource*> sources;
+ std::vector<const VideoSource*>::iterator it;
+
+ sources.push_back(new const VideoSource(_inname, _width, _height));
+ int numOfSrc = 1;
+
+ // constant settings (valid for entire run time)
+ _rttMS = 20;
+ _renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+
+ _outname = "../RTMOTest_out.yuv"; // same out name for all
+ _actualSourcename = "../RTMOTestSource.yuv"; // actual source after frame dropping
+
+ _codecName = "VP8"; // for now just this one - later iterate over all codec types
+ _log.open("../VCM_RTMediaOptLog.txt", std::fstream::out | std::fstream::app);
+ _outputRes=fopen("../VCM_MediaOpt","ab");
+
+ //char filename[128];
+ /* test settings end*/
+
+ // START TEST
+ // iterate over test sequences
+ printf("\n****START TEST OVER ALL RUNS ****\n");
+ int runCnt = 0;
+ for (it = sources.begin() ; it < sources.end(); it++)
+ {
+
+ // test set up
+ _inname = (*it)->GetFileName();
+ _width = (*it)->GetWidth();
+ _height = (*it)->GetHeight();
+ _lengthSourceFrame = 3*_width*_height/2;
+ _frameRate = (*it)->GetFrameRate();
+ //GeneralSetup();
+
+
+ // iterate over all bit rates
+ for (int i = 0; i < nBitrates; i++)
+ {
+ _bitRate = static_cast<float>(bitRateVec[i]);
+ // iterate over all packet loss values
+ for (int j = 0; j < nlossPct; j++)
+ {
+ _lossRate = static_cast<float>(lossPctVec[j]);
+ _nackEnabled = static_cast<bool>(nackEnabledVec[j]);
+ _fecEnabled = static_cast<bool>(fecEnabledVec[j]);
+
+ runCnt++;
+ printf("run #%d out of %d \n", runCnt,(int)(nlossPct*nBitrates*numOfSrc));
+
+ //printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
+
+ /*
+ int ch = sprintf(filename,"../test_mediaOpt/RTMOTest_%d_%d_%d_%d.yuv",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
+ _outname = filename;
+
+ printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
+ */
+ if (_rtp != NULL)
+ {
+ RtpRtcp::DestroyRtpRtcp(_rtp);
+ }
+ _rtp = RtpRtcp::CreateRtpRtcp(1, false);
+ GeneralSetup();
+ Perform();
+ Print(1);
+ TearDown();
+ RtpRtcp::DestroyRtpRtcp(_rtp);
+ _rtp = NULL;
+
+ printf("\n");
+ //printf("**DONE WITH RUN: **%d %d %f %d \n",_nackEnabled,_fecEnabled,lossPctVec[j],int(_bitRate));
+ //
+
+ }// end of packet loss loop
+ }// end of bit rate loop
+ delete *it;
+ }// end of video sequence loop
+ // at end of sequence
+ fclose(_outputRes);
+ printf("\nVCM Media Optimization Test: \n\n%i tests completed\n", vcmMacrosTests);
+ if (vcmMacrosErrors > 0)
+ {
+ printf("%i FAILED\n\n", vcmMacrosErrors);
+ }
+ else
+ {
+ printf("ALL PASSED\n\n");
+ }
+}
+
+
+void
+MediaOptTest::Print(int mode)
+{
+ double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
+ double actualBitRate = ActualBitRate / 1000.0;
+ double psnr;
+ PSNRfromFiles(_actualSourcename.c_str(), _outname.c_str(), _width, _height, &psnr);
+
+ (_log) << "VCM: Media Optimization Test Cycle Completed!" << std::endl;
+ (_log) << "Input file: " << _inname << std::endl;
+ (_log) << "Output file:" << _outname << std::endl;
+ ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
+ (_log) << "Error Reslience: NACK:" << _nackEnabled << "; FEC: " << _fecEnabled << std::endl;
+ (_log) << "Packet Loss applied= %f " << _lossRate << std::endl;
+ (_log) << _numFramesDropped << " FRames were dropped" << std::endl;
+ ( _log) << "PSNR: " << psnr << std::endl;
+ (_log) << std::endl;
+
+ if (_testType == 2)
+ {
+ fprintf(_outputRes,"************\n");
+ fprintf(_outputRes,"\n\n\n");
+ fprintf(_outputRes,"Actual bitrate: %f kbps\n", actualBitRate);
+ fprintf(_outputRes,"Target bitrate: %f kbps\n", _bitRate);
+ fprintf(_outputRes,"NACK: %s ",(_nackEnabled)?"true":"false");
+ fprintf(_outputRes,"FEC: %s \n ",(_fecEnabled)?"true":"false");
+ fprintf(_outputRes,"Packet loss applied = %f\n", _lossRate);
+ fprintf(_outputRes,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
+ fprintf(_outputRes,"PSNR: %f \n", psnr);
+ fprintf(_outputRes,"************\n");
+ }
+
+
+ //
+ if (_testType == 1)
+ {
+ fprintf(_fpout,"************\n");
+ fprintf(_fpout,"\n\n\n");
+ fprintf(_fpout,"Actual bitrate: %f kbps\n", actualBitRate);
+ fprintf(_fpout,"Target bitrate: %f kbps\n", _bitRate);
+ fprintf(_fpout,"NACK: %s ",(_nackEnabled)?"true":"false");
+ fprintf(_fpout,"FEC: %s \n ",(_fecEnabled)?"true":"false");
+ fprintf(_fpout,"Packet loss applied = %f\n", _lossRate);
+ fprintf(_fpout,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
+ fprintf(_fpout,"PSNR: %f \n", psnr);
+ fprintf(_fpout,"************\n");
+
+ int testNum1 = _testNum/(_numParRuns +1) + 1;
+ int testNum2 = _testNum%_numParRuns;
+ if (testNum2 == 0) testNum2 = _numParRuns;
+ fprintf(_fpout2,"%d %d %f %f %f %f \n",testNum1,testNum2,_bitRate,actualBitRate,_lossRate,psnr);
+ fclose(_fpinp);
+ _fpinp = fopen("dat_inp","wb");
+ fprintf(_fpinp,"%f %f %d \n",_bitRate,_lossRate,_testNum);
+ }
+ //
+
+
+ if (mode == 1)
+ {
+ // print to screen
+ printf("\n\n\n");
+ printf("Actual bitrate: %f kbps\n", actualBitRate);
+ printf("Target bitrate: %f kbps\n", _bitRate);
+ printf("NACK: %s ",(_nackEnabled)?"true":"false");
+ printf("FEC: %s \n",(_fecEnabled)?"true":"false");
+ printf("Packet loss applied = %f\n", _lossRate);
+ printf("%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
+ printf("PSNR: %f \n", psnr);
+ }
+ TEST(psnr > 10); // low becuase of possible frame dropping (need to verify that OK for all packet loss values/ rates)
+}
+
+void
+MediaOptTest::TearDown()
+{
+ _log.close();
+ fclose(_sourceFile);
+ fclose(_decodedFile);
+ fclose(_actualSourceFile);
+ return;
+}
+
+
+VCMTestProtectionCallback::VCMTestProtectionCallback():
+_deltaFECRate(0),
+_keyFECRate(0),
+_nack(kNackOff)
+{
+ //
+}
+
+VCMTestProtectionCallback::~VCMTestProtectionCallback()
+{
+ //
+}
+
+WebRtc_Word32
+VCMTestProtectionCallback::ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack)
+{
+ _deltaFECRate = deltaFECRate;
+ _keyFECRate = keyFECRate;
+ if (nack == true)
+ {
+ _nack = kNackRtcp;
+ }
+ else
+ {
+ _nack = kNackOff;
+ }
+ return VCM_OK;
+
+}
+NACKMethod
+VCMTestProtectionCallback::NACKMethod()
+{
+ return _nack;
+}
+
+WebRtc_UWord8
+VCMTestProtectionCallback::FECDeltaRate()
+{
+ return _deltaFECRate;
+}
+
+WebRtc_UWord8
+VCMTestProtectionCallback::FECKeyRate()
+{
+ return _keyFECRate;
+}
+
+
+void
+RTPFeedbackCallback::OnNetworkChanged(const WebRtc_Word32 id,
+ const WebRtc_UWord16 bitrateTargetKbit,
+ const WebRtc_UWord8 fractionLost,
+ const WebRtc_UWord16 roundTripTimeMs,
+ const WebRtc_UWord32 jitterMS,
+ const WebRtc_UWord16 bwEstimateKbitMin,
+ const WebRtc_UWord16 bwEstimateKbitMax)
+{
+
+ _vcm->SetChannelParameters(bitrateTargetKbit, fractionLost,(WebRtc_UWord8)roundTripTimeMs);
+}
diff --git a/src/modules/video_coding/main/test/media_opt_test.h b/src/modules/video_coding/main/test/media_opt_test.h
new file mode 100644
index 0000000..8dbd230
--- /dev/null
+++ b/src/modules/video_coding/main/test/media_opt_test.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+// VCM Media Optimization Test
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
+
+#include "video_coding.h"
+#include "test_macros.h"
+#include "test_util.h"
+#include "video_source.h"
+
+#include <string>
+
+using namespace std;
+//
+
+// media optimization test
+// This test simulates a complete encode-decode cycle via the RTP module.
+// allows error resilience tests, packet loss tests, etc.
+// Does not test the media optimization deirectly, but via the VCM API only.
+// The test allows two modes:
+// 1 - Standard, basic settings, one run
+// 2 - Release test - iterates over a number of video sequences, bit rates, packet loss values ,etc.
+
+class VCMTestProtectionCallback: public webrtc::VCMProtectionCallback
+{
+public:
+ VCMTestProtectionCallback();
+ virtual ~VCMTestProtectionCallback();
+ WebRtc_Word32 ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack);
+ enum webrtc::NACKMethod NACKMethod();
+ WebRtc_UWord8 FECDeltaRate();
+ WebRtc_UWord8 FECKeyRate();
+private:
+ WebRtc_UWord8 _deltaFECRate;
+ WebRtc_UWord8 _keyFECRate;
+ enum webrtc::NACKMethod _nack;
+
+};
+
+
+class MediaOptTest
+{
+public:
+ MediaOptTest(webrtc::VideoCodingModule* vcm);
+ ~MediaOptTest();
+
+ static int RunTest(int testNum, CmdArgs& args);
+ // perform encode-decode of an entire sequence
+ WebRtc_Word32 Perform();
+ // Set up for a single mode test
+ void Setup(int testType, CmdArgs& args);
+ // General set up - applicable for both modes
+ void GeneralSetup();
+ // Run release testing
+ void RTTest();
+ void TearDown();
+ // mode = 1; will print to screen, otherwise only to log file
+ void Print(int mode);
+
+private:
+
+ webrtc::VideoCodingModule* _vcm;
+ webrtc::RtpRtcp* _rtp;
+ std::string _inname;
+ std::string _outname;
+ std::string _actualSourcename;
+ std::fstream _log;
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ FILE* _actualSourceFile;
+ FILE* _outputRes;
+ WebRtc_UWord16 _width;
+ WebRtc_UWord16 _height;
+ WebRtc_UWord32 _lengthSourceFrame;
+ WebRtc_UWord32 _timeStamp;
+ float _frameRate;
+ bool _nackEnabled;
+ bool _fecEnabled;
+ bool _nackFecEnabled;
+ WebRtc_UWord8 _rttMS;
+ float _bitRate;
+ double _lossRate;
+ WebRtc_UWord32 _renderDelayMs;
+ WebRtc_Word32 _frameCnt;
+ float _sumEncBytes;
+ WebRtc_Word32 _numFramesDropped;
+ string _codecName;
+ webrtc::VideoCodecType _sendCodecType;
+ WebRtc_Word32 _numberOfCores;
+ int vcmMacrosTests;
+ int vcmMacrosErrors;
+
+ //for release test#2
+ FILE* _fpinp;
+ FILE* _fpout;
+ FILE* _fpout2;
+ int _testType;
+ int _testNum;
+ int _numParRuns;
+
+}; // end of MediaOptTest class definition
+
+
+// Feed back from the RTP Module callback
+class RTPFeedbackCallback: public webrtc::RtpVideoFeedback
+{
+public:
+ RTPFeedbackCallback(webrtc::VideoCodingModule* vcm) {_vcm = vcm;};
+ void OnReceivedIntraFrameRequest(const WebRtc_Word32 id,
+ const WebRtc_UWord8 message = 0){};
+
+ void OnNetworkChanged(const WebRtc_Word32 id,
+ const WebRtc_UWord16 bitrateTargetKbit,
+ const WebRtc_UWord8 fractionLost,
+ const WebRtc_UWord16 roundTripTimeMs,
+ const WebRtc_UWord32 jitterMS,
+ const WebRtc_UWord16 bwEstimateKbitMin,
+ const WebRtc_UWord16 bwEstimateKbitMax);
+
+private:
+ webrtc::VideoCodingModule* _vcm;
+
+};
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
diff --git a/src/modules/video_coding/main/test/mt_rx_tx_test.cc b/src/modules/video_coding/main/test/mt_rx_tx_test.cc
new file mode 100644
index 0000000..8d4d782
--- /dev/null
+++ b/src/modules/video_coding/main/test/mt_rx_tx_test.cc
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+
+/*************************************************
+ *
+ * Testing multi thread - receive and send sides
+ *
+ **************************************************/
+
+#include "receiver_tests.h" // shared RTP state and receive side threads
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "thread_wrapper.h"
+#include "../source/event.h"
+#include "test_util.h" // send side callback
+#include "media_opt_test.h"
+
+#include <string.h>
+
+using namespace webrtc;
+
+bool
+MainSenderThread(void* obj)
+{
+ SendSharedState* state = static_cast<SendSharedState*>(obj);
+ EventWrapper& waitEvent = *EventWrapper::Create();
+ // preparing a frame for encoding
+ VideoFrame sourceFrame;
+ WebRtc_Word32 width = state->_args.width;
+ WebRtc_Word32 height = state->_args.height;
+ float frameRate = state->_args.frameRate;
+ WebRtc_Word32 lengthSourceFrame = 3*width*height/2;
+ sourceFrame.VerifyAndAllocate(lengthSourceFrame);
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[lengthSourceFrame];
+
+ if (state->_sourceFile == NULL)
+ {
+ state->_sourceFile = fopen(state->_args.inputFile.c_str(), "rb");
+ if (state->_sourceFile == NULL)
+ {
+ printf ("Error when opening file \n");
+ delete &waitEvent;
+ delete tmpBuffer;
+ return false;
+ }
+ }
+ if (feof(state->_sourceFile) == 0)
+ {
+ fread(tmpBuffer, 1, lengthSourceFrame,state->_sourceFile);
+ state->_frameCnt++;
+ sourceFrame.CopyFrame(lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(height);
+ sourceFrame.SetWidth(width);
+ state->_timestamp += (WebRtc_UWord32)(9e4 / frameRate);
+ sourceFrame.SetTimeStamp(state->_timestamp);
+
+ WebRtc_Word32 ret = state->_vcm.AddVideoFrame(sourceFrame);
+ if (ret < 0)
+ {
+ printf("Add Frame error: %d\n", ret);
+ delete &waitEvent;
+ delete tmpBuffer;
+ return false;
+ }
+ waitEvent.Wait(33);
+ }
+
+ delete &waitEvent;
+ delete tmpBuffer;
+
+ return true;
+}
+
+bool
+IntSenderThread(void* obj)
+{
+ SendSharedState* state = static_cast<SendSharedState*>(obj);
+ state->_vcm.SetChannelParameters(1000,30,0);
+
+ return true;
+}
+
+
+int MTRxTxTest(CmdArgs& args)
+{
+ /* TEST SETTINGS */
+ std::string inname = args.inputFile;
+ std::string outname;
+ if (args.outputFile == "")
+ outname = "../MTRxTxTest_decoded.yuv";
+ else
+ outname = args.outputFile;
+
+ WebRtc_UWord16 width = args.width;
+ WebRtc_UWord16 height = args.height;
+ WebRtc_UWord32 lengthSourceFrame = 3*width*height/2;
+
+ float frameRate = args.frameRate;
+ float bitRate = args.bitRate;
+ WebRtc_Word32 numberOfCores = 1;
+
+ // error resilience/network
+ // Nack support is currently not implemented in this test.
+ bool nackEnabled = false;
+ bool fecEnabled = false;
+ WebRtc_UWord8 rttMS = 20;
+ float lossRate = 0.0*255; // no packet loss
+ WebRtc_UWord32 renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+ WebRtc_UWord8 deltaFECRate = 0;
+ WebRtc_UWord8 keyFECRate = 0;
+
+ /* TEST SET-UP */
+
+ // Set up trace
+ Trace::CreateTrace();
+ Trace::SetTraceFile("MTRxTxTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+
+ FILE* sourceFile;
+ FILE* decodedFile;
+
+ if ((sourceFile = fopen(inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", inname.c_str());
+ return -1;
+ }
+
+ if ((decodedFile = fopen(outname.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", outname.c_str());
+ return -1;
+ }
+
+ //RTP
+ RtpRtcp* rtp = RtpRtcp::CreateRtpRtcp(1, false);
+ if (rtp->InitReceiver() < 0)
+ {
+ return -1;
+ }
+ if (rtp->InitSender() < 0)
+ {
+ return -1;
+ }
+ // registering codecs for the RTP module
+ TEST(rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE) == 0);
+ TEST(rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE) == 0);
+ TEST(rtp->RegisterReceivePayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE) == 0);
+
+ // inform RTP Module of error resilience features
+ TEST(rtp->SetGenericFECStatus(fecEnabled, VCM_RED_PAYLOAD_TYPE, VCM_ULPFEC_PAYLOAD_TYPE) == 0);
+
+ TEST(rtp->RegisterSendPayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE, 90000, 1, 10000) == 0);
+
+ //VCM
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ if (vcm->InitializeReceiver() < 0)
+ {
+ return -1;
+ }
+ if (vcm->InitializeSender())
+ {
+ return -1;
+ }
+ // registering codecs for the VCM module
+ VideoCodec sendCodec;
+ vcm->InitializeSender();
+ WebRtc_Word32 numberOfCodecs = vcm->NumberOfCodecs();
+ if (numberOfCodecs < 1)
+ {
+ return -1;
+ }
+
+ if (vcm->Codec(args.codecType, &sendCodec) != 0)
+ {
+ // desired codec unavailable
+ printf("Codec not registered\n");
+ return -1;
+ }
+ // register codec
+ sendCodec.startBitrate = (int) bitRate;
+ sendCodec.height = height;
+ sendCodec.width = width;
+ sendCodec.maxFramerate = (WebRtc_UWord8)frameRate;
+ vcm->RegisterSendCodec(&sendCodec, numberOfCores, 1440);
+ vcm->RegisterReceiveCodec(&sendCodec, numberOfCores); // same settings for encode and decode
+
+ vcm->SetRenderDelay(renderDelayMs);
+ vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
+
+ // Callback Settings
+
+ PacketRequester packetRequester(*rtp);
+ vcm->RegisterPacketRequestCallback(&packetRequester);
+
+ VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(rtp);
+ vcm->RegisterTransportCallback(encodeCompleteCallback);
+ encodeCompleteCallback->SetCodecType(ConvertCodecType(args.codecName.c_str()));
+ encodeCompleteCallback->SetFrameDimensions(width, height);
+ // frame ready to be sent to network
+ RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(rtp, "dump.rtp");
+ rtp->RegisterSendTransport(outgoingTransport);
+ // FrameReceiveCallback
+ VCMDecodeCompleteCallback receiveCallback(decodedFile);
+ RtpDataCallback dataCallback(vcm);
+ rtp->RegisterIncomingDataCallback(&dataCallback);
+ vcm->RegisterReceiveCallback(&receiveCallback);
+
+ VCMTestProtectionCallback protectionCallback;
+ vcm->RegisterProtectionCallback(&protectionCallback);
+
+ outgoingTransport->SetLossPct(lossRate);
+ // Nack support is currently not implemented in this test
+ assert(nackEnabled == false);
+ vcm->SetVideoProtection(kProtectionNack, nackEnabled);
+ vcm->SetVideoProtection(kProtectionFEC, fecEnabled);
+
+ // inform RTP Module of error resilience features
+ rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),
+ protectionCallback.FECDeltaRate());
+ rtp->SetNACKStatus(protectionCallback.NACKMethod());
+
+ vcm->SetChannelParameters((WebRtc_UWord32) bitRate,
+ (WebRtc_UWord8) lossRate, rttMS);
+
+ SharedRTPState mtState(*vcm, *rtp); // receive side
+ SendSharedState mtSendState(*vcm, *rtp, args); // send side
+
+ /*START TEST*/
+
+ // Create and start all threads
+ // send side threads
+ ThreadWrapper* mainSenderThread = ThreadWrapper::CreateThread(MainSenderThread,
+ &mtSendState, kNormalPriority, "MainSenderThread");
+ ThreadWrapper* intSenderThread = ThreadWrapper::CreateThread(IntSenderThread,
+ &mtSendState, kNormalPriority, "IntThread");
+
+ if (MainSenderThread != NULL)
+ {
+ unsigned int tid;
+ mainSenderThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start main sender thread\n");
+ return -1;
+ }
+
+ if (IntSenderThread != NULL)
+ {
+ unsigned int tid;
+ intSenderThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start sender interference thread\n");
+ return -1;
+ }
+
+ // Receive side threads
+ ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread,
+ &mtState, kNormalPriority, "ProcessingThread");
+ ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread,
+ &mtState, kNormalPriority, "DecodeThread");
+
+ if (processingThread != NULL)
+ {
+ unsigned int tid;
+ processingThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start processing thread\n");
+ return -1;
+ }
+
+ if (decodeThread != NULL)
+ {
+ unsigned int tid;
+ decodeThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start decode thread\n");
+ return -1;
+ }
+
+ EventWrapper& waitEvent = *EventWrapper::Create();
+
+ // Decode for 10 seconds and then tear down and exit.
+ waitEvent.Wait(30000);
+
+ // Tear down
+
+ while (!mainSenderThread->Stop())
+ {
+ ;
+ }
+
+ while (!intSenderThread->Stop())
+ {
+ ;
+ }
+
+
+ while (!processingThread->Stop())
+ {
+ ;
+ }
+
+ while (!decodeThread->Stop())
+ {
+ ;
+ }
+
+ delete &waitEvent;
+ delete mainSenderThread;
+ delete intSenderThread;
+ delete processingThread;
+ delete decodeThread;
+ delete encodeCompleteCallback;
+ delete outgoingTransport;
+ VideoCodingModule::Destroy(vcm);
+ RtpRtcp::DestroyRtpRtcp(rtp);
+ rtp = NULL;
+ vcm = NULL;
+ Trace::ReturnTrace();
+ fclose(decodedFile);
+ printf("Multi-Thread test Done: View output file \n");
+ return 0;
+
+}
+
diff --git a/src/modules/video_coding/main/test/normal_test.cc b/src/modules/video_coding/main/test/normal_test.cc
new file mode 100644
index 0000000..4d77a5c
--- /dev/null
+++ b/src/modules/video_coding/main/test/normal_test.cc
@@ -0,0 +1,374 @@
+/*
+ * 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 "normal_test.h"
+#include "../source/event.h"
+#include "tick_time.h"
+#include "common_types.h"
+#include "trace.h"
+#include "test_util.h"
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <time.h>
+
+using namespace webrtc;
+
+int NormalTest::RunTest(CmdArgs& args)
+{
+ // Don't run this test with debug time
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ printf("SIMULATION TIME\n");
+#else
+ printf("REAL-TIME\n");
+#endif
+ Trace::CreateTrace();
+ Trace::SetTraceFile("VCMNormalTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ NormalTest VCMNTest(vcm);
+ VCMNTest.Perform(args);
+ VideoCodingModule::Destroy(vcm);
+ Trace::ReturnTrace();
+ return 0;
+}
+
+////////////////
+// Callback Implementation
+//////////////
+
+VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test):
+_seqNo(0),
+_layerPacketId(1),
+_encodedFile(encodedFile),
+_encodedBytes(0),
+_skipCnt(0),
+_VCMReceiver(NULL),
+_test(test)
+{
+ //
+}
+VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
+{
+}
+
+void
+VCMNTEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport)
+{
+}
+
+WebRtc_Word32
+VCMNTEncodeCompleteCallback::SendData(const FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const RTPFragmentationHeader& fragmentationHeader)
+
+{
+ // will call the VCMReceiver input packet
+ _frameType = frameType;
+ // writing encodedData into file
+ fwrite(payloadData, 1, payloadSize, _encodedFile);
+ WebRtcRTPHeader rtpInfo;
+ rtpInfo.header.markerBit = true;
+ rtpInfo.type.Video.width = 0;
+ rtpInfo.type.Video.height = 0;
+ switch (_test.VideoType())
+ {
+ case kVideoCodecH263:
+ rtpInfo.type.Video.codec = kRTPVideoH263;
+ rtpInfo.type.Video.codecHeader.H263.bits = false;
+ rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
+ rtpInfo.type.Video.height = (WebRtc_UWord16)_test.Height();
+ rtpInfo.type.Video.width = (WebRtc_UWord16)_test.Width();
+ break;
+ case kVideoCodecVP8:
+ rtpInfo.type.Video.codec = kRTPVideoVP8;
+ break;
+ case kVideoCodecI420:
+ rtpInfo.type.Video.codec = kRTPVideoI420;
+ break;
+ }
+ rtpInfo.header.payloadType = payloadType;
+ rtpInfo.header.sequenceNumber = _seqNo++;
+ rtpInfo.header.ssrc = 0;
+ rtpInfo.header.timestamp = timeStamp;
+ rtpInfo.frameType = frameType;
+ rtpInfo.type.Video.isFirstPacket = true;
+ // Size should also be received from that table, since the payload type
+ // defines the size.
+
+ _encodedBytes += payloadSize;
+ if (payloadSize < 20)
+ {
+ _skipCnt++;
+ }
+ _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
+ return 0;
+}
+void
+VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
+{
+ _VCMReceiver = vcm;
+ return;
+}
+ WebRtc_Word32
+VCMNTEncodeCompleteCallback::EncodedBytes()
+{
+ return _encodedBytes;
+}
+
+WebRtc_UWord32
+VCMNTEncodeCompleteCallback::SkipCnt()
+{
+ return _skipCnt;
+}
+
+// Decoded Frame Callback Implmentation
+VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
+{
+ //
+}
+ WebRtc_Word32
+VCMNTDecodeCompleCallback::FrameToRender(webrtc::VideoFrame& videoFrame)
+{
+ if (videoFrame.Width() != _currentWidth ||
+ videoFrame.Height() != _currentHeight)
+ {
+ _currentWidth = videoFrame.Width();
+ _currentHeight = videoFrame.Height();
+ if (_decodedFile != NULL)
+ {
+ fclose(_decodedFile);
+ _decodedFile = NULL;
+ }
+ _decodedFile = fopen(_outname.c_str(), "wb");
+ }
+ fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
+ _decodedBytes+= videoFrame.Length();
+ return VCM_OK;
+}
+
+ WebRtc_Word32
+VCMNTDecodeCompleCallback::DecodedBytes()
+{
+ return _decodedBytes;
+}
+
+ //VCM Normal Test Class implementation
+
+NormalTest::NormalTest(VideoCodingModule* vcm)
+:
+_vcm(vcm),
+_totalEncodeTime(0),
+_totalDecodeTime(0),
+_decodeCompleteTime(0),
+_encodeCompleteTime(0),
+_totalEncodePipeTime(0),
+_totalDecodePipeTime(0),
+_frameCnt(0),
+_timeStamp(0),
+_encFrameCnt(0),
+_decFrameCnt(0),
+_sumEncBytes(0)
+
+{
+ //
+}
+
+NormalTest::~NormalTest()
+{
+ //
+}
+void
+NormalTest::Setup(CmdArgs& args)
+{
+ _inname = args.inputFile;
+ _encodedName = "encoded_normaltest.yuv";
+ _width = args.width;
+ _height = args.height;
+ _frameRate = args.frameRate;
+ _bitRate = args.bitRate;
+ if (args.outputFile == "")
+ {
+ std::ostringstream filename;
+ filename << "../NormalTest_" << _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv";
+ _outname = filename.str();
+ }
+ else
+ {
+ _outname = args.outputFile;
+ }
+ _lengthSourceFrame = 3*_width*_height/2;
+ _videoType = args.codecType;
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+
+ _log.open("../TestLog.txt", std::fstream::out | std::fstream::app);
+ return;
+}
+
+WebRtc_Word32
+NormalTest::Perform(CmdArgs& args)
+{
+ Setup(args);
+ EventWrapper* waitEvent = EventWrapper::Create();
+ VideoCodec _sendCodec;//, _receiveCodec; // tmp - sendCodecd used as receive codec
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK);
+ _sendCodec.startBitrate = (int)_bitRate; // should be later on changed via the API
+ _sendCodec.width = static_cast<WebRtc_UWord16>(_width);
+ _sendCodec.height = static_cast<WebRtc_UWord16>(_height);
+ _sendCodec.maxFramerate = _frameRate;
+ TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);// will also set and init the desired codec
+ // register a decoder (same codec for decoder and encoder )
+ TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK);
+ /* Callback Settings */
+ VCMNTDecodeCompleCallback _decodeCallback(_outname);
+ _vcm->RegisterReceiveCallback(&_decodeCallback);
+ VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
+ _vcm->RegisterTransportCallback(&_encodeCompleteCallback);
+ // encode and decode with the same vcm
+ _encodeCompleteCallback.RegisterReceiverVCM(_vcm);
+ ///////////////////////
+ /// Start Test
+ ///////////////////////
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
+ double startTime = clock()/(double)CLOCKS_PER_SEC;
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
+
+ SendStatsTest sendStats;
+ sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
+ _vcm->RegisterSendStatisticsCallback(&sendStats);
+
+ while (feof(_sourceFile)== 0)
+ {
+ WebRtc_Word64 processStartTime = VCMTickTime::MillisecondTimestamp();
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ _frameCnt++;
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+ _encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC;
+ WebRtc_Word32 ret = _vcm->AddVideoFrame(sourceFrame);
+ double encodeTime = clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())];
+ _totalEncodeTime += encodeTime;
+ if (ret < 0)
+ {
+ printf("Error in AddFrame: %d\n", ret);
+ //exit(1);
+ }
+ _decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode
+ ret = _vcm->Decode();
+ _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())];
+ if (ret < 0)
+ {
+ printf("Error in Decode: %d\n", ret);
+ //exit(1);
+ }
+ if (_vcm->TimeUntilNextProcess() <= 0)
+ {
+ _vcm->Process();
+ }
+ WebRtc_UWord32 framePeriod = static_cast<WebRtc_UWord32>(1000.0f/static_cast<float>(_sendCodec.maxFramerate) + 0.5f);
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ for (int i=0; i < framePeriod; i++)
+ {
+ VCMTickTime::IncrementDebugClock();
+ }
+#else
+ WebRtc_Word64 timeSpent = VCMTickTime::MillisecondTimestamp() - processStartTime;
+ if (timeSpent < framePeriod)
+ {
+ waitEvent->Wait(framePeriod - timeSpent);
+ }
+#endif
+ }
+ double endTime = clock()/(double)CLOCKS_PER_SEC;
+ _testTotalTime = endTime - startTime;
+ _sumEncBytes = _encodeCompleteCallback.EncodedBytes();
+
+ delete tmpBuffer;
+ delete waitEvent;
+ Teardown();
+ Print();
+ return 0;
+}
+
+void
+NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp)
+{
+ _encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
+ _encFrameCnt++;
+ _totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)];
+
+}
+
+void
+NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp)
+{
+ _decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
+ _decFrameCnt++;
+ _totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp];
+}
+
+void
+NormalTest::Print()
+{
+ std::cout << "Normal Test Completed!" << std::endl;
+ (_log) << "Normal Test Completed!" << std::endl;
+ (_log) << "Input file: " << _inname << std::endl;
+ (_log) << "Output file: " << _outname << std::endl;
+ (_log) << "Total run time: " << _testTotalTime << std::endl;
+ printf("Total run time: %f s \n", _testTotalTime);
+ double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
+ double actualBitRate = ActualBitRate / 1000.0;
+ double avgEncTime = _totalEncodeTime / _frameCnt;
+ double avgDecTime = _totalDecodeTime / _frameCnt;
+ double psnr;
+ PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr);
+ printf("Actual bitrate: %f kbps\n", actualBitRate);
+ printf("Target bitrate: %f kbps\n", _bitRate);
+ ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
+ printf("Average encode time: %f s\n", avgEncTime);
+ ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
+ printf("Average decode time: %f s\n", avgDecTime);
+ ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
+ printf("PSNR: %f \n", psnr);
+ ( _log) << "PSNR: " << psnr << std::endl;
+ (_log) << std::endl;
+}
+void
+NormalTest::Teardown()
+{
+ //_log.close();
+ fclose(_sourceFile);
+ fclose(_encodedFile);
+ return;
+}
+
+
+
+
diff --git a/src/modules/video_coding/main/test/normal_test.h b/src/modules/video_coding/main/test/normal_test.h
new file mode 100644
index 0000000..25af174
--- /dev/null
+++ b/src/modules/video_coding/main/test/normal_test.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
+
+#include "video_coding.h"
+#include "test_macros.h"
+#include "test_util.h"
+
+#include <map>
+
+class NormalTest;
+
+//Send Side - Packetization callback - will create and send a packet to the VCMReceiver
+class VCMNTEncodeCompleteCallback : public webrtc::VCMPacketizationCallback
+{
+public:
+ // constructor input: file in which encoded data will be written
+ VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test);
+ virtual ~VCMNTEncodeCompleteCallback();
+ // Register transport callback
+ void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport);
+ // process encoded data received from the encoder, pass stream to the VCMReceiver module
+ WebRtc_Word32 SendData(const webrtc::FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const webrtc::RTPFragmentationHeader& fragmentationHeader);
+
+ // Register exisitng VCM. Currently - encode and decode with the same vcm module.
+ void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm);
+ // Return sum of encoded data (all frames in the sequence)
+ WebRtc_Word32 EncodedBytes();
+ // return number of encoder-skipped frames
+ WebRtc_UWord32 SkipCnt();;
+ // conversion function for payload type (needed for the callback function)
+// RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType);
+
+private:
+ FILE* _encodedFile;
+ WebRtc_UWord32 _encodedBytes;
+ WebRtc_UWord32 _skipCnt;
+ webrtc::VideoCodingModule* _VCMReceiver;
+ webrtc::FrameType _frameType;
+ WebRtc_UWord8* _payloadData; // max payload size??
+ WebRtc_UWord16 _seqNo;
+ WebRtc_UWord8 _layerPacketId;
+ NormalTest& _test;
+ // int _vcmMacrosTests;
+ // int _vcmMacrosErrors;
+
+}; // end of VCMEncodeCompleteCallback
+
+class VCMNTDecodeCompleCallback: public webrtc::VCMReceiveCallback
+{
+public:
+ VCMNTDecodeCompleCallback(std::string outname): // or should it get a name?
+ _outname(outname),
+ _decodedFile(NULL),
+ _decodedBytes(0),
+ _currentWidth(0),
+ _currentHeight(0) {}
+ virtual ~VCMNTDecodeCompleCallback();
+ void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback);
+ // will write decoded frame into file
+ WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
+ WebRtc_Word32 DecodedBytes();
+private:
+ FILE* _decodedFile;
+ std::string _outname;
+ WebRtc_UWord32 _decodedBytes;
+ WebRtc_UWord32 _currentWidth;
+ WebRtc_UWord32 _currentHeight;
+
+}; // end of VCMDecodeCompleCallback class
+
+
+class NormalTest
+{
+public:
+ NormalTest(webrtc::VideoCodingModule* vcm);
+ ~NormalTest();
+ static int RunTest(CmdArgs& args);
+ WebRtc_Word32 Perform(CmdArgs& args);
+ // option:: turn into private and call from perform
+ WebRtc_UWord32 Width() const { return _width; };
+ WebRtc_UWord32 Height() const { return _height; };
+ webrtc::VideoCodecType VideoType() const { return _videoType; };
+
+
+protected:
+ // test setup - open files, general initializations
+ void Setup(CmdArgs& args);
+ // close open files, delete used memory
+ void Teardown();
+ // print results to std output and to log file
+ void Print();
+ // calculating pipeline delay, and encoding time
+ void FrameEncoded(WebRtc_UWord32 timeStamp);
+ // calculating pipeline delay, and decoding time
+ void FrameDecoded(WebRtc_UWord32 timeStamp);
+
+ webrtc::VideoCodingModule* _vcm;
+ webrtc::VideoCodec _sendCodec;
+ webrtc::VideoCodec _receiveCodec;
+ std::string _inname;
+ std::string _outname;
+ std::string _encodedName;
+ WebRtc_Word32 _sumEncBytes;
+ FILE* _sourceFile;
+ FILE* _decodedFile;
+ FILE* _encodedFile;
+ std::fstream _log;
+ WebRtc_UWord32 _width;
+ WebRtc_UWord32 _height;
+ float _frameRate;
+ float _bitRate;
+ WebRtc_UWord32 _lengthSourceFrame;
+ WebRtc_UWord32 _timeStamp;
+ webrtc::VideoCodecType _videoType;
+ double _totalEncodeTime;
+ double _totalDecodeTime;
+ double _decodeCompleteTime;
+ double _encodeCompleteTime;
+ double _totalEncodePipeTime;
+ double _totalDecodePipeTime;
+ double _testTotalTime;
+ std::map<int, double> _encodeTimes;
+ std::map<int, double> _decodeTimes;
+ WebRtc_Word32 _frameCnt;
+ WebRtc_Word32 _encFrameCnt;
+ WebRtc_Word32 _decFrameCnt;
+
+}; // end of VCMNormalTestClass
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
diff --git a/src/modules/video_coding/main/test/plotJitterEstimate.m b/src/modules/video_coding/main/test/plotJitterEstimate.m
new file mode 100644
index 0000000..d6185f5
--- /dev/null
+++ b/src/modules/video_coding/main/test/plotJitterEstimate.m
@@ -0,0 +1,52 @@
+function plotJitterEstimate(filename)
+
+[timestamps, framedata, slopes, randJitters, framestats, timetable, filtjitter, rtt, rttStatsVec] = jitterBufferTraceParser(filename);
+
+x = 1:size(framestats, 1);
+%figure(2);
+subfigure(3, 2, 1);
+hold on;
+plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)) + 3*sqrt(randJitters(x,2)), 'b'); title('Estimate ms');
+plot(x, filtjitter, 'r');
+plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)), 'g');
+subfigure(3, 2, 2);
+%subplot(211);
+plot(x, slopes(x, 1)); title('Line slope');
+%subplot(212);
+%plot(x, slopes(x, 2)); title('Line offset');
+subfigure(3, 2, 3); hold on;
+plot(x, framestats); plot(x, framedata(x, 1)); title('frame size and average frame size');
+subfigure(3, 2, 4);
+plot(x, framedata(x, 2)); title('Delay');
+subfigure(3, 2, 5);
+hold on;
+plot(x, randJitters(x,1),'r');
+plot(x, randJitters(x,2)); title('Random jitter');
+
+subfigure(3, 2, 6);
+delays = framedata(:,2);
+dL = [0; framedata(2:end, 1) - framedata(1:end-1, 1)];
+hold on;
+plot(dL, delays, '.');
+s = [min(dL) max(dL)];
+plot(s, slopes(end, 1)*s + slopes(end, 2), 'g');
+plot(s, slopes(end, 1)*s + slopes(end, 2) + 3*sqrt(randJitters(end,2)), 'r');
+plot(s, slopes(end, 1)*s + slopes(end, 2) - 3*sqrt(randJitters(end,2)), 'r');
+title('theta(1)*x+theta(2), (dT-dTS)/dL');
+if sum(size(rttStatsVec)) > 0
+ figure; hold on;
+ rttNstdDevsDrift = 3.5;
+ rttNstdDevsJump = 2.5;
+ rttSamples = rttStatsVec(:, 1);
+ rttAvgs = rttStatsVec(:, 2);
+ rttStdDevs = sqrt(rttStatsVec(:, 3));
+ rttMax = rttStatsVec(:, 4);
+ plot(rttSamples, 'ko-');
+ plot(rttAvgs, 'g');
+ plot(rttAvgs + rttNstdDevsDrift*rttStdDevs, 'b--');
+ plot(rttAvgs + rttNstdDevsJump*rttStdDevs, 'b');
+ plot(rttAvgs - rttNstdDevsJump*rttStdDevs, 'b');
+ plot(rttMax, 'r');
+ %plot(driftRestarts*max(maxRtts), '.');
+ %plot(jumpRestarts*max(maxRtts), '.');
+end
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/plotReceiveTrace.m b/src/modules/video_coding/main/test/plotReceiveTrace.m
new file mode 100644
index 0000000..4d262aa
--- /dev/null
+++ b/src/modules/video_coding/main/test/plotReceiveTrace.m
@@ -0,0 +1,213 @@
+function [t, TS] = plotReceiveTrace(filename, flat)
+fid=fopen(filename);
+%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; First packet of frame 1869537938
+%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:1 ; 5260; Decoding timestamp 1869534934
+%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; Render frame 1869534934 at 20772610
+%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:-1 ; 5260; Frame decoded: timeStamp=1870511259 decTime=0 maxDecTime=0, at 19965
+%DEBUG ; ( 7:59:42:500 | 0) VIDEO:-1 ; 2500; Received complete frame timestamp 1870514263 frame type 1 frame size 7862 at time 19965, jitter estimate was 130
+%DEBUG ; ( 8: 5:51:774 | 0) VIDEO:-1 ; 3968; ExtrapolateLocalTime(1870967878)=24971 ms
+
+if nargin == 1
+ flat = 0;
+end
+line = fgetl(fid);
+estimatedArrivalTime = [];
+packetTime = [];
+firstPacketTime = [];
+decodeTime = [];
+decodeCompleteTime = [];
+renderTime = [];
+completeTime = [];
+while ischar(line)%line ~= -1
+ if length(line) == 0
+ line = fgetl(fid);
+ continue;
+ end
+ % Parse the trace line header
+ [tempres, count] = sscanf(line, 'DEBUG ; (%u:%u:%u:%u |%*lu)%13c:');
+ if count < 5
+ line = fgetl(fid);
+ continue;
+ end
+ hr=tempres(1);
+ mn=tempres(2);
+ sec=tempres(3);
+ ms=tempres(4);
+ timeInMs=hr*60*60*1000 + mn*60*1000 + sec*1000 + ms;
+ label = tempres(5:end);
+ I = find(label ~= 32);
+ label = label(I(1):end); % remove white spaces
+ if ~strncmp(char(label), 'VIDEO', 5) & ~strncmp(char(label), 'VIDEO CODING', 12)
+ line = fgetl(fid);
+ continue;
+ end
+ message = line(72:end);
+
+ % Parse message
+ [p, count] = sscanf(message, 'ExtrapolateLocalTime(%lu)=%lu ms');
+ if count == 2
+ estimatedArrivalTime = [estimatedArrivalTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'Packet seqNo %u of frame %lu at %lu');
+ if count == 3
+ packetTime = [packetTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'First packet of frame %lu at %lu');
+ if count == 2
+ firstPacketTime = [firstPacketTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'Decoding timestamp %lu at %lu');
+ if count == 2
+ decodeTime = [decodeTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'Render frame %lu at %lu. Render delay %lu, required delay %lu, max decode time %lu, min total delay %lu');
+ if count == 6
+ renderTime = [renderTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%lu, at %lu');
+ if count == 4
+ decodeCompleteTime = [decodeCompleteTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ [p, count] = sscanf(message, 'Received complete frame timestamp %lu frame type %u frame size %*u at time %lu, jitter estimate was %lu');
+ if count == 4
+ completeTime = [completeTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+
+ line = fgetl(fid);
+end
+fclose(fid);
+
+t = completeTime(:,3);
+TS = completeTime(:,1);
+
+figure;
+subplot(211);
+hold on;
+slope = 0;
+
+if sum(size(packetTime)) > 0
+ % Plot the time when each packet arrives
+ firstTimeStamp = packetTime(1,2);
+ x = (packetTime(:,2) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ firstTime = packetTime(1,3);
+ plot(x, packetTime(:,3) - firstTime - slope, 'b.');
+else
+ % Plot the time when the first packet of a frame arrives
+ firstTimeStamp = firstPacketTime(1,1);
+ x = (firstPacketTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ firstTime = firstPacketTime(1,2);
+ plot(x, firstPacketTime(:,2) - firstTime - slope, 'b.');
+end
+
+% Plot the frame complete time
+if prod(size(completeTime)) > 0
+ x = (completeTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, completeTime(:,3) - firstTime - slope, 'ks');
+end
+
+% Plot the time the decode starts
+if prod(size(decodeTime)) > 0
+ x = (decodeTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, decodeTime(:,2) - firstTime - slope, 'r.');
+end
+
+% Plot the decode complete time
+if prod(size(decodeCompleteTime)) > 0
+ x = (decodeCompleteTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, decodeCompleteTime(:,4) - firstTime - slope, 'g.');
+end
+
+if prod(size(renderTime)) > 0
+ % Plot the wanted render time in ms
+ x = (renderTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, renderTime(:,2) - firstTime - slope, 'c-');
+
+ % Plot the render time if there were no render delay or decoding delay.
+ x = (renderTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'c--');
+
+ % Plot the render time if there were no render delay.
+ x = (renderTime(:,1) - firstTimeStamp)/90;
+ if flat
+ slope = x;
+ end
+ plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'b-');
+end
+
+%plot(x, 90*x, 'r-');
+
+xlabel('RTP timestamp (in ms)');
+ylabel('Time (ms)');
+legend('Packet arrives', 'Frame complete', 'Decode', 'Decode complete', 'Time to render', 'Only jitter', 'Must decode');
+
+% subplot(312);
+% hold on;
+% completeTs = completeTime(:, 1);
+% arrivalTs = estimatedArrivalTime(:, 1);
+% [c, completeIdx, arrivalIdx] = intersect(completeTs, arrivalTs);
+% %plot(completeTs(completeIdx), completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2));
+% timeUntilComplete = completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2);
+% devFromAvgCompleteTime = timeUntilComplete - mean(timeUntilComplete);
+% plot(completeTs(completeIdx) - completeTs(completeIdx(1)), devFromAvgCompleteTime);
+% plot(completeTime(:, 1) - completeTime(1, 1), completeTime(:, 4), 'r');
+% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 2), 'g');
+% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 3), 'k');
+% xlabel('RTP timestamp');
+% ylabel('Time (ms)');
+% legend('Complete time - Estimated arrival time', 'Desired jitter buffer level', 'Actual decode time', 'Max decode time', 0);
+
+if prod(size(renderTime)) > 0
+ subplot(212);
+ hold on;
+ firstTime = renderTime(1, 1);
+ targetDelay = max(renderTime(:, 3) + renderTime(:, 4) + renderTime(:, 5), renderTime(:, 6));
+ plot(renderTime(:, 1) - firstTime, renderTime(:, 3), 'r-');
+ plot(renderTime(:, 1) - firstTime, renderTime(:, 4), 'b-');
+ plot(renderTime(:, 1) - firstTime, renderTime(:, 5), 'g-');
+ plot(renderTime(:, 1) - firstTime, renderTime(:, 6), 'k-');
+ plot(renderTime(:, 1) - firstTime, targetDelay, 'c-');
+ xlabel('RTP timestamp');
+ ylabel('Time (ms)');
+ legend('Render delay', 'Jitter delay', 'Decode delay', 'Extra delay', 'Min total delay');
+end
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/plotTimingTest.m b/src/modules/video_coding/main/test/plotTimingTest.m
new file mode 100644
index 0000000..52a6f30
--- /dev/null
+++ b/src/modules/video_coding/main/test/plotTimingTest.m
@@ -0,0 +1,62 @@
+function plotTimingTest(filename)
+fid=fopen(filename);
+
+%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; Stochastic test 1
+%DEBUG ; ( 9:53:33:859 | 0) VIDEO CODING:-1 ; 7132; Frame decoded: timeStamp=3000 decTime=10 at 10012
+%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStamp=3000 clock=10037 maxWaitTime=0
+%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStampMs=33 renderTime=54
+line = fgetl(fid);
+decTime = [];
+waitTime = [];
+renderTime = [];
+foundStart = 0;
+testName = 'Stochastic test 1';
+while ischar(line)
+ if length(line) == 0
+ line = fgetl(fid);
+ continue;
+ end
+ lineOrig = line;
+ line = line(72:end);
+ if ~foundStart
+ if strncmp(line, testName, length(testName))
+ foundStart = 1;
+ end
+ line = fgetl(fid);
+ continue;
+ end
+ [p, count] = sscanf(line, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%d, at %lu');
+ if count == 4
+ decTime = [decTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+ [p, count] = sscanf(line, 'timeStamp=%u clock=%u maxWaitTime=%u');
+ if count == 3
+ waitTime = [waitTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+ [p, count] = sscanf(line, 'timeStamp=%u renderTime=%u');
+ if count == 2
+ renderTime = [renderTime; p'];
+ line = fgetl(fid);
+ continue;
+ end
+ line = fgetl(fid);
+end
+fclose(fid);
+
+% Compensate for wrap arounds and start counting from zero.
+timeStamps = waitTime(:, 1);
+tsDiff = diff(timeStamps);
+wrapIdx = find(tsDiff < 0);
+timeStamps(wrapIdx+1:end) = hex2dec('ffffffff') + timeStamps(wrapIdx+1:end);
+timeStamps = timeStamps - timeStamps(1);
+
+figure;
+hold on;
+plot(timeStamps, decTime(:, 2), 'r');
+plot(timeStamps, waitTime(:, 3), 'g');
+plot(timeStamps(2:end), diff(renderTime(:, 2)), 'b');
+legend('Decode time', 'Max wait time', 'Render time diff');
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/quality_modes_test.cc b/src/modules/video_coding/main/test/quality_modes_test.cc
new file mode 100644
index 0000000..f161fc4
--- /dev/null
+++ b/src/modules/video_coding/main/test/quality_modes_test.cc
@@ -0,0 +1,499 @@
+/*
+ * 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 "quality_modes_test.h"
+#include "../source/event.h"
+#include "vplib.h"
+
+#include <iostream>
+#include <string>
+#include <time.h>
+
+using namespace webrtc;
+
+int qualityModeTest()
+{
+ // Don't run this test with debug time
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ return -1;
+#endif
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ QualityModesTest QMTest(vcm);
+ QMTest.Perform();
+ VideoCodingModule::Destroy(vcm);
+ return 0;
+}
+
+
+QualityModesTest::QualityModesTest(VideoCodingModule *vcm):
+NormalTest(vcm),
+_vpm()
+{
+ //
+}
+
+
+QualityModesTest::~QualityModesTest()
+{
+ //
+}
+
+void
+QualityModesTest::Setup()
+{
+
+
+ _inname= "../codecs/testFiles/database/crew_30f_4CIF.yuv";
+ _outname = "../out_qmtest.yuv";
+ _encodedName = "../encoded_qmtest.yuv";
+
+ //NATIVE/SOURCE VALUES
+ _nativeWidth = 2*352;
+ _nativeHeight = 2*288;
+ _nativeFrameRate = 30;
+
+
+ //TARGET/ENCODER VALUES
+ _width = 2*352;
+ _height = 2*288;
+ _frameRate = 30;
+ //
+ _bitRate = 400;
+
+ _flagSSIM = false;
+
+ _lengthSourceFrame = 3*_nativeWidth*_nativeHeight/2;
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+ if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write file %s.\n", _outname.c_str());
+ exit(1);
+ }
+
+ _log.open("../TestLog.txt", std::fstream::out | std::fstream::app);
+ return;
+}
+
+
+void
+QualityModesTest::Print()
+{
+ std::cout << "Quality Modes Test Completed!" << std::endl;
+ (_log) << "Quality Modes Test Completed!" << std::endl;
+ (_log) << "Input file: " << _inname << std::endl;
+ (_log) << "Output file: " << _outname << std::endl;
+ (_log) << "Total run time: " << _testTotalTime << std::endl;
+ printf("Total run time: %f s \n", _testTotalTime);
+ double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _nativeFrameRate));
+ double actualBitRate = ActualBitRate / 1000.0;
+ double avgEncTime = _totalEncodeTime / _frameCnt;
+ double avgDecTime = _totalDecodeTime / _frameCnt;
+ double psnr,ssim;
+ PSNRfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &psnr);
+ printf("Actual bitrate: %f kbps\n", actualBitRate);
+ printf("Target bitrate: %f kbps\n", _bitRate);
+ ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
+ printf("Average encode time: %f s\n", avgEncTime);
+ ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
+ printf("Average decode time: %f s\n", avgDecTime);
+ ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
+ printf("PSNR: %f \n", psnr);
+ printf("**Number of frames dropped in VPM***%d \n",_numFramesDroppedVPM);
+ ( _log) << "PSNR: " << psnr << std::endl;
+ if (_flagSSIM == 1)
+ {
+ printf("***computing SSIM***\n");
+ SSIMfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &ssim);
+ printf("SSIM: %f \n", ssim);
+ }
+ (_log) << std::endl;
+}
+void
+QualityModesTest::Teardown()
+{
+ _log.close();
+ fclose(_sourceFile);
+ fclose(_decodedFile);
+ fclose(_encodedFile);
+ return;
+}
+
+
+WebRtc_Word32
+QualityModesTest::Perform()
+{
+ Setup();
+ // changing bit/frame rate during the test
+ const float bitRateUpdate[] = {1000};
+ const float frameRateUpdate[] = {30};
+ const int updateFrameNum[] = {10000}; // frame numbers at which an update will occur
+
+ WebRtc_UWord32 numChanges = sizeof(updateFrameNum)/sizeof(*updateFrameNum);
+ WebRtc_UWord8 change = 0;// change counter
+
+ _vpm = VideoProcessingModule::Create(1);
+
+ EventWrapper* waitEvent = EventWrapper::Create();
+ VideoCodec codec;//both send and receive
+ _vcm->InitializeReceiver();
+ _vcm->InitializeSender();
+ WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs();
+ for (int i = 0; i < NumberOfCodecs; i++)
+ {
+ _vcm->Codec(i, &codec);
+ if(strncmp(codec.plName,"VP8" , 5) == 0)
+ {
+ codec.startBitrate = (int)_bitRate;
+ codec.maxFramerate = (WebRtc_UWord8) _frameRate;
+ codec.width = (WebRtc_UWord16)_width;
+ codec.height = (WebRtc_UWord16)_height;
+ TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec
+ i = NumberOfCodecs;
+ }
+ }
+
+ // register a decoder (same codec for decoder and encoder )
+ TEST(_vcm->RegisterReceiveCodec(&codec, 2) == VCM_OK);
+ /* Callback Settings */
+ VCMQMDecodeCompleCallback _decodeCallback(_decodedFile);
+ _vcm->RegisterReceiveCallback(&_decodeCallback);
+ VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
+ _vcm->RegisterTransportCallback(&_encodeCompleteCallback);
+ // encode and decode with the same vcm
+ _encodeCompleteCallback.RegisterReceiverVCM(_vcm);
+
+ //quality modes callback
+ QMTestVideoSettingsCallback QMCallback;
+ QMCallback.RegisterVCM(_vcm);
+ QMCallback.RegisterVPM(_vpm);
+ _vcm->RegisterVideoQMCallback(&QMCallback);
+
+ ///////////////////////
+ /// Start Test
+ ///////////////////////
+ _vpm->EnableTemporalDecimation(true);
+ _vpm->EnableContentAnalysis(true);
+ _vpm->SetInputFrameResampleMode(kFastRescaling);
+
+ // disabling internal VCM frame dropper
+ _vcm->EnableFrameDropper(false);
+
+ VideoFrame sourceFrame;
+ VideoFrame *decimatedFrame = NULL;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
+ double startTime = clock()/(double)CLOCKS_PER_SEC;
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
+
+ SendStatsTest sendStats;
+ sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
+ _vcm->RegisterSendStatisticsCallback(&sendStats);
+
+ VideoContentMetrics* contentMetrics = NULL;
+ // setting user frame rate
+ _vpm->SetMaxFrameRate((WebRtc_UWord32)(_nativeFrameRate+ 0.5f));
+ // for starters: keeping native values:
+ _vpm->SetTargetResolution(_width, _height, (WebRtc_UWord32)(_frameRate+ 0.5f));
+ _decodeCallback.SetOriginalFrameDimensions(_nativeWidth, _nativeHeight);
+
+ //tmp - disabling VPM frame dropping
+ _vpm->EnableTemporalDecimation(false);
+
+
+ WebRtc_Word32 ret = 0;
+ _numFramesDroppedVPM = 0;
+
+ while (feof(_sourceFile)== 0)
+ {
+ fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
+ _frameCnt++;
+ sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
+ sourceFrame.SetHeight(_nativeHeight);
+ sourceFrame.SetWidth(_nativeWidth);
+
+ _timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(codec.maxFramerate));
+ sourceFrame.SetTimeStamp(_timeStamp);
+
+ ret = _vpm->PreprocessFrame(&sourceFrame, &decimatedFrame);
+ if (ret == 1)
+ {
+ printf("VD: frame drop %d \n",_frameCnt);
+ _numFramesDroppedVPM += 1;
+ continue; // frame drop
+ }
+ else if (ret < 0)
+ {
+ printf("Error in PreprocessFrame: %d\n", ret);
+ //exit(1);
+ }
+ contentMetrics = _vpm->ContentMetrics();
+ if (contentMetrics == NULL)
+ {
+ printf("error: contentMetrics = NULL\n");
+ }
+
+ // counting only encoding time
+ _encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC;
+
+ WebRtc_Word32 ret = _vcm->AddVideoFrame(*decimatedFrame, contentMetrics);
+
+ _totalEncodeTime += clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())];
+
+ if (ret < 0)
+ {
+ printf("Error in AddFrame: %d\n", ret);
+ //exit(1);
+ }
+ _decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode
+ ret = _vcm->Decode();
+ _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())];
+ if (ret < 0)
+ {
+ printf("Error in Decode: %d\n", ret);
+ //exit(1);
+ }
+ if (_vcm->TimeUntilNextProcess() <= 0)
+ {
+ _vcm->Process();
+ }
+ // mimicking setTargetRates - update every 1 sec
+ // this will trigger QMSelect
+ if (_frameCnt%((int)_frameRate) == 0)
+ {
+ _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 1);
+ waitEvent->Wait(33);
+ }
+ waitEvent->Wait(33);
+ // check for bit rate update
+ if (change < numChanges && _frameCnt == updateFrameNum[change])
+ {
+ _bitRate = bitRateUpdate[change];
+ _frameRate = frameRateUpdate[change];
+ codec.startBitrate = (int)_bitRate;
+ codec.maxFramerate = (WebRtc_UWord8) _frameRate;
+ TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec
+ change++;
+ }
+ }
+
+ double endTime = clock()/(double)CLOCKS_PER_SEC;
+ _testTotalTime = endTime - startTime;
+ _sumEncBytes = _encodeCompleteCallback.EncodedBytes();
+
+ delete tmpBuffer;
+ delete waitEvent;
+ _vpm->Reset();
+ Teardown();
+ Print();
+ VideoProcessingModule::Destroy(_vpm);
+ return 0;
+}
+
+
+// implementing callback to be called from VCM to update VPM of frame rate and size
+QMTestVideoSettingsCallback::QMTestVideoSettingsCallback():
+_vpm(NULL),
+_vcm(NULL)
+{
+ //
+}
+
+void
+QMTestVideoSettingsCallback::RegisterVPM(VideoProcessingModule *vpm)
+{
+ _vpm = vpm;
+}
+void
+QMTestVideoSettingsCallback::RegisterVCM(VideoCodingModule *vcm)
+{
+ _vcm = vcm;
+}
+
+bool
+QMTestVideoSettingsCallback::Updated()
+{
+ if (_updated)
+ {
+ _updated = false;
+ return true;
+ }
+ return false;
+}
+
+WebRtc_Word32
+QMTestVideoSettingsCallback::SetVideoQMSettings(const WebRtc_UWord32 frameRate,
+ const WebRtc_UWord32 width,
+ const WebRtc_UWord32 height)
+{
+ WebRtc_Word32 retVal = 0;
+ printf("QM updates: W = %d, H = %d, FR = %d, \n", width, height, frameRate);
+ retVal = _vpm->SetTargetResolution(width, height, frameRate);
+ //Initialize codec with new values - is this the best place to do it?
+ if (!retVal)
+ {
+ // first get current settings
+ VideoCodec currentCodec;
+ _vcm->SendCodec(¤tCodec);
+ // now set new values:
+ currentCodec.height = (WebRtc_UWord16)height;
+ currentCodec.width = (WebRtc_UWord16)width;
+ currentCodec.maxFramerate = (WebRtc_UWord8)frameRate;
+
+ // re-register encoder
+ retVal = _vcm->RegisterSendCodec(¤tCodec, 2, 1440);
+ _updated = true;
+ }
+
+ return retVal;
+}
+
+
+// Decoded Frame Callback Implmentation
+VCMQMDecodeCompleCallback::VCMQMDecodeCompleCallback(FILE* decodedFile):
+_decodedFile(decodedFile),
+_decodedBytes(0),
+//_test(test),
+_origWidth(0),
+_origHeight(0),
+_decWidth(0),
+_decHeight(0),
+//_interpolator(NULL),
+_decBuffer(NULL),
+_frameCnt(0)
+{
+ //
+}
+
+VCMQMDecodeCompleCallback::~VCMQMDecodeCompleCallback()
+ {
+// if (_interpolator != NULL)
+// {
+// deleteInterpolator(_interpolator);
+// _interpolator = NULL;
+// }
+ if (_decBuffer != NULL)
+ {
+ delete [] _decBuffer;
+ _decBuffer = NULL;
+ }
+ }
+WebRtc_Word32
+VCMQMDecodeCompleCallback::FrameToRender(VideoFrame& videoFrame)
+{
+ if ((_origWidth == videoFrame.Width()) && (_origHeight == videoFrame.Height()))
+ {
+ fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
+ _frameCnt++;
+ //printf("frame dec # %d", _frameCnt);
+ // no need for interpolator and decBuffer
+ if (_decBuffer != NULL)
+ {
+ delete [] _decBuffer;
+ _decBuffer = NULL;
+ }
+// if (_interpolator != NULL)
+// {
+// deleteInterpolator(_interpolator);
+// _interpolator = NULL;
+// }
+ _decWidth = 0;
+ _decHeight = 0;
+ }
+ else
+ {
+ if ((_decWidth != videoFrame.Width()) || (_decHeight != videoFrame.Height()))
+ {
+ _decWidth = videoFrame.Width();
+ _decHeight = videoFrame.Height();
+ buildInterpolator();
+ }
+
+// interpolateFrame(_interpolator, videoFrame.Buffer(),_decBuffer);
+ fwrite(_decBuffer, 1, _origWidth*_origHeight*3/2, _decodedFile);
+ _frameCnt++;
+ }
+
+ _decodedBytes += videoFrame.Length();
+ return VCM_OK;
+}
+
+WebRtc_Word32
+VCMQMDecodeCompleCallback::DecodedBytes()
+{
+ return _decodedBytes;
+}
+
+void
+VCMQMDecodeCompleCallback::SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height)
+{
+ _origWidth = width;
+ _origHeight = height;
+}
+
+WebRtc_Word32
+VCMQMDecodeCompleCallback::buildInterpolator()
+{
+// if (_interpolator != NULL)
+// {
+// deleteInterpolator(_interpolator);
+// _interpolator = NULL;
+// }
+
+ // create decimator
+ WebRtc_Word32 filterPar = 4; //Lanczos (assuming sampling ratio is 1, 1.5, 2, 4)
+
+ float HeightRatio = 0;
+ float WidthRatio = 0;
+
+ WidthRatio = (float)_origWidth/(float)_decWidth;
+ HeightRatio = (float)_origHeight/(float)_decHeight;
+
+ if ( (HeightRatio == 1.0 || HeightRatio == 1.5 || HeightRatio == 2 || HeightRatio == 4) &&
+ (WidthRatio == 1.0 || WidthRatio == 1.5 || WidthRatio == 2 || WidthRatio == 4))
+ {
+ filterPar = 4; //Lanczos
+ } else
+ {
+ filterPar = 0; //BiCubic
+ }
+
+
+ // define interpolator here
+
+ // create interpolator here
+
+// if (_interpolator == NULL)
+// {
+// return -1;
+// }
+
+ WebRtc_UWord32 decFrameLength = _origWidth*_origHeight*3 >> 1;
+ if (_decBuffer != NULL)
+ {
+ delete [] _decBuffer;
+ }
+ _decBuffer = new WebRtc_UWord8[decFrameLength];
+ if (_decBuffer == NULL)
+ {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/quality_modes_test.h b/src/modules/video_coding/main/test/quality_modes_test.h
new file mode 100644
index 0000000..dd1833a
--- /dev/null
+++ b/src/modules/video_coding/main/test/quality_modes_test.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
+
+#include "video_processing.h"
+#include "normal_test.h"
+#include "video_coding_defines.h"
+
+int qualityModeTest();
+
+class QualityModesTest : public NormalTest
+{
+public:
+ QualityModesTest(webrtc::VideoCodingModule* vcm);
+ virtual ~QualityModesTest();
+ WebRtc_Word32 Perform();
+
+private:
+
+ void Setup();
+ void Print();
+ void Teardown();
+ void SsimComp();
+
+ webrtc::VideoProcessingModule* _vpm;
+
+ WebRtc_UWord32 _width;
+ WebRtc_UWord32 _height;
+ float _frameRate;
+ WebRtc_UWord32 _nativeWidth;
+ WebRtc_UWord32 _nativeHeight;
+ float _nativeFrameRate;
+
+ WebRtc_UWord32 _numFramesDroppedVPM;
+ bool _flagSSIM;
+
+}; // end of QualityModesTest class
+
+
+class VCMQMDecodeCompleCallback: public webrtc::VCMReceiveCallback
+{
+public:
+ VCMQMDecodeCompleCallback(FILE* decodedFile);
+ virtual ~VCMQMDecodeCompleCallback();
+ void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback);
+ // will write decoded frame into file
+ WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
+ WebRtc_Word32 DecodedBytes();
+ void SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height);
+ WebRtc_Word32 buildInterpolator();
+private:
+ FILE* _decodedFile;
+ WebRtc_UWord32 _decodedBytes;
+ // QualityModesTest& _test;
+ WebRtc_Word32 _origWidth;
+ WebRtc_Word32 _origHeight;
+ WebRtc_Word32 _decWidth;
+ WebRtc_Word32 _decHeight;
+// VideoInterpolator* _interpolator;
+ WebRtc_UWord8* _decBuffer;
+ WebRtc_UWord32 _frameCnt; // debug
+
+}; // end of VCMQMDecodeCompleCallback class
+
+class QMTestVideoSettingsCallback : public webrtc::VCMQMSettingsCallback
+{
+public:
+ QMTestVideoSettingsCallback();
+ // update VPM with QM settings
+ WebRtc_Word32 SetVideoQMSettings(const WebRtc_UWord32 frameRate,
+ const WebRtc_UWord32 width,
+ const WebRtc_UWord32 height);
+ // register VPM used by test
+ void RegisterVPM(webrtc::VideoProcessingModule* vpm);
+ void RegisterVCM(webrtc::VideoCodingModule* vcm);
+ bool Updated();
+
+private:
+ webrtc::VideoProcessingModule* _vpm;
+ webrtc::VideoCodingModule* _vcm;
+ bool _updated;
+};
+
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
diff --git a/src/modules/video_coding/main/test/receiver_tests.h b/src/modules/video_coding/main/test/receiver_tests.h
new file mode 100644
index 0000000..92b9099
--- /dev/null
+++ b/src/modules/video_coding/main/test/receiver_tests.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
+
+#include "video_coding.h"
+#include "module_common_types.h"
+#include "common_types.h"
+#include "rtp_rtcp.h"
+#include "typedefs.h"
+#include "rtp_player.h"
+#include "test_util.h"
+
+#include <string>
+#include <stdio.h>
+
+class RtpDataCallback : public webrtc::RtpData
+{
+public:
+ RtpDataCallback(webrtc::VideoCodingModule* vcm)
+ : _vcm(vcm) {};
+
+ virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord16 payloadSize,
+ const webrtc::WebRtcRTPHeader* rtpHeader);
+private:
+ webrtc::VideoCodingModule* _vcm;
+};
+
+class FrameReceiveCallback : public webrtc::VCMReceiveCallback
+{
+public:
+ FrameReceiveCallback(std::string outFilename) :
+ _outFilename(outFilename),
+ _outFile(NULL),
+ _timingFile(NULL) {}
+
+ virtual ~FrameReceiveCallback();
+
+ WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
+
+private:
+ std::string _outFilename;
+ FILE* _outFile;
+ FILE* _timingFile;
+};
+
+class SharedState
+{
+public:
+ SharedState(webrtc::VideoCodingModule& vcm, RTPPlayer& rtpPlayer) :
+ _rtpPlayer(rtpPlayer),
+ _vcm(vcm) {}
+ webrtc::VideoCodingModule& _vcm;
+ RTPPlayer& _rtpPlayer;
+};
+
+class SharedRTPState
+{
+public:
+ SharedRTPState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp) :
+ _rtp(rtp),
+ _vcm(vcm) {}
+ webrtc::VideoCodingModule& _vcm;
+ webrtc::RtpRtcp& _rtp;
+};
+
+int RtpPlay(CmdArgs& args);
+int RtpPlayMT(CmdArgs& args,
+ int releaseTest = 0,
+ webrtc::VideoCodecType releaseTestVideoType = webrtc::kVideoCodecVP8);
+int ReceiverTimingTests(CmdArgs& args);
+int JitterBufferTest(CmdArgs& args);
+int DecodeFromStorageTest(CmdArgs& args);
+
+// Thread functions:
+bool ProcessingThread(void* obj);
+bool RtpReaderThread(void* obj);
+bool DecodeThread(void* obj);
+bool NackThread(void* obj);
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
diff --git a/src/modules/video_coding/main/test/receiver_timing_tests.cc b/src/modules/video_coding/main/test/receiver_timing_tests.cc
new file mode 100644
index 0000000..ce2c8bb
--- /dev/null
+++ b/src/modules/video_coding/main/test/receiver_timing_tests.cc
@@ -0,0 +1,227 @@
+/*
+ * 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 "receiver_tests.h"
+#include "video_coding.h"
+#include "trace.h"
+#include "tick_time.h"
+#include "../source/event.h"
+#include "../source/internal_defines.h"
+#include "timing.h"
+#include "test_macros.h"
+#include "test_util.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+
+using namespace webrtc;
+
+float vcmFloatMax(float a, float b)
+{
+ return a > b ? a : b;
+}
+
+float vcmFloatMin(float a, float b)
+{
+ return a < b ? a : b;
+}
+
+double const pi = 4*std::atan(1.0);
+
+class GaussDist
+{
+public:
+ static float RandValue(float m, float stdDev) // returns a single normally distributed number
+ {
+ float r1 = static_cast<float>((std::rand() + 1.0)/(RAND_MAX + 1.0)); // gives equal distribution in (0, 1]
+ float r2 = static_cast<float>((std::rand() + 1.0)/(RAND_MAX + 1.0));
+ return m + stdDev * static_cast<float>(std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2));
+ }
+};
+
+int ReceiverTimingTests(CmdArgs& args)
+{
+ // Make sure this test is never executed with simulated clocks
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ return -1;
+#endif
+
+ // Set up trace
+ Trace::CreateTrace();
+ Trace::SetTraceFile("receiverTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+
+ // A static random seed
+ srand(0);
+
+ VCMTiming timing;
+ float clockInMs = 0.0;
+ WebRtc_UWord32 waitTime = 0;
+ WebRtc_Word32 jitterDelayMs = 0;
+ WebRtc_Word32 maxDecodeTimeMs = 0;
+ WebRtc_Word32 extraDelayMs = 0;
+ WebRtc_UWord32 timeStamp = 0;
+
+ timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
+
+ timing.UpdateCurrentDelay(timeStamp);
+ TEST(timing.MaxWaitingTime(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)) >= 0);
+
+ timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
+
+ timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ jitterDelayMs = 20;
+ timing.SetRequiredDelay(jitterDelayMs);
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ // First update initializes the render time. Since we have no decode delay
+ // we get waitTime = renderTime - now - renderDelay = jitter
+ TEST(waitTime == jitterDelayMs);
+
+ jitterDelayMs += VCMTiming::kDelayMaxChangeMsPerS + 10;
+ timeStamp += 90000;
+ clockInMs += 1000.0f;
+ timing.SetRequiredDelay(jitterDelayMs);
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ // Since we gradually increase the delay we only get
+ // 100 ms every second.
+ TEST(waitTime == jitterDelayMs - 10);
+
+ timeStamp += 90000;
+ clockInMs += 1000.0;
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ TEST(waitTime == jitterDelayMs);
+
+ // 300 incoming frames without jitter, verify that this gives the exact wait time
+ for (int i=0; i < 300; i++)
+ {
+ clockInMs += 1000.0f/30.0f;
+ timeStamp += 3000;
+ timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ }
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ TEST(waitTime == jitterDelayMs);
+
+ // Add decode time estimates
+ for (int i=0; i < 10; i++)
+ {
+ WebRtc_Word64 startTimeMs = static_cast<WebRtc_Word64>(clockInMs + 0.5);
+ clockInMs += 10.0f;
+ timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ timeStamp += 3000;
+ clockInMs += 1000.0f/30.0f - 10.0f;
+ timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ }
+ maxDecodeTimeMs = 10;
+ timing.SetRequiredDelay(jitterDelayMs);
+ clockInMs += 1000.0f;
+ timeStamp += 90000;
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ TEST(waitTime == jitterDelayMs);
+
+ WebRtc_UWord32 totalDelay1 = timing.TargetVideoDelay();
+ WebRtc_UWord32 minTotalDelayMs = 200;
+ timing.SetMinimumTotalDelay(minTotalDelayMs);
+ clockInMs += 5000.0f;
+ timeStamp += 5*90000;
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ WebRtc_UWord32 totalDelay2 = timing.TargetVideoDelay();
+ // We should at least have minTotalDelayMs - decodeTime (10) - renderTime (10) to wait
+ TEST(waitTime == minTotalDelayMs - maxDecodeTimeMs - 10);
+ // The total video delay should not increase with the extra delay,
+ // the extra delay should be independent.
+ TEST(totalDelay1 == totalDelay2);
+
+ // Reset min total delay
+ timing.SetMinimumTotalDelay(0);
+ clockInMs += 5000.0f;
+ timeStamp += 5*90000;
+ timing.UpdateCurrentDelay(timeStamp);
+
+ // A sudden increase in timestamp of 2.1 seconds
+ clockInMs += 1000.0f/30.0f;
+ timeStamp += static_cast<WebRtc_UWord32>(2.1*90000 + 0.5);
+ WebRtc_Word64 ret = timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+ TEST(ret == -1);
+ timing.Reset();
+
+ // This test produces a trace which can be parsed with plotTimingTest.m. The plot
+ // can be used to see that the timing is reasonable under noise, and that the
+ // gradual transition between delays works as expected.
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "Stochastic test 1");
+
+ jitterDelayMs = 60;
+ maxDecodeTimeMs = 10;
+ extraDelayMs = 0;
+
+ timeStamp = static_cast<WebRtc_UWord32>(-10000); // To produce a wrap
+ clockInMs = 10000.0f;
+ timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
+
+ float noise = 0.0f;
+ for (int i=0; i < 1400; i++)
+ {
+ if (i == 400)
+ {
+ jitterDelayMs = 30;
+ }
+ else if (i == 700)
+ {
+ jitterDelayMs = 100;
+ }
+ else if (i == 1000)
+ {
+ minTotalDelayMs = 200;
+ timing.SetMinimumTotalDelay(minTotalDelayMs);
+ }
+ else if (i == 1200)
+ {
+ minTotalDelayMs = 0;
+ timing.SetMinimumTotalDelay(minTotalDelayMs);
+ }
+ WebRtc_Word64 startTimeMs = static_cast<WebRtc_Word64>(clockInMs + 0.5);
+ noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 2), -10.0f), 30.0f);
+ clockInMs += 10.0f;
+ timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast<WebRtc_Word64>(clockInMs + noise + 0.5));
+ timeStamp += 3000;
+ clockInMs += 1000.0f/30.0f - 10.0f;
+ noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 8), -15.0f), 15.0f);
+ timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + noise + 0.5));
+ timing.SetRequiredDelay(jitterDelayMs);
+ timing.UpdateCurrentDelay(timeStamp);
+ waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
+ static_cast<WebRtc_Word64>(clockInMs + 0.5));
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "timeStamp=%u clock=%u maxWaitTime=%u", timeStamp,
+ static_cast<WebRtc_UWord32>(clockInMs + 0.5), waitTime);
+
+ WebRtc_Word64 renderTimeMs = timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
+
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1,
+ "timeStamp=%u renderTime=%u",
+ timeStamp,
+ MaskWord64ToUWord32(renderTimeMs));
+ }
+ WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "End Stochastic test 1");
+ Trace::ReturnTrace();
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/release_test.cc b/src/modules/video_coding/main/test/release_test.cc
new file mode 100644
index 0000000..8e3a073
--- /dev/null
+++ b/src/modules/video_coding/main/test/release_test.cc
@@ -0,0 +1,46 @@
+/*
+ * 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 "ReleaseTest.h"
+#include "ReceiverTests.h"
+#include "TestMacros.h"
+#include "MediaOptTest.h"
+#include "CodecDataBaseTest.h"
+#include "GenericCodecTest.h"
+
+
+
+
+int ReleaseTest()
+{
+ printf("VCM RELEASE TESTS \n\n");
+
+ // Automatic tests
+
+ printf("Testing receive side timing...\n");
+ TEST(ReceiverTimingTests() == 0);
+
+ printf("Testing jitter buffer...\n");
+ TEST(JitterBufferTest() == 0);
+
+ printf("Testing Codec Data Base...\n");
+ TEST(CodecDBTest() == 0);
+
+ printf("Testing Media Optimization....\n");
+ TEST(VCMMediaOptTest(1) == 0);
+
+ // Tests requiring verification
+
+ printf("Testing Multi thread send-receive....\n");
+ TEST(MTRxTxTest() == 0);
+ printf("Verify by viewing output file MTRxTx_out.yuv \n");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/release_test.h b/src/modules/video_coding/main/test/release_test.h
new file mode 100644
index 0000000..2578160
--- /dev/null
+++ b/src/modules/video_coding/main/test/release_test.h
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#ifndef RELEASE_TEST_H
+#define RELEASE_TEST_H
+
+int ReleaseTest();
+int ReleaseTestPart2();
+
+#endif
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/release_test_pt2.cc b/src/modules/video_coding/main/test/release_test_pt2.cc
new file mode 100644
index 0000000..5ff48e5
--- /dev/null
+++ b/src/modules/video_coding/main/test/release_test_pt2.cc
@@ -0,0 +1,31 @@
+/*
+ * 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 "ReleaseTest.h"
+#include "ReceiverTests.h"
+#include "TestMacros.h"
+#include "MediaOptTest.h"
+#include "CodecDataBaseTest.h"
+#include "GenericCodecTest.h"
+
+
+
+
+int ReleaseTestPart2()
+{
+ printf("Verify that TICK_TIME_DEBUG and EVENT_DEBUG are uncommented");
+ // Tests requiring verification
+
+ printf("Testing Generic Codecs...\n");
+ TEST(VCMGenericCodecTest() == 0);
+ printf("Verify by viewing output file GCTest_out.yuv \n");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/resampler_test.cc b/src/modules/video_coding/main/test/resampler_test.cc
new file mode 100644
index 0000000..adcee17
--- /dev/null
+++ b/src/modules/video_coding/main/test/resampler_test.cc
@@ -0,0 +1,241 @@
+/*
+ * 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 "ResamplerTest.h"
+#include "video_coding.h"
+#include "tick_time.h"
+#include "../source/event.h"
+#include "VCMSpatialResampler.h"
+
+#include <iostream>
+#include <sstream>
+
+using namespace webrtc;
+
+int ResamplerTest()
+{
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ class ResamplerTest test(vcm);
+ int ret = test.Perform();
+ VideoCodingModule::Destroy(vcm);
+
+ return ret;
+}
+
+ResamplerTest::ResamplerTest(VideoCodingModule* vcm):
+_width(0),
+_height(0),
+_timeStamp(0),
+_lengthSourceFrame(0),
+_vcmMacrosTests(0),
+_vcmMacrosErrors(0),
+_vcm(vcm)
+{
+ //
+}
+ResamplerTest::~ResamplerTest()
+{
+ //
+}
+void
+ResamplerTest::Setup()
+{
+ _inname= "../../../../../codecs_video/testFiles/foreman.yuv";
+ _width = 352;
+ _height = 288;
+ _frameRate = 30;
+ _lengthSourceFrame = 3*_width*_height/2;
+ _encodedName = "../ResamplerTest_encoded.yuv";
+
+ if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
+ {
+ printf("Cannot read file %s.\n", _inname.c_str());
+ exit(1);
+ }
+
+ if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
+ {
+ printf("Cannot write encoded file.\n");
+ exit(1);
+ }
+
+ return;
+}
+
+WebRtc_Word32 ResamplerTest::Perform()
+{
+ // Make sure this test isn't executed without simulated clocks
+#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
+ return -1;
+#endif
+
+ // Setup test
+ Setup();
+
+ ResamplerStandAloneTest();
+
+ ResamplerVCMTest();
+
+ TearDown();
+ return 0;
+}
+
+void
+ResamplerTest::ResamplerVCMTest()
+{
+ // Create the input frame and read a frame from file
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile);
+ sourceFrame.SetLength(_lengthSourceFrame);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+
+ TEST_EXIT_ON_FAIL(_vcm->InitializeReceiver() == VCM_OK);
+ TEST_EXIT_ON_FAIL(_vcm->InitializeSender() == VCM_OK);
+
+ TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(true) == VCM_OK);
+
+ TestSizeVCM(sourceFrame, 128, 80); // Cut, decimation 1x, interpolate
+ TestSizeVCM(sourceFrame, 352/2, 288/2); // Even decimation
+ TestSizeVCM(sourceFrame, 352, 288); // No resampling
+ TestSizeVCM(sourceFrame, 2*352, 2*288); // Upsampling 2x
+ TestSizeVCM(sourceFrame, 400, 256); // Upsampling 1.5x and cut
+ TestSizeVCM(sourceFrame, 960, 720); // Upsampling 3.5x and cut
+
+ TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(false) == VCM_OK);
+
+ TestSizeVCM(sourceFrame, 320, 240); // Cropped
+ TestSizeVCM(sourceFrame, 1280, 720); // Padded
+}
+
+void
+ResamplerTest::TestSizeVCM(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight)
+{
+ assert(false);
+ /*
+ std::ostringstream filename;
+ filename << "../VCM_Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv";
+ std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl;
+ FILE* decodedFile = fopen(filename.str().c_str(), "wb");
+
+ _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
+ sourceFrame.SetTimeStamp(_timeStamp);
+
+ VCMDecodeCompleteCallback decodeCallback(decodedFile);
+ VCMEncodeCompleteCallback encodeCompleteCallback(_encodedFile);
+ TEST_EXIT_ON_FAIL(_vcm->RegisterReceiveCallback(&decodeCallback) == VCM_OK);
+ TEST_EXIT_ON_FAIL(_vcm->RegisterTransportCallback(&encodeCompleteCallback) == VCM_OK);
+ encodeCompleteCallback.RegisterReceiverVCM(_vcm);
+ encodeCompleteCallback.SetCodecType(webrtc::VideoCodecVP8);
+
+ RegisterCodec(targetWidth, targetHeight);
+ encodeCompleteCallback.SetFrameDimensions(targetWidth, targetHeight);
+ TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
+ TEST(_vcm->Decode() == VCM_OK);
+
+ fclose(decodedFile);
+ */
+}
+
+void
+ResamplerTest::RegisterCodec(WebRtc_UWord32 width, WebRtc_UWord32 height)
+{
+ // Register codecs
+ assert(false);
+ /*
+ VideoCodec codec;
+ VideoCodingModule::Codec(webrtc::kVideoCodecVP8, &codec);
+ codec.width = static_cast<WebRtc_Word16>(width);
+ codec.height = static_cast<WebRtc_Word16>(height);
+ TEST(_vcm->RegisterSendCodec(&codec, 1, 1440) == VCM_OK);
+ TEST(_vcm->RegisterReceiveCodec(&codec, 1) == VCM_OK);
+ TEST(_vcm->SetChannelParameters(2000, 0, 0) == VCM_OK);
+ */
+}
+
+WebRtc_Word32
+ResamplerTest::ResamplerStandAloneTest()
+{
+ // Create the input frame and read a frame from file
+ VideoFrame sourceFrame;
+ sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
+ fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile);
+ sourceFrame.SetLength(_lengthSourceFrame);
+ sourceFrame.SetHeight(_height);
+ sourceFrame.SetWidth(_width);
+
+ TestSize(sourceFrame, 100, 50); // Cut, decimation 1x, interpolate
+ TestSize(sourceFrame, 352/2, 288/2); // Even decimation
+ TestSize(sourceFrame, 352, 288); // No resampling
+ TestSize(sourceFrame, 2*352, 2*288); // Even upsampling
+ TestSize(sourceFrame, 400, 256); // Upsampling 1.5x and cut
+ TestSize(sourceFrame, 960, 720); // Upsampling 3.5x and cut
+ TestSize(sourceFrame, 1280, 720); // Upsampling 4x and cut
+
+ sourceFrame.Free();
+ return 0;
+}
+
+void
+ResamplerTest::TestSize(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight)
+{
+ VCMSimpleSpatialResampler resampler;
+ VideoFrame outFrame;
+ std::ostringstream filename;
+ filename << "../Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv";
+ std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl;
+ FILE* standAloneFile = fopen(filename.str().c_str(), "wb");
+ //resampler.EnableUpSampling(true);
+ resampler.EnableInterpolation(true);
+ TEST(resampler.SetTargetFrameSize(targetWidth, targetHeight) == VCM_OK);
+ TEST(resampler.ResampleFrame(sourceFrame, outFrame) == VCM_OK);
+ TEST(outFrame.Buffer() != NULL);
+ TEST(outFrame.Length() == (targetWidth * targetHeight * 3 / 2));
+
+ // Write to file for visual inspection
+ fwrite(outFrame.Buffer(), 1, outFrame.Length(), standAloneFile);
+
+ outFrame.Free();
+ fclose(standAloneFile);
+}
+
+void
+ResamplerTest::Print()
+{
+ printf("\nVCM Resampler Test: \n\n%i tests completed\n", _vcmMacrosTests);
+ if (_vcmMacrosErrors > 0)
+ {
+ printf("%i FAILED\n\n", _vcmMacrosErrors);
+ }
+ else
+ {
+ printf("ALL PASSED\n\n");
+ }
+}
+
+void
+ResamplerTest::TearDown()
+{
+ fclose(_sourceFile);
+ fclose(_encodedFile);
+ return;
+}
+
+void
+ResamplerTest::IncrementDebugClock(float frameRate)
+{
+ for (int t= 0; t < 1000/frameRate; t++)
+ {
+ VCMTickTime::IncrementDebugClock();
+ }
+ return;
+}
+
diff --git a/src/modules/video_coding/main/test/rtp_player.cc b/src/modules/video_coding/main/test/rtp_player.cc
new file mode 100644
index 0000000..38bff97
--- /dev/null
+++ b/src/modules/video_coding/main/test/rtp_player.cc
@@ -0,0 +1,466 @@
+/*
+ * 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 "rtp_player.h"
+#include "../source/internal_defines.h"
+#include "rtp_rtcp.h"
+#include "tick_time.h"
+
+#include <cstdlib>
+#ifdef WIN32
+#include <windows.h>
+#include <Winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+using namespace webrtc;
+
+RawRtpPacket::RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len)
+:
+rtpData(), rtpLen(len), resendTimeMs(-1)
+{
+ rtpData = new WebRtc_UWord8[rtpLen];
+ memcpy(rtpData, data, rtpLen);
+}
+
+RawRtpPacket::~RawRtpPacket()
+{
+ delete [] rtpData;
+}
+
+LostPackets::LostPackets()
+:
+_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
+_lossCount(0),
+ListWrapper(),
+_debugFile(NULL)
+{
+ _debugFile = fopen("PacketLossDebug.txt", "w");
+}
+
+LostPackets::~LostPackets()
+{
+ if (_debugFile)
+ {
+ fclose(_debugFile);
+ }
+ ListItem* item = First();
+ while (item != NULL)
+ {
+ RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
+ if (packet != NULL)
+ {
+ delete packet;
+ }
+ Erase(item);
+ item = First();
+ }
+ delete &_critSect;
+}
+
+WebRtc_UWord32 LostPackets::AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen)
+{
+ CriticalSectionScoped cs(_critSect);
+ RawRtpPacket* packet = new RawRtpPacket(rtpData, rtpLen);
+ ListItem* newItem = new ListItem(packet);
+ InsertBefore(First(), newItem);
+ const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3];
+ if (_debugFile != NULL)
+ {
+ fprintf(_debugFile, "%u Lost packet: %u\n", _lossCount, seqNo);
+ }
+ _lossCount++;
+ return 0;
+}
+
+WebRtc_UWord32 LostPackets::SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime)
+{
+ CriticalSectionScoped cs(_critSect);
+ ListItem* item = First();
+ while (item != NULL)
+ {
+ RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
+ const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
+ const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
+ if (sequenceNumber == seqNo && packet->resendTimeMs + 10 < nowMs)
+ {
+ if (_debugFile != NULL)
+ {
+ fprintf(_debugFile, "Resend %u at %u\n", seqNo, MaskWord64ToUWord32(resendTime));
+ }
+ packet->resendTimeMs = resendTime;
+ return 0;
+ }
+ item = Next(item);
+ }
+ fprintf(_debugFile, "Packet not lost %u\n", sequenceNumber);
+ return -1;
+}
+
+WebRtc_UWord32 LostPackets::NumberOfPacketsToResend() const
+{
+ CriticalSectionScoped cs(_critSect);
+ WebRtc_UWord32 count = 0;
+ ListItem* item = First();
+ while (item != NULL)
+ {
+ RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
+ if (packet->resendTimeMs >= 0)
+ {
+ count++;
+ }
+ item = Next(item);
+ }
+ return count;
+}
+
+void LostPackets::ResentPacket(WebRtc_UWord16 seqNo)
+{
+ CriticalSectionScoped cs(_critSect);
+ if (_debugFile != NULL)
+ {
+ fprintf(_debugFile, "Resent %u at %u\n", seqNo,
+ MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp()));
+ }
+}
+
+RTPPlayer::RTPPlayer(const char* filename, RtpData* callback)
+:
+_rtpModule(*RtpRtcp::CreateRtpRtcp(1, false)),
+_nextRtpTime(0),
+_dataCallback(callback),
+_firstPacket(true),
+_lossRate(0.0f),
+_nackEnabled(false),
+_resendPacketCount(0),
+_noLossStartup(100),
+_endOfFile(false),
+_rttMs(0),
+_firstPacketRtpTime(0),
+_firstPacketTimeMs(0),
+_reorderBuffer(NULL),
+_reordering(false),
+_nextPacket(),
+_nextPacketLength(0),
+_randVec(),
+_randVecPos(0)
+{
+ _rtpFile = fopen(filename, "rb");
+ memset(_nextPacket, 0, sizeof(_nextPacket));
+}
+
+RTPPlayer::~RTPPlayer()
+{
+ RtpRtcp::DestroyRtpRtcp(&_rtpModule);
+ if (_rtpFile != NULL)
+ {
+ fclose(_rtpFile);
+ }
+ if (_reorderBuffer != NULL)
+ {
+ delete _reorderBuffer;
+ _reorderBuffer = NULL;
+ }
+}
+
+WebRtc_Word32 RTPPlayer::Initialize(const ListWrapper& payloadList)
+{
+ std::srand(321);
+ for (int i=0; i < RAND_VEC_LENGTH; i++)
+ {
+ _randVec[i] = rand();
+ }
+ _randVecPos = 0;
+ WebRtc_Word32 ret = _rtpModule.SetNACKStatus(kNackOff);
+ if (ret < 0)
+ {
+ return -1;
+ }
+ ret = _rtpModule.InitReceiver();
+ if (ret < 0)
+ {
+ return -1;
+ }
+
+ _rtpModule.InitSender();
+ _rtpModule.SetRTCPStatus(kRtcpNonCompound);
+ _rtpModule.SetTMMBRStatus(true);
+
+ ret = _rtpModule.RegisterIncomingDataCallback(_dataCallback);
+ if (ret < 0)
+ {
+ return -1;
+ }
+ // Register payload types
+ ListItem* item = payloadList.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ if (_rtpModule.RegisterReceivePayload(payloadType->name.c_str(), payloadType->payloadType) < 0)
+ {
+ return -1;
+ }
+ }
+ item = payloadList.Next(item);
+ }
+ if (ReadHeader() < 0)
+ {
+ return -1;
+ }
+ memset(_nextPacket, 0, sizeof(_nextPacket));
+ _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime);
+ return 0;
+}
+
+WebRtc_Word32 RTPPlayer::ReadHeader()
+{
+ char firstline[FIRSTLINELEN];
+ if (_rtpFile == NULL)
+ {
+ return -1;
+ }
+ fgets(firstline, FIRSTLINELEN, _rtpFile);
+ if(strncmp(firstline,"#!rtpplay",9) == 0) {
+ if(strncmp(firstline,"#!rtpplay1.0",12) != 0){
+ printf("ERROR: wrong rtpplay version, must be 1.0\n");
+ return -1;
+ }
+ }
+ else if (strncmp(firstline,"#!RTPencode",11) == 0) {
+ if(strncmp(firstline,"#!RTPencode1.0",14) != 0){
+ printf("ERROR: wrong RTPencode version, must be 1.0\n");
+ return -1;
+ }
+ }
+ else {
+ printf("ERROR: wrong file format of input file\n");
+ return -1;
+ }
+
+ WebRtc_UWord32 start_sec;
+ WebRtc_UWord32 start_usec;
+ WebRtc_UWord32 source;
+ WebRtc_UWord16 port;
+ WebRtc_UWord16 padding;
+
+ fread(&start_sec, 4, 1, _rtpFile);
+ start_sec=ntohl(start_sec);
+ fread(&start_usec, 4, 1, _rtpFile);
+ start_usec=ntohl(start_usec);
+ fread(&source, 4, 1, _rtpFile);
+ source=ntohl(source);
+ fread(&port, 2, 1, _rtpFile);
+ port=ntohs(port);
+ fread(&padding, 2, 1, _rtpFile);
+ padding=ntohs(padding);
+ return 0;
+}
+
+WebRtc_UWord32 RTPPlayer::TimeUntilNextPacket() const
+{
+ WebRtc_Word64 timeLeft = (_nextRtpTime - _firstPacketRtpTime) - (VCMTickTime::MillisecondTimestamp() - _firstPacketTimeMs);
+ if (timeLeft < 0)
+ {
+ return 0;
+ }
+ return static_cast<WebRtc_UWord32>(timeLeft);
+}
+
+WebRtc_Word32 RTPPlayer::NextPacket(const WebRtc_Word64 timeNow)
+{
+ // Send any packets ready to be resent
+ _lostPackets.Lock();
+ ListItem* item = _lostPackets.First();
+ _lostPackets.Unlock();
+ while (item != NULL)
+ {
+ _lostPackets.Lock();
+ RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
+ _lostPackets.Unlock();
+ if (timeNow >= packet->resendTimeMs && packet->resendTimeMs != -1)
+ {
+ const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
+ printf("Resend: %u\n", seqNo);
+ WebRtc_Word32 ret = SendPacket(packet->rtpData, packet->rtpLen);
+ ListItem* itemToRemove = item;
+ _lostPackets.Lock();
+ item = _lostPackets.Next(item);
+ _lostPackets.Erase(itemToRemove);
+ delete packet;
+ _lostPackets.Unlock();
+ _resendPacketCount++;
+ if (ret > 0)
+ {
+ _lostPackets.ResentPacket(seqNo);
+ }
+ else if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ else
+ {
+ _lostPackets.Lock();
+ item = _lostPackets.Next(item);
+ _lostPackets.Unlock();
+ }
+ }
+
+ // Send any packets from rtp file
+ if (!_endOfFile && (TimeUntilNextPacket() == 0 || _firstPacket))
+ {
+ _rtpModule.Process();
+ if (_firstPacket)
+ {
+ _firstPacketRtpTime = static_cast<WebRtc_Word64>(_nextRtpTime);
+ _firstPacketTimeMs = VCMTickTime::MillisecondTimestamp();
+ }
+ if (_reordering && _reorderBuffer == NULL)
+ {
+ _reorderBuffer = new RawRtpPacket(reinterpret_cast<WebRtc_UWord8*>(_nextPacket), static_cast<WebRtc_UWord16>(_nextPacketLength));
+ return 0;
+ }
+ WebRtc_Word32 ret = SendPacket(reinterpret_cast<WebRtc_UWord8*>(_nextPacket), static_cast<WebRtc_UWord16>(_nextPacketLength));
+ if (_reordering && _reorderBuffer != NULL)
+ {
+ RawRtpPacket* rtpPacket = _reorderBuffer;
+ _reorderBuffer = NULL;
+ SendPacket(rtpPacket->rtpData, rtpPacket->rtpLen);
+ delete rtpPacket;
+ }
+ _firstPacket = false;
+ if (ret < 0)
+ {
+ return ret;
+ }
+ _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime);
+ if (_nextPacketLength < 0)
+ {
+ _endOfFile = true;
+ return 0;
+ }
+ else if (_nextPacketLength == 0)
+ {
+ return 0;
+ }
+ }
+ if (_endOfFile && _lostPackets.NumberOfPacketsToResend() == 0)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+WebRtc_Word32 RTPPlayer::SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen)
+{
+ if ((_randVec[(_randVecPos++) % RAND_VEC_LENGTH] + 1.0)/(RAND_MAX + 1.0) < _lossRate &&
+ _noLossStartup < 0)
+ {
+ if (_nackEnabled)
+ {
+ const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3];
+ printf("Throw: %u\n", seqNo);
+ _lostPackets.AddPacket(rtpData, rtpLen);
+ return 0;
+ }
+ }
+ else
+ {
+ WebRtc_Word32 ret = _rtpModule.IncomingPacket(rtpData, rtpLen);
+ if (ret < 0)
+ {
+ return -1;
+ }
+ }
+ if (_noLossStartup >= 0)
+ {
+ _noLossStartup--;
+ }
+ return 1;
+}
+
+WebRtc_Word32 RTPPlayer::ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset)
+{
+ WebRtc_UWord16 length, plen;
+
+ if (fread(&length,2,1,_rtpFile)==0)
+ return(-1);
+ length=ntohs(length);
+
+ if (fread(&plen,2,1,_rtpFile)==0)
+ return(-1);
+ plen=ntohs(plen);
+
+ if (fread(offset,4,1,_rtpFile)==0)
+ return(-1);
+ *offset=ntohl(*offset);
+
+ // Use length here because a plen of 0 specifies rtcp
+ length = (WebRtc_UWord16) (length - HDR_SIZE);
+ if (fread((unsigned short *) rtpdata,1,length,_rtpFile) != length)
+ return(-1);
+
+#ifdef JUNK_DATA
+ // destroy the RTP payload with random data
+ if (plen > 12) { // ensure that we have more than just a header
+ for ( int ix = 12; ix < plen; ix=ix+2 ) {
+ rtpdata[ix>>1] = (short) (rtpdata[ix>>1] + (short) rand());
+ }
+ }
+#endif
+ return plen;
+}
+
+WebRtc_Word32 RTPPlayer::SimulatePacketLoss(float lossRate, bool enableNack, WebRtc_UWord32 rttMs)
+{
+ _nackEnabled = enableNack;
+ _lossRate = lossRate;
+ _rttMs = rttMs;
+ return 0;
+}
+
+WebRtc_Word32 RTPPlayer::SetReordering(bool enabled)
+{
+ _reordering = enabled;
+ return 0;
+}
+
+WebRtc_Word32 RTPPlayer::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length)
+{
+ if (sequenceNumbers == NULL)
+ {
+ return 0;
+ }
+ for (int i=0; i < length; i++)
+ {
+ _lostPackets.SetResendTime(sequenceNumbers[i], VCMTickTime::MillisecondTimestamp() + _rttMs);
+ }
+ return 0;
+}
+
+void RTPPlayer::Print() const
+{
+ printf("Lost packets: %u, resent packets: %u\n", _lostPackets.TotalNumberOfLosses(), _resendPacketCount);
+ printf("Packets still lost: %u\n", _lostPackets.GetSize());
+ printf("Packets waiting to be resent: %u\n", _lostPackets.NumberOfPacketsToResend());
+ printf("Sequence numbers:\n");
+ ListItem* item = _lostPackets.First();
+ while (item != NULL)
+ {
+ RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
+ const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
+ printf("%u, ", seqNo);
+ item = _lostPackets.Next(item);
+ }
+ printf("\n");
+}
diff --git a/src/modules/video_coding/main/test/rtp_player.h b/src/modules/video_coding/main/test/rtp_player.h
new file mode 100644
index 0000000..53fd867
--- /dev/null
+++ b/src/modules/video_coding/main/test/rtp_player.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
+
+#include "typedefs.h"
+#include "rtp_rtcp.h"
+#include "list_wrapper.h"
+#include "critical_section_wrapper.h"
+#include "video_coding_defines.h"
+
+#include <stdio.h>
+#include <string>
+
+#define HDR_SIZE 8 // rtpplay packet header size in bytes
+#define FIRSTLINELEN 40
+#define RAND_VEC_LENGTH 4096
+
+struct RawRtpPacket
+{
+public:
+ RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len);
+ ~RawRtpPacket();
+
+ WebRtc_UWord8* rtpData;
+ WebRtc_UWord16 rtpLen;
+ WebRtc_Word64 resendTimeMs;
+};
+
+class LostPackets : public webrtc::ListWrapper
+{
+public:
+ LostPackets();
+ ~LostPackets();
+
+ WebRtc_UWord32 AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen);
+ WebRtc_UWord32 SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime);
+ WebRtc_UWord32 TotalNumberOfLosses() const { return _lossCount; };
+ WebRtc_UWord32 NumberOfPacketsToResend() const;
+ void ResentPacket(WebRtc_UWord16 seqNo);
+ void Lock() {_critSect.Enter();};
+ void Unlock() {_critSect.Leave();};
+private:
+ webrtc::CriticalSectionWrapper& _critSect;
+ WebRtc_UWord32 _lossCount;
+ FILE* _debugFile;
+};
+
+struct PayloadCodecTuple
+{
+ PayloadCodecTuple(WebRtc_UWord8 plType, std::string codecName, webrtc::VideoCodecType type) :
+ name(codecName), payloadType(plType), codecType(type) {};
+ const std::string name;
+ const WebRtc_UWord8 payloadType;
+ const webrtc::VideoCodecType codecType;
+};
+
+class RTPPlayer : public webrtc::VCMPacketRequestCallback
+{
+public:
+ RTPPlayer(const char* filename, webrtc::RtpData* callback);
+ virtual ~RTPPlayer();
+
+ WebRtc_Word32 Initialize(const webrtc::ListWrapper& payloadList);
+ WebRtc_Word32 NextPacket(const WebRtc_Word64 timeNow);
+ WebRtc_UWord32 TimeUntilNextPacket() const;
+ WebRtc_Word32 SimulatePacketLoss(float lossRate, bool enableNack = false, WebRtc_UWord32 rttMs = 0);
+ WebRtc_Word32 SetReordering(bool enabled);
+ WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length);
+ void Print() const;
+
+private:
+ WebRtc_Word32 SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen);
+ WebRtc_Word32 ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset);
+ WebRtc_Word32 ReadHeader();
+ FILE* _rtpFile;
+ webrtc::RtpRtcp& _rtpModule;
+ WebRtc_UWord32 _nextRtpTime;
+ webrtc::RtpData* _dataCallback;
+ bool _firstPacket;
+ float _lossRate;
+ bool _nackEnabled;
+ LostPackets _lostPackets;
+ WebRtc_UWord32 _resendPacketCount;
+ WebRtc_Word32 _noLossStartup;
+ bool _endOfFile;
+ WebRtc_UWord32 _rttMs;
+ WebRtc_Word64 _firstPacketRtpTime;
+ WebRtc_Word64 _firstPacketTimeMs;
+ RawRtpPacket* _reorderBuffer;
+ bool _reordering;
+ WebRtc_Word16 _nextPacket[8000];
+ WebRtc_Word32 _nextPacketLength;
+ int _randVec[RAND_VEC_LENGTH];
+ int _randVecPos;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
diff --git a/src/modules/video_coding/main/test/subfigure.m b/src/modules/video_coding/main/test/subfigure.m
new file mode 100644
index 0000000..eadfcb6
--- /dev/null
+++ b/src/modules/video_coding/main/test/subfigure.m
@@ -0,0 +1,30 @@
+function H = subfigure(m, n, p)
+%
+% H = SUBFIGURE(m, n, p)
+%
+% Create a new figure window and adjust position and size such that it will
+% become the p-th tile in an m-by-n matrix of windows. (The interpretation of
+% m, n, and p is the same as for SUBPLOT.
+%
+% Henrik Lundin, 2009-01-19
+%
+
+
+h = figure;
+
+[j, i] = ind2sub([n m], p);
+scrsz = get(0,'ScreenSize'); % get screen size
+%scrsz = [1, 1, 1600, 1200];
+
+taskbarSize = 58;
+windowbarSize = 68;
+windowBorder = 4;
+
+scrsz(2) = scrsz(2) + taskbarSize;
+scrsz(4) = scrsz(4) - taskbarSize;
+
+set(h, 'position', [(j-1)/n * scrsz(3) + scrsz(1) + windowBorder,...
+ (m-i)/m * scrsz(4) + scrsz(2) + windowBorder, ...
+ scrsz(3)/n - (windowBorder + windowBorder),...
+ scrsz(4)/m - (windowbarSize + windowBorder + windowBorder)]);
+
diff --git a/src/modules/video_coding/main/test/test_macros.h b/src/modules/video_coding/main/test/test_macros.h
new file mode 100644
index 0000000..14ae71f
--- /dev/null
+++ b/src/modules/video_coding/main/test/test_macros.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef VCM_TEST_MACROS_H
+#define VCM_TEST_MACROS_H
+
+#include <cstdio>
+#include <cstdlib>
+
+static int vcmMacrosTests = 0;
+static int vcmMacrosErrors = 0;
+
+#define PRINT_ERR_MSG(msg) \
+ do { \
+ fprintf(stderr, "Error at line %i of %s\n%s", \
+ __LINE__, __FILE__, msg); \
+ } while(0)
+
+#define TEST(expr) \
+ do { \
+ vcmMacrosTests++; \
+ if (!(expr)) { \
+ PRINT_ERR_MSG("Assertion failed: " #expr "\n\n"); \
+ vcmMacrosErrors++; \
+ } \
+ } while(0)
+
+#define TEST_EXIT_ON_FAIL(expr) \
+ do { \
+ vcmMacrosTests++; \
+ if (!(expr)) { \
+ PRINT_ERR_MSG("Assertion failed: " #expr "\nExiting...\n\n"); \
+ vcmMacrosErrors++; \
+ exit(EXIT_FAILURE); \
+ } \
+ } while(0)
+
+#endif
\ No newline at end of file
diff --git a/src/modules/video_coding/main/test/test_util.cc b/src/modules/video_coding/main/test/test_util.cc
new file mode 100644
index 0000000..d2db97f
--- /dev/null
+++ b/src/modules/video_coding/main/test/test_util.cc
@@ -0,0 +1,725 @@
+/*
+ * 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 "test_util.h"
+#include "rtp_dump.h"
+#include <cmath>
+
+using namespace webrtc;
+
+/******************************
+ * VCMEncodeCompleteCallback
+ *****************************/
+// Basic callback implementation
+// passes the encoded frame directly to the encoder
+// Packetization callback implmentation
+VCMEncodeCompleteCallback::VCMEncodeCompleteCallback(FILE* encodedFile):
+_seqNo(0),
+_encodedFile(encodedFile),
+_encodedBytes(0),
+_VCMReceiver(NULL),
+_encodeComplete(false),
+_width(0),
+_height(0),
+_codecType(kRTPVideoNoVideo),
+_layerPacketId(1)
+{
+ //
+}
+VCMEncodeCompleteCallback::~VCMEncodeCompleteCallback()
+{
+}
+
+void
+VCMEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport)
+{
+}
+
+WebRtc_Word32
+VCMEncodeCompleteCallback::SendData(const FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const RTPFragmentationHeader& fragmentationHeader)
+{
+ // will call the VCMReceiver input packet
+ _frameType = frameType;
+ // writing encodedData into file
+ fwrite(payloadData, 1, payloadSize, _encodedFile);
+ WebRtcRTPHeader rtpInfo;
+ rtpInfo.header.markerBit = true; // end of frame
+ rtpInfo.type.Video.isFirstPacket = true;
+ rtpInfo.type.Video.codec = _codecType;
+ switch (_codecType)
+ {
+ case webrtc::kRTPVideoH263:
+ rtpInfo.type.Video.codecHeader.H263.bits = false;
+ rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
+ rtpInfo.type.Video.height = (WebRtc_UWord16)_height;
+ rtpInfo.type.Video.width = (WebRtc_UWord16)_width;
+ break;
+ }
+
+ rtpInfo.header.payloadType = payloadType;
+ rtpInfo.header.sequenceNumber = _seqNo++;
+ rtpInfo.header.ssrc = 0;
+ rtpInfo.header.timestamp = timeStamp;
+ rtpInfo.frameType = frameType;
+ // Size should also be received from that table, since the payload type
+ // defines the size.
+
+ _encodedBytes += payloadSize;
+ // directly to receiver
+ _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
+ _encodeComplete = true;
+
+ return 0;
+}
+
+ float
+ VCMEncodeCompleteCallback::EncodedBytes()
+ {
+ return _encodedBytes;
+ }
+
+bool
+VCMEncodeCompleteCallback::EncodeComplete()
+{
+ if (_encodeComplete)
+ {
+ _encodeComplete = false;
+ return true;
+ }
+ return false;
+}
+
+void
+VCMEncodeCompleteCallback::Initialize()
+{
+ _encodeComplete = false;
+ _encodedBytes = 0;
+ _seqNo = 0;
+ return;
+}
+
+void
+VCMEncodeCompleteCallback::ResetByteCount()
+{
+ _encodedBytes = 0;
+}
+
+/**********************************/
+/* VCMRTPEncodeCompleteCallback /
+/********************************/
+// Encode Complete callback implementation
+// passes the encoded frame via the RTP module to the decoder
+// Packetization callback implmentation
+
+WebRtc_Word32
+VCMRTPEncodeCompleteCallback::SendData(const FrameType frameType,
+ const WebRtc_UWord8 payloadType,
+ const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord32 payloadSize,
+ const RTPFragmentationHeader& fragmentationHeader)
+{
+ _frameType = frameType;
+ _encodedBytes+= payloadSize;
+ _encodeComplete = true;
+ //printf("encoded = %d Bytes\n", payloadSize);
+ return _RTPModule->SendOutgoingData(frameType, payloadType, timeStamp, payloadData, payloadSize, &fragmentationHeader);
+}
+
+ float
+ VCMRTPEncodeCompleteCallback::EncodedBytes()
+ {
+ // only good for one call - after which will reset value;
+ float tmp = _encodedBytes;
+ _encodedBytes = 0;
+ return tmp;
+ }
+
+bool
+VCMRTPEncodeCompleteCallback::EncodeComplete()
+{
+ if (_encodeComplete)
+ {
+ _encodeComplete = false;
+ return true;
+ }
+ return false;
+}
+
+// Decoded Frame Callback Implmentation
+
+ WebRtc_Word32
+ VCMDecodeCompleteCallback::FrameToRender(VideoFrame& videoFrame)
+ {
+ fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
+ _decodedBytes+= videoFrame.Length();
+ // keeping last decoded frame
+ _lastDecodedFrame.VerifyAndAllocate(videoFrame.Size());
+ _lastDecodedFrame.CopyFrame(videoFrame.Size(), videoFrame.Buffer());
+ _lastDecodedFrame.SetHeight(videoFrame.Height());
+ _lastDecodedFrame.SetWidth(videoFrame.Width());
+ _lastDecodedFrame.SetTimeStamp(videoFrame.TimeStamp());
+
+ return VCM_OK;
+ }
+
+int
+VCMDecodeCompleteCallback::PSNRLastFrame(const VideoFrame& sourceFrame, double *YPSNRptr)
+{
+ double mse = 0.0;
+ double mseLogSum = 0.0;
+
+ WebRtc_Word32 frameBytes = sourceFrame.Height() * sourceFrame.Width(); // only Y
+ WebRtc_UWord8 *ref = sourceFrame.Buffer();
+ if (_lastDecodedFrame.Height() == 0)
+ {
+ *YPSNRptr = 0;
+ return 0; // no new decoded frames
+ }
+ WebRtc_UWord8 *test = _lastDecodedFrame.Buffer();
+ for( int k = 0; k < frameBytes; k++ )
+ {
+ mse += (test[k] - ref[k]) * (test[k] - ref[k]);
+ }
+
+ // divide by number of pixels
+ mse /= (double) (frameBytes);
+
+ // accumulate for total average
+ mseLogSum += std::log10( mse );
+
+ *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum; // for only 1 frame
+
+ _lastDecodedFrame.Free();
+ _lastDecodedFrame.SetHeight(0);
+ return 0;
+}
+
+ WebRtc_Word32
+ VCMDecodeCompleteCallback::DecodedBytes()
+ {
+ return _decodedBytes;
+ }
+
+ RTPSendCompleteCallback::RTPSendCompleteCallback(RtpRtcp* rtp, const char* filename):
+ _rtp(rtp),
+ _sendCount(0),
+ _lossPct(0),
+ _rtpDump(NULL)
+ {
+ if (filename != NULL)
+ {
+ _rtpDump = RtpDump::CreateRtpDump();
+ _rtpDump->Start(filename);
+ }
+ }
+ RTPSendCompleteCallback::~RTPSendCompleteCallback()
+ {
+ if (_rtpDump != NULL)
+ {
+ _rtpDump->Stop();
+ RtpDump::DestroyRtpDump(_rtpDump);
+ }
+ }
+int
+RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len)
+{
+ _sendCount++;
+ // Packet Loss - randomly drop %loss packets
+ // don't drop I-frame packets
+ if(PacketLoss(_lossPct) && (_sendCount > 12))
+ {
+ // drop
+ //printf("\tDrop packet, sendCount = %d\n", _sendCount);
+ return len;
+ }
+ if (_rtpDump != NULL)
+ {
+ if (_rtpDump->DumpPacket((const WebRtc_UWord8*)data, len) != 0)
+ {
+ return -1;
+ }
+ }
+ if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
+ {
+ return len;
+ }
+ return -1;
+ }
+
+int
+RTPSendCompleteCallback::SendRTCPPacket(int channel, const void *data, int len)
+{
+ if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
+ {
+ return len;
+ }
+ return -1;
+}
+
+void
+RTPSendCompleteCallback::SetLossPct(double lossPct)
+{
+ _lossPct = lossPct;
+ return;
+}
+
+bool
+RTPSendCompleteCallback::PacketLoss(double lossPct)
+{
+ double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0);
+ return randVal < lossPct/100;
+}
+
+WebRtc_Word32
+PacketRequester::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length)
+{
+ return _rtp.SendNACK(sequenceNumbers, length);
+}
+
+WebRtc_Word32
+PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *YPSNRptr)
+{
+ FILE *refFp = fopen(refFileName, "rb");
+ if( refFp == NULL ) {
+ // cannot open reference file
+ fprintf(stderr, "Cannot open file %s\n", refFileName);
+ return -1;
+ }
+
+ FILE *testFp = fopen(testFileName, "rb");
+ if( testFp == NULL ) {
+ // cannot open test file
+ fprintf(stderr, "Cannot open file %s\n", testFileName);
+ return -2;
+ }
+
+ double mse = 0.0;
+ double mseLogSum = 0.0;
+ WebRtc_Word32 frames = 0;
+
+ WebRtc_Word32 frameBytes = 3*width*height/2; // bytes in one frame I420
+ WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; // space for one frame I420
+ WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; // space for one frame I420
+
+ WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp);
+ WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp);
+
+ while( refBytes == frameBytes && testBytes == frameBytes )
+ {
+ mse = 0.0;
+
+ int sh = 8;//boundary offset
+ for( int k2 = sh; k2 < height-sh;k2++)
+ for( int k = sh; k < width-sh;k++)
+ {
+ int kk = k2*width + k;
+ mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]);
+ }
+
+ // divide by number of pixels
+ mse /= (double) (width * height);
+
+ // accumulate for total average
+ mseLogSum += std::log10( mse );
+ frames++;
+
+ refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ testBytes = (int) fread(test, 1, frameBytes, testFp);
+ }
+ // for identical reproduction:
+ if (mse == 0)
+ {
+ *YPSNRptr = 48;
+ }
+ else
+ {
+ *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames;
+ }
+
+
+ delete [] ref;
+ delete [] test;
+
+ fclose(refFp);
+ fclose(testFp);
+
+ return 0;
+}
+
+WebRtc_Word32
+SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr)
+{
+ FILE *refFp = fopen(refFileName, "rb");
+ if( refFp == NULL ) {
+ // cannot open reference file
+ fprintf(stderr, "Cannot open file %s\n", refFileName);
+ return -1;
+ }
+
+ FILE *testFp = fopen(testFileName, "rb");
+ if( testFp == NULL ) {
+ // cannot open test file
+ fprintf(stderr, "Cannot open file %s\n", testFileName);
+ return -2;
+ }
+
+ int frames = 0;
+
+ int frameBytes = 3*width*height/2; // bytes in one frame I420
+ unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420
+ unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420
+
+ int refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ int testBytes = (int) fread(test, 1, frameBytes, testFp);
+
+ float *righMostColumnAvgTest = new float[width];
+ float *righMostColumnAvgRef = new float[width];
+ float *righMostColumnContrastTest = new float[width];
+ float *righMostColumnContrastRef = new float[width];
+ float *righMostColumnCrossCorr = new float[width];
+
+ float term1,term2,term3,term4,term5;
+
+ //
+ // SSIM: variable definition, window function, initialization
+ int window = 10;
+ //
+ int flag_window = 0; //0 and 1 for uniform window filter, 2 for gaussian window
+ //
+ float variance_window = 2.0; //variance for window function
+ float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1)
+ //statistics per column of window (#columns = window+1), 0 element for avg over all columns
+ float avgTest[12];
+ float avgRef[12];
+ float contrastTest[12];
+ float contrastRef[12];
+ float crossCorr[12];
+ //
+ //offsets for stability
+ float offset1 = 1.0f; //0.1
+ float offset2 = 1.0f; //0.1
+ //for Guassian window: settings from paper:
+ //float offset1 = 6.0f; // ~ (K1*L)^2 , K1 = 0.01
+ //float offset2 = 58.0f; // ~ (K1*L)^2 , K2 = 0.03
+
+
+ float offset3 = offset2/2;
+ //
+ //define window for SSIM: take uniform filter for now
+ float sumfil = 0.0;
+ int nn=-1;
+ for(int j=-window/2;j<=window/2;j++)
+ for(int i=-window/2;i<=window/2;i++)
+ {
+ nn+=1;
+ if (flag_window != 2)
+ ssimFilter[nn] = 1.0;
+ else
+ {
+ float dist = (float)(i*i) + (float)(j*j);
+ float tmp = 0.5f*dist/variance_window;
+ ssimFilter[nn] = exp(-tmp);
+ }
+ sumfil +=ssimFilter[nn];
+ }
+ //normalize window
+ nn=-1;
+ for(int j=-window/2;j<=window/2;j++)
+ for(int i=-window/2;i<=window/2;i++)
+ {
+ nn+=1;
+ ssimFilter[nn] = ssimFilter[nn]/((float)sumfil);
+ }
+ //
+ float ssimScene = 0.0; //avgerage SSIM for sequence
+ //
+ //SSIM: done with variables and defintion
+ //
+
+ int sh = 8; //boundary offset
+
+ while( refBytes == frameBytes && testBytes == frameBytes )
+ {
+ float ssimFrame = 0.0;
+
+ int numPixels = 0;
+
+ //skip over pixels vertically and horizontally
+ //for window cases 1 and 2
+ int skipH = 2;
+ int skipV = 2;
+
+ //uniform window case, with window computation updated for each pixel horiz and vert: can't skip pixels for this case
+ if (flag_window == 0)
+ {
+ skipH = 1;
+ skipV = 1;
+ }
+ for(int i=sh;i<height-sh;i+=skipV)
+ for(int j=sh;j<width-sh;j+=skipH)
+ {
+ avgTest[0] = 0.0;
+ avgRef[0] = 0.0;
+ contrastTest[0] = 0.0;
+ contrastRef[0] = 0.0;
+ crossCorr[0] = 0.0;
+
+ numPixels +=1;
+
+ if (flag_window > 0 )
+ {
+ //initialize statistics
+ avgTest[0] = 0.0;
+ avgRef[0] = 0.0;
+ contrastTest[0] = 0.0;
+ contrastRef[0] = 0.0;
+ crossCorr[0] = 0.0;
+
+ int nn=-1;
+ //compute contrast and correlation
+ //windows are symmetrics
+ for(int jj=-window/2;jj<=window/2;jj++)
+ for(int ii=-window/2;ii<=window/2;ii++)
+ {
+ nn+=1;
+ int i2 = i+ii;
+ int j2 = j+jj;
+ float tmp1 = (float)test[i2*width+j2];
+ float tmp2 = (float)ref[i2*width+j2];
+
+ term1 = tmp1;
+ term2 = tmp2;
+ term3 = tmp1*tmp1;
+ term4 = tmp2*tmp2;
+ term5 = tmp1*tmp2;
+
+ //local average of each signal
+ avgTest[0] += ssimFilter[nn]*term1;
+ avgRef[0] += ssimFilter[nn]*term2;
+ //local correlation/contrast of each signal
+ contrastTest[0] += ssimFilter[nn]*term3;
+ contrastRef[0] += ssimFilter[nn]*term4;
+ //local cross correlation
+ crossCorr[0] += ssimFilter[nn]*term5;
+
+ }
+
+ }
+
+ else
+ {
+ //for uniform window case == 0: only need to loop over whole window for first row and column, and then shift/update
+ if (j == sh || i == sh)
+ {
+ //initialize statistics
+ for(int k=0;k<window+2;k++)
+ {
+ avgTest[k] = 0.0;
+ avgRef[k] = 0.0;
+ contrastTest[k] = 0.0;
+ contrastRef[k] = 0.0;
+ crossCorr[k] = 0.0;
+ }
+
+ int nn=-1;
+ //compute contrast and correlation
+ //windows are symmetrics
+ for(int jj=-window/2;jj<=window/2;jj++)
+ for(int ii=-window/2;ii<=window/2;ii++)
+ {
+ nn+=1;
+ int i2 = i+ii;
+ int j2 = j+jj;
+ float tmp1 = (float)test[i2*width+j2];
+ float tmp2 = (float)ref[i2*width+j2];
+
+ term1 = tmp1;
+ term2 = tmp2;
+ term3 = tmp1*tmp1;
+ term4 = tmp2*tmp2;
+ term5 = tmp1*tmp2;
+
+ //local average of each signal
+ avgTest[jj+window/2+1] += term1;
+ avgRef[jj+window/2+1] += term2;
+ //local correlation/contrast of each signal
+ contrastTest[jj+window/2+1] += term3;
+ contrastRef[jj+window/2+1] += term4;
+ //local cross correlation
+ crossCorr[jj+window/2+1] += term5;
+
+ }
+
+ //normalize
+ for(int k=1;k<window+2;k++)
+ {
+ avgTest[k] = ssimFilter[0]*avgTest[k];
+ avgRef[k] = ssimFilter[0]*avgRef[k];
+ contrastTest[k] = ssimFilter[0]*contrastTest[k];
+ contrastRef[k] = ssimFilter[0]*contrastRef[k];
+ crossCorr[k] = ssimFilter[0]*crossCorr[k];
+ }
+
+ }
+ //for all other pixels, update window filter computation
+ else
+ {
+ //shift statistics horiz.
+ for(int k=1;k<window+1;k++)
+ {
+ avgTest[k]=avgTest[k+1];
+ avgRef[k]=avgRef[k+1];
+ contrastTest[k] = contrastTest[k+1];
+ contrastRef[k] = contrastRef[k+1];
+ crossCorr[k] = crossCorr[k+1];
+ }
+
+ //compute statistics for last column
+ //update right-most column, by updating with bottom pixel contribution
+ int j2 = j + window/2; //last column of window
+ int i2 = i + window/2; //last window pixel of column
+ int ix = i - window/2 - 1; //last window pixel of top neighboring pixel
+ float tmp1 = (float)test[i2*width+j2];
+ float tmp2 = (float)ref[i2*width+j2];
+ float tmp1x = (float)test[ix*width+j2];
+ float tmp2x = (float)ref[ix*width+j2];
+
+ avgTest[window+1] = righMostColumnAvgTest[j] + ssimFilter[0]*(tmp1 - tmp1x);
+ avgRef[window+1] = righMostColumnAvgRef[j] + ssimFilter[0]*(tmp2 - tmp2x);
+ contrastTest[window+1] = righMostColumnContrastTest[j] + ssimFilter[0]*(tmp1*tmp1 - tmp1x*tmp1x);
+ contrastRef[window+1] = righMostColumnContrastRef[j] + ssimFilter[0]*(tmp2*tmp2 - tmp2x*tmp2x);
+ crossCorr[window+1] = righMostColumnCrossCorr[j] + ssimFilter[0]*(tmp1*tmp2 - tmp1x*tmp2x);
+ }
+
+ //sum over all columns
+ for(int k=1;k<window+2;k++)
+ {
+ avgTest[0] += avgTest[k];
+ avgRef[0] += avgRef[k];
+ contrastTest[0] += contrastTest[k];
+ contrastRef[0] += contrastRef[k];
+ crossCorr[0] += crossCorr[k];
+ }
+
+ //
+ righMostColumnAvgTest[j] = avgTest[window+1];
+ righMostColumnAvgRef[j] = avgRef[window+1];
+ righMostColumnContrastTest[j] = contrastTest[window+1];
+ righMostColumnContrastRef[j] = contrastRef[window+1];
+ righMostColumnCrossCorr[j] = crossCorr[window+1];
+ //
+
+ } //end of window = 0 case
+
+ float tmp1 = (contrastTest[0] - avgTest[0]*avgTest[0]);
+ if (tmp1 < 0.0) tmp1 = 0.0;
+ contrastTest[0] = sqrt(tmp1);
+ float tmp2 = (contrastRef[0] - avgRef[0]*avgRef[0]);
+ if (tmp2 < 0.0) tmp2 = 0.0;
+ contrastRef[0] = sqrt(tmp2);
+ crossCorr[0] = crossCorr[0] - avgTest[0]*avgRef[0];
+
+ float ssimCorrCoeff = (crossCorr[0]+offset3)/(contrastTest[0]*contrastRef[0] + offset3);
+ float ssimLuminance = (2*avgTest[0]*avgRef[0]+offset1)/(avgTest[0]*avgTest[0] + avgRef[0]*avgRef[0] + offset1);
+ float ssimContrast = (2*contrastTest[0]*contrastRef[0]+offset2)/(contrastTest[0]*contrastTest[0] + contrastRef[0]*contrastRef[0] + offset2);
+
+ float ssimPixel = ssimCorrCoeff * ssimLuminance * ssimContrast;
+ ssimFrame += ssimPixel;
+
+ } //done with ssim computation
+
+ ssimFrame = ssimFrame / (numPixels);
+ //printf("***SSIM for frame ***%f \n",ssimFrame);
+ ssimScene += ssimFrame;
+ //
+ //SSIM: done with SSIM computation
+ //
+
+ frames++;
+
+ refBytes = (int) fread(ref, 1, frameBytes, refFp);
+ testBytes = (int) fread(test, 1, frameBytes, testFp);
+
+ }
+
+ //SSIM: normalize/average for sequence
+ ssimScene = ssimScene / frames;
+ *SSIMptr = ssimScene;
+
+
+ delete [] ref;
+ delete [] test;
+
+ delete [] righMostColumnAvgTest;
+ delete [] righMostColumnAvgRef;
+ delete [] righMostColumnContrastTest;
+ delete [] righMostColumnContrastRef;
+ delete [] righMostColumnCrossCorr;
+
+
+ fclose(refFp);
+ fclose(testFp);
+
+ return 0;
+
+}
+
+
+RTPVideoCodecTypes
+ConvertCodecType(const char* plname)
+{
+ if (strncmp(plname,"VP8" , 3) == 0)
+ {
+ return kRTPVideoVP8;
+ }else if (strncmp(plname,"H263" , 5) == 0)
+ {
+ return kRTPVideoH263;
+ }else if (strncmp(plname, "H263-1998",10) == 0)
+ {
+ return kRTPVideoH263;
+ }else if (strncmp(plname,"I420" , 5) == 0)
+ {
+ return kRTPVideoI420;
+ }else
+ {
+ return kRTPVideoNoVideo; // defualt value
+ }
+
+}
+
+WebRtc_Word32
+SendStatsTest::SendStatistics(const WebRtc_UWord32 bitRate, const WebRtc_UWord32 frameRate)
+{
+ TEST(frameRate <= _frameRate);
+ TEST(bitRate > 0 && bitRate < 100000);
+ printf("VCM 1 sec: Bit rate: %u\tFrame rate: %u\n", bitRate, frameRate);
+ return 0;
+}
+
+WebRtc_Word32
+KeyFrameReqTest::FrameTypeRequest(const FrameType frameType)
+{
+ TEST(frameType == kVideoFrameKey);
+ if (frameType == kVideoFrameKey)
+ {
+ printf("Key frame requested\n");
+ }
+ else
+ {
+ printf("Non-key frame requested: %d\n", frameType);
+ }
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/test_util.h b/src/modules/video_coding/main/test/test_util.h
new file mode 100644
index 0000000..42b1bf1
--- /dev/null
+++ b/src/modules/video_coding/main/test/test_util.h
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_UTIL_H
+#define TEST_UTIL_H
+
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "trace.h"
+#include "module_common_types.h"
+#include "tick_time.h"
+#include "test_macros.h"
+#include "test_util.h"
+
+#include <string.h>
+#include <fstream>
+#include <cstdlib>
+
+enum { kMaxWaitEncTimeMs = 100 };
+
+// Class used for passing command line arguments to tests
+class CmdArgs
+{
+public:
+ CmdArgs() : codecName(""), codecType(webrtc::kVideoCodecVP8), width(-1),
+ height(-1), bitRate(-1), frameRate(-1),
+ inputFile(""), outputFile(""), testNum(-1)
+ {}
+ std::string codecName;
+ webrtc::VideoCodecType codecType;
+ int width;
+ int height;
+ int bitRate;
+ int frameRate;
+ std::string inputFile;
+ std::string outputFile;
+ int testNum;
+};
+
+// forward declaration
+int MTRxTxTest(CmdArgs& args);
+namespace webrtc
+{
+ class RtpDump;
+}
+
+// definition of general test function to be used by VCM tester (mainly send side)
+/*
+ Includes the following:
+ 1. General Callback definition for VCM test functions - no RTP.
+ 2. EncodeComplete callback:
+ 2a. Transfer encoded data directly to the decoder
+ 2b. Pass encoded data via the RTP module
+ 3. Caluclate PSNR from file function (for now: does not deal with frame drops)
+ */
+
+//Send Side - Packetization callback - send an encoded frame directly to the VCMReceiver
+class VCMEncodeCompleteCallback: public webrtc::VCMPacketizationCallback
+{
+public:
+ // constructor input: file in which encoded data will be written, and test parameters
+ VCMEncodeCompleteCallback(FILE* encodedFile);
+ virtual ~VCMEncodeCompleteCallback();
+ // Register transport callback
+ void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport);
+ // process encoded data received from the encoder, pass stream to the VCMReceiver module
+ WebRtc_Word32 SendData(const webrtc::FrameType frameType,
+ const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize,
+ const webrtc::RTPFragmentationHeader& fragmentationHeader);
+ // Register exisitng VCM. Currently - encode and decode with the same vcm module.
+ void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm) { _VCMReceiver = vcm; }
+ // Return size of last encoded frame encoded data (all frames in the sequence)
+ // Good for only one call - after which will reset value (to allow detection of frame drop)
+ float EncodedBytes();
+ // return encode complete (true/false)
+ bool EncodeComplete();
+ // Inform callback of codec used
+ void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; }
+ // inform callback of frame dimensions
+ void SetFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height)
+ {
+ _width = width;
+ _height = height;
+ }
+ //Initialize callback data
+ void Initialize();
+ void ResetByteCount();
+
+ // conversion function for payload type (needed for the callback function)
+ // RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType);
+
+private:
+ FILE* _encodedFile;
+ float _encodedBytes;
+ webrtc::VideoCodingModule* _VCMReceiver;
+ webrtc::FrameType _frameType;
+ WebRtc_UWord8* _payloadData;
+ WebRtc_UWord8 _seqNo;
+ bool _encodeComplete;
+ WebRtc_Word32 _width;
+ WebRtc_Word32 _height;
+ webrtc::RTPVideoCodecTypes _codecType;
+ WebRtc_UWord8 _layerPacketId;
+
+}; // end of VCMEncodeCompleteCallback
+
+//Send Side - Packetization callback - packetize an encoded frame via the RTP module
+class VCMRTPEncodeCompleteCallback: public webrtc::VCMPacketizationCallback
+{
+public:
+ VCMRTPEncodeCompleteCallback(webrtc::RtpRtcp* rtp) :
+ _seqNo(0), _encodedBytes(0), _RTPModule(rtp), _encodeComplete(false) {}
+ virtual ~VCMRTPEncodeCompleteCallback() {}
+ // process encoded data received from the encoder, pass stream to the RTP module
+ WebRtc_Word32 SendData(const webrtc::FrameType frameType,
+ const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp,
+ const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize,
+ const webrtc::RTPFragmentationHeader& fragmentationHeader);
+ // Return size of last encoded frame. Value good for one call
+ // (resets to zero after call to inform test of frame drop)
+ float EncodedBytes();
+ // return encode complete (true/false)
+ bool EncodeComplete();
+ // Inform callback of codec used
+ void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; }
+
+ // inform callback of frame dimensions
+ void SetFrameDimensions(WebRtc_Word16 width, WebRtc_Word16 height)
+ {
+ _width = width;
+ _height = height;
+ }
+
+private:
+ float _encodedBytes;
+ webrtc::FrameType _frameType;
+ WebRtc_UWord8* _payloadData;
+ WebRtc_UWord16 _seqNo;
+ bool _encodeComplete;
+ webrtc::RtpRtcp* _RTPModule;
+ WebRtc_Word16 _width;
+ WebRtc_Word16 _height;
+ webrtc::RTPVideoCodecTypes _codecType;
+}; // end of VCMEncodeCompleteCallback
+
+class VCMDecodeCompleteCallback: public webrtc::VCMReceiveCallback
+{
+public:
+ VCMDecodeCompleteCallback(FILE* decodedFile) :
+ _decodedFile(decodedFile), _decodedBytes(0) {}
+ virtual ~VCMDecodeCompleteCallback() {}
+ // will write decoded frame into file
+ WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
+ WebRtc_Word32 DecodedBytes();
+ int PSNRLastFrame(const webrtc::VideoFrame& sourceFrame, double *YPSNRptr);
+private:
+ FILE* _decodedFile;
+ WebRtc_UWord32 _decodedBytes;
+ webrtc::VideoFrame _lastDecodedFrame;
+}; // end of VCMDecodeCompleCallback class
+
+///
+class RTPSendCompleteCallback: public webrtc::Transport
+{
+public:
+ // constructor input: (reeive side) rtp module to send encoded data to
+ RTPSendCompleteCallback(webrtc::RtpRtcp* rtp,
+ const char* filename = NULL);
+ virtual ~RTPSendCompleteCallback();
+ // Send Packet to receive side RTP module
+ virtual int SendPacket(int channel, const void *data, int len);
+ // Send RTCP Packet to receive side RTP module
+ virtual int SendRTCPPacket(int channel, const void *data, int len);
+ // Set percentage of channel loss in the network
+ void SetLossPct(double lossPct);
+ // return send count
+ int SendCount() { return _sendCount; }
+private:
+ // randomly decide weather to drop a packet or not, based on the channel model
+ bool PacketLoss(double lossPct);
+
+ WebRtc_UWord32 _sendCount;
+ webrtc::RtpRtcp* _rtp;
+ double _lossPct;
+ webrtc::RtpDump* _rtpDump;
+};
+
+// used in multi thread test
+class SendSharedState
+{
+public:
+ SendSharedState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp,
+ CmdArgs args) :
+ _rtp(rtp), _vcm(vcm), _args(args), _sourceFile(NULL), _frameCnt(0),
+ _timestamp(0) {}
+
+ webrtc::VideoCodingModule& _vcm;
+ webrtc::RtpRtcp& _rtp;
+ CmdArgs _args;
+ FILE* _sourceFile;
+ WebRtc_Word32 _frameCnt;
+ WebRtc_Word32 _timestamp;
+};
+
+class PacketRequester: public webrtc::VCMPacketRequestCallback
+{
+public:
+ PacketRequester(webrtc::RtpRtcp& rtp) :
+ _rtp(rtp) {}
+ WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers,
+ WebRtc_UWord16 length);
+
+private:
+ webrtc::RtpRtcp& _rtp;
+};
+
+// PSNR & SSIM calculations
+WebRtc_Word32
+PSNRfromFiles(const WebRtc_Word8 *refFileName,
+ const WebRtc_Word8 *testFileName, WebRtc_Word32 width,
+ WebRtc_Word32 height, double *YPSNRptr);
+
+WebRtc_Word32
+SSIMfromFiles(const WebRtc_Word8 *refFileName,
+ const WebRtc_Word8 *testFileName, WebRtc_Word32 width,
+ WebRtc_Word32 height, double *SSIMptr);
+
+// codec type conversion
+webrtc::RTPVideoCodecTypes
+ConvertCodecType(const char* plname);
+
+class SendStatsTest: public webrtc::VCMSendStatisticsCallback
+{
+public:
+ SendStatsTest() : _frameRate(15) {}
+ WebRtc_Word32 SendStatistics(const WebRtc_UWord32 bitRate,
+ const WebRtc_UWord32 frameRate);
+ void SetTargetFrameRate(WebRtc_UWord32 frameRate) { _frameRate = frameRate; }
+private:
+ WebRtc_UWord32 _frameRate;
+};
+
+class KeyFrameReqTest: public webrtc::VCMFrameTypeCallback
+{
+public:
+ WebRtc_Word32 FrameTypeRequest(const webrtc::FrameType frameType);
+};
+
+#endif
diff --git a/src/modules/video_coding/main/test/tester_main.cc b/src/modules/video_coding/main/test/tester_main.cc
new file mode 100644
index 0000000..8d8e37f
--- /dev/null
+++ b/src/modules/video_coding/main/test/tester_main.cc
@@ -0,0 +1,187 @@
+/*
+ * 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 "receiver_tests.h"
+#include "normal_test.h"
+#include "codec_database_test.h"
+#include "generic_codec_test.h"
+#include "../source/event.h"
+#include "media_opt_test.h"
+#include "quality_modes_test.h"
+#include "test_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+//#include "vld.h"
+#endif
+
+using namespace webrtc;
+
+/*
+ * Build with TICK_TIME_DEBUG and EVENT_DEBUG defined
+ * to build the tests with simulated clock.
+ */
+
+// TODO(holmer): How do we get debug time into the cmd line interface?
+/* Debug time */
+#if defined(TICK_TIME_DEBUG) && defined(EVENT_DEBUG)
+WebRtc_Word64 VCMTickTime::_timeNowDebug = 0; // current time in ms
+#endif
+
+int ParseArguments(int argc, char **argv, CmdArgs& args)
+{
+ int i = 1;
+ while (i < argc)
+ {
+ if (argv[i][0] != '-')
+ {
+ return -1;
+ }
+ switch (argv[i][1])
+ {
+ case 'w':
+ {
+ int w = atoi(argv[i+1]);
+ if (w < 1)
+ return -1;
+ args.width = w;
+ break;
+ }
+ case 'h':
+ {
+ int h = atoi(argv[i+1]);
+ if (h < 1)
+ return -1;
+ args.height = h;
+ break;
+ }
+ case 'b':
+ {
+ int b = atoi(argv[i+1]);
+ if (b < 1)
+ return -1;
+ args.bitRate = b;
+ break;
+ }
+ case 'f':
+ {
+ int f = atoi(argv[i+1]);
+ if (f < 1)
+ return -1;
+ args.frameRate = f;
+ break;
+ }
+ case 'c':
+ {
+ // TODO(holmer): This should be replaced with a map if more codecs
+ // are added
+ args.codecName = argv[i+1];
+ if (strncmp(argv[i+1], "VP8", 3) == 0)
+ {
+ args.codecType = kVideoCodecVP8;
+ }
+ else if (strncmp(argv[i+1], "I420", 4) == 0)
+ {
+ args.codecType = kVideoCodecI420;
+ }
+ else if (strncmp(argv[i+1], "H263", 4) == 0)
+ {
+ args.codecType = kVideoCodecH263;
+ }
+ else
+ return -1;
+
+ break;
+ }
+ case 'i':
+ {
+ args.inputFile = argv[i+1];
+ break;
+ }
+ case 'o':
+ args.outputFile = argv[i+1];
+ break;
+ case 'n':
+ {
+ int n = atoi(argv[i+1]);
+ if (n < 1)
+ return -1;
+ args.testNum = n;
+ break;
+ }
+ default:
+ return -1;
+ }
+ i += 2;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ CmdArgs args;
+
+ if (ParseArguments(argc, argv, args) != 0)
+ {
+ printf("Unable to parse input arguments\n");
+ printf("args: -n <test #> -w <width> -h <height> -f <fps> -b <bps> -c <codec>"
+ " -i <input file> -o <output file>\n");
+ return -1;
+ }
+ int ret = 0;
+ switch (args.testNum)
+ {
+ case 1:
+ ret = NormalTest::RunTest(args);
+ break;
+ case 2:
+ ret = MTRxTxTest(args);
+ break;
+ case 3:
+ ret = GenericCodecTest::RunTest(args);
+ break;
+ case 4:
+ ret = CodecDataBaseTest::RunTest(args);
+ break;
+ case 5:
+ // 0- normal, 1-Release test(50 runs) 2- from file
+ ret = MediaOptTest::RunTest(0, args);
+ break;
+ case 6:
+ ret = ReceiverTimingTests(args);
+ break;
+ case 7:
+ ret = RtpPlay(args);
+ break;
+ case 8:
+ ret = RtpPlayMT(args);
+ break;
+ case 9:
+ ret = JitterBufferTest(args);
+ break;
+ case 10:
+ ret = DecodeFromStorageTest(args);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ if (ret != 0)
+ {
+ printf("Test failed!\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+
diff --git a/src/modules/video_coding/main/test/video_rtp_play.cc b/src/modules/video_coding/main/test/video_rtp_play.cc
new file mode 100644
index 0000000..c2134fb
--- /dev/null
+++ b/src/modules/video_coding/main/test/video_rtp_play.cc
@@ -0,0 +1,206 @@
+/*
+ * 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 "receiver_tests.h"
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "trace.h"
+#include "tick_time.h"
+#include "../source/event.h"
+#include "../source/internal_defines.h"
+#include "test_macros.h"
+#include "rtp_player.h"
+
+#include <stdio.h>
+#include <string.h>
+
+using namespace webrtc;
+
+WebRtc_Word32
+RtpDataCallback::OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
+ const WebRtc_UWord16 payloadSize,
+ const WebRtcRTPHeader* rtpHeader)
+{
+ return _vcm->IncomingPacket(payloadData, payloadSize, *rtpHeader);
+}
+
+FrameReceiveCallback::~FrameReceiveCallback()
+{
+ if (_timingFile != NULL)
+ {
+ fclose(_timingFile);
+ }
+ if (_outFile != NULL)
+ {
+ fclose(_outFile);
+ }
+}
+
+WebRtc_Word32
+FrameReceiveCallback::FrameToRender(VideoFrame& videoFrame)
+{
+ if (_timingFile == NULL)
+ {
+ _timingFile = fopen("renderTiming.txt", "w");
+ if (_timingFile == NULL)
+ {
+ return -1;
+ }
+ }
+ if (_outFile == NULL)
+ {
+ _outFile = fopen(_outFilename.c_str(), "wb");
+ if (_outFile == NULL)
+ {
+ return -1;
+ }
+ }
+ fprintf(_timingFile, "%u, %u\n",
+ videoFrame.TimeStamp(),
+ MaskWord64ToUWord32(videoFrame.RenderTimeMs()));
+ fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _outFile);
+ return 0;
+}
+
+int RtpPlay(CmdArgs& args)
+{
+ // Make sure this test isn't executed without simulated clocks
+#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
+ return -1;
+#endif
+ // BEGIN Settings
+
+ bool protectionEnabled = false;
+ VCMVideoProtection protectionMethod = kProtectionNack;
+ WebRtc_UWord32 rttMS = 10;
+ float lossRate = 0.0f;
+ bool reordering = false;
+ WebRtc_UWord32 renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+ const WebRtc_Word64 MAX_RUNTIME_MS = -1;
+ std::string outFile = args.outputFile;
+ if (outFile == "")
+ outFile = "RtpPlay_decoded.yuv";
+ FrameReceiveCallback receiveCallback(outFile);
+ VideoCodingModule* vcm = VideoCodingModule::Create(1);
+ RtpDataCallback dataCallback(vcm);
+ RTPPlayer rtpStream(args.inputFile.c_str(), &dataCallback);
+
+
+ ListWrapper payloadTypes;
+ payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8));
+
+ Trace::CreateTrace();
+ Trace::SetTraceFile("receiverTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ // END Settings
+
+ // Set up
+
+ WebRtc_Word32 ret = vcm->InitializeReceiver();
+ if (ret < 0)
+ {
+ return -1;
+ }
+ vcm->RegisterReceiveCallback(&receiveCallback);
+ vcm->RegisterPacketRequestCallback(&rtpStream);
+
+ // Register receive codecs in VCM
+ ListItem* item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ VideoCodec codec;
+ if (VideoCodingModule::Codec(payloadType->codecType, &codec) < 0)
+ {
+ return -1;
+ }
+ codec.plType = payloadType->payloadType;
+ if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
+ {
+ return -1;
+ }
+ }
+ item = payloadTypes.Next(item);
+ }
+
+ if (rtpStream.Initialize(payloadTypes) < 0)
+ {
+ return -1;
+ }
+ bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack ||
+ protectionMethod == kProtectionDualDecoder);
+ rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
+ rtpStream.SetReordering(reordering);
+ vcm->SetChannelParameters(0, 0, rttMS);
+ vcm->SetVideoProtection(protectionMethod, protectionEnabled);
+ vcm->SetRenderDelay(renderDelayMs);
+ vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
+
+ ret = 0;
+
+ // RTP stream main loop
+ while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0)
+ {
+ if (VCMTickTime::MillisecondTimestamp() % 5 == 0)
+ {
+ ret = vcm->Decode();
+ if (ret < 0)
+ {
+ return -1;
+ }
+ }
+ while (vcm->DecodeDualFrame(0) == 1);
+ if (vcm->TimeUntilNextProcess() <= 0)
+ {
+ vcm->Process();
+ }
+ if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS)
+ {
+ break;
+ }
+ VCMTickTime::IncrementDebugClock();
+ }
+
+ switch (ret)
+ {
+ case 1:
+ printf("Success\n");
+ break;
+ case -1:
+ printf("Failed\n");
+ break;
+ case 0:
+ printf("Timeout\n");
+ break;
+ }
+
+ rtpStream.Print();
+
+ // Tear down
+ item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ delete payloadType;
+ }
+ ListItem* itemToRemove = item;
+ item = payloadTypes.Next(item);
+ payloadTypes.Erase(itemToRemove);
+ }
+ delete vcm;
+ vcm = NULL;
+ Trace::ReturnTrace();
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/video_rtp_play_mt.cc b/src/modules/video_coding/main/test/video_rtp_play_mt.cc
new file mode 100644
index 0000000..7d30101
--- /dev/null
+++ b/src/modules/video_coding/main/test/video_rtp_play_mt.cc
@@ -0,0 +1,271 @@
+/*
+ * 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 "receiver_tests.h"
+#include "video_coding.h"
+#include "rtp_rtcp.h"
+#include "trace.h"
+#include "thread_wrapper.h"
+#include "../source/event.h"
+#include "tick_time.h"
+#include "test_macros.h"
+#include "rtp_player.h"
+
+#include <string.h>
+
+using namespace webrtc;
+
+bool ProcessingThread(void* obj)
+{
+ SharedState* state = static_cast<SharedState*>(obj);
+ if (state->_vcm.TimeUntilNextProcess() <= 0)
+ {
+ if (state->_vcm.Process() < 0)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool RtpReaderThread(void* obj)
+{
+ SharedState* state = static_cast<SharedState*>(obj);
+ EventWrapper& waitEvent = *EventWrapper::Create();
+ // RTP stream main loop
+ WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
+ if (state->_rtpPlayer.NextPacket(nowMs) < 0)
+ {
+ return false;
+ }
+ waitEvent.Wait(state->_rtpPlayer.TimeUntilNextPacket());
+ delete &waitEvent;
+ return true;
+}
+
+bool DecodeThread(void* obj)
+{
+ SharedState* state = static_cast<SharedState*>(obj);
+ WebRtc_Word32 ret = state->_vcm.Decode(10000);
+ while (state->_vcm.DecodeDualFrame(0) == 1);
+ return true;
+}
+
+int RtpPlayMT(CmdArgs& args, int releaseTestNo, webrtc::VideoCodecType releaseTestVideoType)
+{
+ // Don't run these tests with debug time
+#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
+ return -1;
+#endif
+
+ // BEGIN Settings
+
+ bool protectionEnabled = true;
+ VCMVideoProtection protection = kProtectionDualDecoder;
+ WebRtc_UWord8 rttMS = 50;
+ float lossRate = 0.05f;
+ WebRtc_UWord32 renderDelayMs = 0;
+ WebRtc_UWord32 minPlayoutDelayMs = 0;
+ const WebRtc_Word64 MAX_RUNTIME_MS = 10000;
+ std::string outFilename = args.outputFile;
+ if (outFilename == "")
+ outFilename = "RtpPlayMT_decoded.yuv";
+
+ bool nackEnabled = (protectionEnabled &&
+ (protection == kProtectionDualDecoder ||
+ protection == kProtectionNack ||
+ kProtectionNackFEC));
+ VideoCodingModule* vcm =
+ VideoCodingModule::Create(1);
+ RtpDataCallback dataCallback(vcm);
+ std::string rtpFilename;
+ rtpFilename = args.inputFile;
+ if (releaseTestNo > 0)
+ {
+ // Setup a release test
+ switch (releaseTestVideoType)
+ {
+ case webrtc::kVideoCodecVP8:
+ rtpFilename = args.inputFile;
+ outFilename = "MTReceiveTest_VP8";
+ break;
+ default:
+ return -1;
+ }
+ switch (releaseTestNo)
+ {
+ case 1:
+ // Normal execution
+ protectionEnabled = false;
+ nackEnabled = false;
+ rttMS = 0;
+ lossRate = 0.0f;
+ outFilename += "_Normal.yuv";
+ break;
+ case 2:
+ // Packet loss
+ protectionEnabled = false;
+ nackEnabled = false;
+ rttMS = 0;
+ lossRate = 0.05f;
+ outFilename += "_0.05.yuv";
+ break;
+ case 3:
+ // Packet loss and NACK
+ protection = kProtectionNack;
+ nackEnabled = true;
+ protectionEnabled = true;
+ rttMS = 100;
+ lossRate = 0.05f;
+ outFilename += "_0.05_NACK_100ms.yuv";
+ break;
+ case 4:
+ // Packet loss and dual decoder
+ // Not implemented
+ return 0;
+ break;
+ default:
+ return -1;
+ }
+ printf("Watch %s to verify that the output is reasonable\n", outFilename.c_str());
+ }
+ RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback);
+ ListWrapper payloadTypes;
+ payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE,
+ "VP8", kVideoCodecVP8));
+
+ Trace::CreateTrace();
+ Trace::SetTraceFile("receiverTestTrace.txt");
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+
+ // END Settings
+
+ // Set up
+
+ SharedState mtState(*vcm, rtpStream);
+
+ if (rtpStream.Initialize(payloadTypes) < 0)
+ {
+ return -1;
+ }
+ rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
+
+ WebRtc_Word32 ret = vcm->InitializeReceiver();
+ if (ret < 0)
+ {
+ return -1;
+ }
+
+ // Create and start all threads
+ ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread,
+ &mtState, kNormalPriority, "ProcessingThread");
+ ThreadWrapper* rtpReaderThread = ThreadWrapper::CreateThread(RtpReaderThread,
+ &mtState, kNormalPriority, "RtpReaderThread");
+ ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread,
+ &mtState, kNormalPriority, "DecodeThread");
+
+ // Register receive codecs in VCM
+ ListItem* item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ VideoCodec codec;
+ VideoCodingModule::Codec(payloadType->codecType, &codec);
+ codec.plType = payloadType->payloadType;
+ if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
+ {
+ return -1;
+ }
+ }
+ item = payloadTypes.Next(item);
+ }
+
+ if (processingThread != NULL)
+ {
+ unsigned int tid;
+ processingThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start processing thread\n");
+ return -1;
+ }
+ if (rtpReaderThread != NULL)
+ {
+ unsigned int tid;
+ rtpReaderThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start RTP reader thread\n");
+ return -1;
+ }
+ if (decodeThread != NULL)
+ {
+ unsigned int tid;
+ decodeThread->Start(tid);
+ }
+ else
+ {
+ printf("Unable to start decode thread\n");
+ return -1;
+ }
+
+ FrameReceiveCallback receiveCallback(outFilename);
+ vcm->RegisterReceiveCallback(&receiveCallback);
+ vcm->RegisterPacketRequestCallback(&rtpStream);
+
+ vcm->SetChannelParameters(0, 0, rttMS);
+ vcm->SetVideoProtection(protection, protectionEnabled);
+ vcm->SetRenderDelay(renderDelayMs);
+ vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
+
+ EventWrapper& waitEvent = *EventWrapper::Create();
+
+ // Decode for 10 seconds and then tear down and exit.
+ waitEvent.Wait(MAX_RUNTIME_MS);
+
+ // Tear down
+ item = payloadTypes.First();
+ while (item != NULL)
+ {
+ PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
+ if (payloadType != NULL)
+ {
+ delete payloadType;
+ }
+ ListItem* itemToRemove = item;
+ item = payloadTypes.Next(item);
+ payloadTypes.Erase(itemToRemove);
+ }
+ while (!processingThread->Stop())
+ {
+ ;
+ }
+ while (!rtpReaderThread->Stop())
+ {
+ ;
+ }
+ while (!decodeThread->Stop())
+ {
+ ;
+ }
+ VideoCodingModule::Destroy(vcm);
+ vcm = NULL;
+ delete &waitEvent;
+ delete processingThread;
+ delete decodeThread;
+ delete rtpReaderThread;
+ rtpStream.Print();
+ Trace::ReturnTrace();
+ return 0;
+}
diff --git a/src/modules/video_coding/main/test/video_source.cc b/src/modules/video_coding/main/test/video_source.cc
new file mode 100644
index 0000000..756e336
--- /dev/null
+++ b/src/modules/video_coding/main/test/video_source.cc
@@ -0,0 +1,200 @@
+/*
+ * 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 "vplib.h"
+#include "video_source.h"
+#include <cassert>
+
+VideoSource::VideoSource()
+:
+_fileName("../../../../../codecs_video/testFiles/foreman.yuv"),
+_width(352),
+_height(288),
+_type(webrtc::kI420),
+_frameRate(30)
+{
+ //
+}
+
+VideoSource::VideoSource(std::string fileName, VideoSize size,
+ float frameRate, webrtc::VideoType type /*= webrtc::kI420*/)
+:
+_fileName(fileName),
+_type(type),
+_frameRate(frameRate),
+_width(0),
+_height(0)
+{
+ assert(size != kUndefined && size != kNumberOfVideoSizes);
+ assert(type != webrtc::kUnknown);
+ assert(frameRate > 0);
+ GetWidthHeight(size);
+}
+
+VideoSource::VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height,
+ float frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/)
+:
+_fileName(fileName),
+_width(width),
+_height(height),
+_type(type),
+_frameRate(frameRate)
+{
+ assert(width > 0);
+ assert(height > 0);
+ assert(type != webrtc::kUnknown);
+ assert(frameRate > 0);
+}
+
+WebRtc_Word32
+VideoSource::GetFrameLength() const
+{
+ return webrtc::CalcBufferSize(_type, _width, _height);
+}
+
+std::string
+VideoSource::GetName() const
+{
+ // Remove path.
+ size_t slashPos = _fileName.find_last_of("/\\");
+ if (slashPos == std::string::npos)
+ {
+ slashPos = 0;
+ }
+ else
+ {
+ slashPos++;
+ }
+
+ // Remove extension and underscored suffix if it exists.
+ //return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"),
+ // _fileName.find_last_of(".")) - slashPos);
+ // MS: Removing suffix, not underscore....keeping full file name
+ return _fileName.substr(slashPos, _fileName.find_last_of(".") - slashPos);
+
+}
+
+int
+VideoSource::GetWidthHeight( VideoSize size)
+{
+ switch(size)
+ {
+ case kSQCIF:
+ _width = 128;
+ _height = 96;
+ return 0;
+ case kQQVGA:
+ _width = 160;
+ _height = 120;
+ return 0;
+ case kQCIF:
+ _width = 176;
+ _height = 144;
+ return 0;
+ case kCGA:
+ _width = 320;
+ _height = 200;
+ return 0;
+ case kQVGA:
+ _width = 320;
+ _height = 240;
+ return 0;
+ case kSIF:
+ _width = 352;
+ _height = 240;
+ return 0;
+ case kWQVGA:
+ _width = 400;
+ _height = 240;
+ return 0;
+ case kCIF:
+ _width = 352;
+ _height = 288;
+ return 0;
+ case kW288p:
+ _width = 512;
+ _height = 288;
+ return 0;
+ case k448p:
+ _width = 576;
+ _height = 448;
+ return 0;
+ case kVGA:
+ _width = 640;
+ _height = 480;
+ return 0;
+ case k432p:
+ _width = 720;
+ _height = 432;
+ return 0;
+ case kW432p:
+ _width = 768;
+ _height = 432;
+ return 0;
+ case k4SIF:
+ _width = 704;
+ _height = 480;
+ return 0;
+ case kW448p:
+ _width = 768;
+ _height = 448;
+ return 0;
+ case kNTSC:
+ _width = 720;
+ _height = 480;
+ return 0;
+ case kFW448p:
+ _width = 800;
+ _height = 448;
+ return 0;
+ case kWVGA:
+ _width = 800;
+ _height = 480;
+ return 0;
+ case k4CIF:
+ _width = 704;
+ _height = 576;
+ return 0;
+ case kSVGA:
+ _width = 800;
+ _height = 600;
+ return 0;
+ case kW544p:
+ _width = 960;
+ _height = 544;
+ return 0;
+ case kW576p:
+ _width = 1024;
+ _height = 576;
+ return 0;
+ case kHD:
+ _width = 960;
+ _height = 720;
+ return 0;
+ case kXGA:
+ _width = 1024;
+ _height = 768;
+ return 0;
+ case kFullHD:
+ _width = 1440;
+ _height = 1080;
+ return 0;
+ case kWHD:
+ _width = 1280;
+ _height = 720;
+ return 0;
+ case kWFullHD:
+ _width = 1920;
+ _height = 1080;
+ return 0;
+ default:
+ return -1;
+ }
+}
diff --git a/src/modules/video_coding/main/test/video_source.h b/src/modules/video_coding/main/test/video_source.h
new file mode 100644
index 0000000..0f7d5e8
--- /dev/null
+++ b/src/modules/video_coding/main/test/video_source.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
+#define WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
+
+#include "vplib.h"
+#include "typedefs.h"
+
+#include <string>
+
+enum VideoSize
+ {
+ kUndefined,
+ kSQCIF, // 128*96 = 12 288
+ kQQVGA, // 160*120 = 19 200
+ kQCIF, // 176*144 = 25 344
+ kCGA, // 320*200 = 64 000
+ kQVGA, // 320*240 = 76 800
+ kSIF, // 352*240 = 84 480
+ kWQVGA, // 400*240 = 96 000
+ kCIF, // 352*288 = 101 376
+ kW288p, // 512*288 = 147 456 (WCIF)
+ k448p, // 576*448 = 281 088
+ kVGA, // 640*480 = 307 200
+ k432p, // 720*432 = 311 040
+ kW432p, // 768*432 = 331 776
+ k4SIF, // 704*480 = 337 920
+ kW448p, // 768*448 = 344 064
+ kNTSC, // 720*480 = 345 600
+ kFW448p, // 800*448 = 358 400
+ kWVGA, // 800*480 = 384 000
+ k4CIF, // 704*576 = 405 504
+ kSVGA, // 800*600 = 480 000
+ kW544p, // 960*544 = 522 240
+ kW576p, // 1024*576 = 589 824 (W4CIF)
+ kHD, // 960*720 = 691 200
+ kXGA, // 1024*768 = 786 432
+ kWHD, // 1280*720 = 921 600
+ kFullHD, // 1440*1080 = 1 555 200
+ kWFullHD, // 1920*1080 = 2 073 600
+
+ kNumberOfVideoSizes
+ };
+
+
+class VideoSource
+{
+public:
+ VideoSource();
+ VideoSource(std::string fileName, VideoSize size, float frameRate, webrtc::VideoType type = webrtc::kI420);
+ VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height,
+ float frameRate = 30, webrtc::VideoType type = webrtc::kI420);
+
+ std::string GetFileName() const { return _fileName; }
+ WebRtc_UWord16 GetWidth() const { return _width; }
+ WebRtc_UWord16 GetHeight() const { return _height; }
+ webrtc::VideoType GetType() const { return _type; }
+ float GetFrameRate() const { return _frameRate; }
+ int GetWidthHeight( VideoSize size);
+
+ // Returns the filename with the path (including the leading slash) removed.
+ std::string GetName() const;
+
+ WebRtc_Word32 GetFrameLength() const;
+
+private:
+ std::string _fileName;
+ WebRtc_UWord16 _width;
+ WebRtc_UWord16 _height;
+ webrtc::VideoType _type;
+ float _frameRate;
+};
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
+