/*
 *  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.
 */

/*
 * vie_network_impl.cpp
 */

#include "vie_network_impl.h"

// Defines
#include "engine_configurations.h"
#include "vie_defines.h"

// WebRTC include
#include "vie_errors.h"
#include "trace.h"
#include "vie_impl.h"
#include "vie_channel.h"
#include "vie_channel_manager.h"
#include "vie_encoder.h"

// System include
#include <stdio.h>

// Conditional system include
#if (defined(_WIN32) || defined(_WIN64))
#include <qos.h>
#endif

namespace webrtc
{

// ----------------------------------------------------------------------------
// GetInterface
// ----------------------------------------------------------------------------

ViENetwork* ViENetwork::GetInterface(VideoEngine* videoEngine)
{
#ifdef WEBRTC_VIDEO_ENGINE_NETWORK_API
    if (videoEngine == NULL)
    {
        return NULL;
    }
    VideoEngineImpl* vieImpl = reinterpret_cast<VideoEngineImpl*> (videoEngine);
    ViENetworkImpl* vieNetworkImpl = vieImpl;
    (*vieNetworkImpl)++; // Increase ref count

    return vieNetworkImpl;
#else
    return NULL;
#endif
}

// ----------------------------------------------------------------------------
// Release
//
// Releases the interface, i.e. reduces the reference counter. The number of
// remaining references is returned, -1 if released too many times.
// ----------------------------------------------------------------------------

int ViENetworkImpl::Release()
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, _instanceId,
                 "ViENetwork::Release()");
    (*this)--; // Decrease ref count

    WebRtc_Word32 refCount = GetCount();
    if (refCount < 0)
    {
        WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, _instanceId,
                     "ViENetwork release too many times");
        SetLastError(kViEAPIDoesNotExist);
        return -1;
    }
    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, _instanceId,
                 "ViENetwork reference count: %d", refCount);
    return refCount;
}

// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------

ViENetworkImpl::ViENetworkImpl()
{
    WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, _instanceId,
                 "ViENetworkImpl::ViENetworkImpl() Ctor");
}

// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------

ViENetworkImpl::~ViENetworkImpl()
{
    WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, _instanceId,
                 "ViENetworkImpl::~ViENetworkImpl() Dtor");
}

// ============================================================================
// Receive functions
// ============================================================================

// ----------------------------------------------------------------------------
// SetLocalReceiver
//
// Initializes the receive socket
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetLocalReceiver(const int videoChannel,
                                     const unsigned short rtpPort,
                                     const unsigned short rtcpPort,
                                     const char* ipAddress)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, rtpPort: %u, rtcpPort: %u, ipAddress: %s)",
                 __FUNCTION__, videoChannel, rtpPort, rtcpPort, ipAddress);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId,videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }

    if (ptrViEChannel->Receiving())
    {
        SetLastError(kViENetworkAlreadyReceiving);
        return -1;
    }
    if (ptrViEChannel->SetLocalReceiver(rtpPort, rtcpPort, ipAddress) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// GetLocalReceiver
//
// Gets settings for an initialized receive socket
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetLocalReceiver(const int videoChannel,
                                     unsigned short& rtpPort,
                                     unsigned short& rtcpPort, char* ipAddress)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetLocalReceiver(rtpPort, rtcpPort, ipAddress) != 0)
    {
        SetLastError(kViENetworkLocalReceiverNotSet);
        return -1;
    }
    return 0;
}

// ============================================================================
// Send functions
// ============================================================================

// ----------------------------------------------------------------------------
// SetSendDestination
//
// Initializes the send socket
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetSendDestination(const int videoChannel,
                                       const char* ipAddress,
                                       const unsigned short rtpPort,
                                       const unsigned short rtcpPort,
                                       const unsigned short sourceRtpPort,
                                       const unsigned short sourceRtcpPort)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, ipAddress: %s, rtpPort: %u, rtcpPort: %u, "
                 "sourceRtpPort: %u, sourceRtcpPort: %u)",
                 __FUNCTION__, videoChannel, ipAddress, rtpPort, rtcpPort,
                 sourceRtpPort, sourceRtcpPort);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel doesn't exist", __FUNCTION__);
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->Sending())
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel already sending.", __FUNCTION__);
        SetLastError(kViENetworkAlreadySending);
        return -1;
    }
    if (ptrViEChannel->SetSendDestination(ipAddress, rtpPort, rtcpPort,
                                          sourceRtpPort, sourceRtcpPort) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }

    return 0;
}

// ----------------------------------------------------------------------------
// GetSendDestination
//
// Gets settings for an initialized send socket
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetSendDestination(const int videoChannel, char* ipAddress,
                                       unsigned short& rtpPort,
                                       unsigned short& rtcpPort,
                                       unsigned short& sourceRtpPort,
                                       unsigned short& sourceRtcpPort)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetSendDestination(ipAddress, rtpPort, rtcpPort,
                                          sourceRtpPort, sourceRtcpPort) != 0)
    {
        SetLastError(kViENetworkDestinationNotSet);
        return -1;
    }
    return 0;
}

// ============================================================================
// External transport
// ============================================================================

// ----------------------------------------------------------------------------
// RegisterSendTransport
//
// Registers the customer implemented send transport
// ----------------------------------------------------------------------------
int ViENetworkImpl::RegisterSendTransport(const int videoChannel,
                                          Transport& transport)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel doesn't exist", __FUNCTION__);
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->Sending())
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel already sending.", __FUNCTION__);
        SetLastError(kViENetworkAlreadySending);
        return -1;
    }
    if (ptrViEChannel->RegisterSendTransport(transport) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// DeregisterSendTransport
//
// Deregisters the send transport implementation from
// RegisterSendTransport
// ----------------------------------------------------------------------------
int ViENetworkImpl::DeregisterSendTransport(const int videoChannel)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel doesn't exist", __FUNCTION__);
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->Sending())
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s Channel already sending", __FUNCTION__);
        SetLastError(kViENetworkAlreadySending);
        return -1;
    }
    if (ptrViEChannel->DeregisterSendTransport() != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// ReceivedRTPPacket
//
// Function for inserting a RTP packet received by a customer transport
// ----------------------------------------------------------------------------
int ViENetworkImpl::ReceivedRTPPacket(const int videoChannel, const void* data,
                                      const int length)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, data: -, length: %d)", __FUNCTION__,
                 videoChannel, length);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    return ptrViEChannel->ReceivedRTPPacket(data, length);
}

// ----------------------------------------------------------------------------
// ReceivedRTCPPacket
//
// Function for inserting a RTCP packet received by a customer transport
// ----------------------------------------------------------------------------
int ViENetworkImpl::ReceivedRTCPPacket(const int videoChannel,
                                       const void* data, const int length)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, data: -, length: %d)", __FUNCTION__,
                 videoChannel, length);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    return ptrViEChannel->ReceivedRTCPPacket(data, length);
}

// ============================================================================
// Get info
// ============================================================================

// ----------------------------------------------------------------------------
// GetSourceInfo
//
// Retreives informatino about the remote side sockets
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetSourceInfo(const int videoChannel,
                                  unsigned short& rtpPort,
                                  unsigned short& rtcpPort, char* ipAddress,
                                  unsigned int ipAddressLength)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetSourceInfo(rtpPort, rtcpPort, ipAddress,
                                     ipAddressLength) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// GetLocalIP
//
// Gets the local ip address
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetLocalIP(char ipAddress[64], bool ipv6)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s( ipAddress, ipV6: %d)", __FUNCTION__, ipv6);

#ifndef WEBRTC_EXTERNAL_TRANSPORT
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    if (ipAddress == NULL)
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s: No argument", __FUNCTION__);
        SetLastError(kViENetworkInvalidArgument);
        return -1;
    }

    WebRtc_UWord8 numSocketThreads = 1;
    UdpTransport* ptrSocketTransport =
        UdpTransport::Create(
            ViEModuleId(_instanceId,-1),numSocketThreads);

    if (ptrSocketTransport == NULL)
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s: Could not create socket module", __FUNCTION__);
        SetLastError(kViENetworkUnknownError);
        return -1;
    }

    WebRtc_Word8 localIpAddress[64];
    if (ipv6)
    {
        WebRtc_UWord8 localIP[16];
        if (ptrSocketTransport->LocalHostAddressIPV6(localIP) != 0)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: Could not get local IP", __FUNCTION__);
            SetLastError(kViENetworkUnknownError);
            return -1;
        }
        // Convert 128-bit address to character string (a:b:c:d:e:f:g:h)
        sprintf(localIpAddress,
                "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
                localIP[0], localIP[1], localIP[2], localIP[3], localIP[4],
                localIP[5], localIP[6], localIP[7], localIP[8], localIP[9],
                localIP[10], localIP[11], localIP[12], localIP[13],
                localIP[14], localIP[15]);
    }
    else
    {
        WebRtc_UWord32 localIP = 0;
        if (ptrSocketTransport->LocalHostAddress(localIP) != 0)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: Could not get local IP", __FUNCTION__);
            SetLastError(kViENetworkUnknownError);
            return -1;
        }
        // Convert 32-bit address to character string (x.y.z.w)
        sprintf(localIpAddress, "%d.%d.%d.%d", (int) ((localIP >> 24) & 0x0ff),
                (int) ((localIP >> 16) & 0x0ff),
                (int) ((localIP >> 8) & 0x0ff), (int) (localIP & 0x0ff));
    }
    strcpy(ipAddress, localIpAddress);
    UdpTransport::Destroy(
        ptrSocketTransport);
    WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s: local ip = %s", __FUNCTION__, localIpAddress);

    return 0;
#else
    WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideo, ViEId(_instanceId),
        "%s: not available for external transport", __FUNCTION__);

    return -1;
#endif
}

// ============================================================================
// IPv6
// ============================================================================

// ----------------------------------------------------------------------------
// EnableIPv6
//
// Enables IPv6
// ----------------------------------------------------------------------------
int ViENetworkImpl::EnableIPv6(int videoChannel)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->EnableIPv6() != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// IsIPv6Enabled
//
// Returns true if IPv6 is enabled, false otherwise.
// ----------------------------------------------------------------------------
bool ViENetworkImpl::IsIPv6Enabled(int videoChannel)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return false;
    }
    return ptrViEChannel->IsIPv6Enabled();
}

// ============================================================================
// Source IP address and port filter
// ============================================================================

// ----------------------------------------------------------------------------
// SetSourceFilter
//
// Sets filter source port and IP address. Packets from all other sources
// will be discarded.
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetSourceFilter(const int videoChannel,
                                    const unsigned short rtpPort,
                                    const unsigned short rtcpPort,
                                    const char* ipAddress)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, rtpPort: %u, rtcpPort: %u, ipAddress: %s)",
                 __FUNCTION__, videoChannel, rtpPort, rtcpPort, ipAddress);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->SetSourceFilter(rtpPort, rtcpPort, ipAddress) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// GetSourceFilter
//
// Gets vaules set by SetSourceFilter
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetSourceFilter(const int videoChannel,
                                    unsigned short& rtpPort,
                                    unsigned short& rtcpPort, char* ipAddress)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetSourceFilter(rtpPort, rtcpPort, ipAddress) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// ToS
// ============================================================================

// ----------------------------------------------------------------------------
// SetToS
//
// Sets values for ToS
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetSendToS(const int videoChannel, const int DSCP,
                               const bool useSetSockOpt = false)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, DSCP: %d, useSetSockOpt: %d)", __FUNCTION__,
                 videoChannel, DSCP, useSetSockOpt);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }

#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
    WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(_instanceId, videoChannel),
                 "   force useSetSockopt=true since there is no alternative"
                 " implementation");
    if (ptrViEChannel->SetToS(DSCP, true) != 0)
#else
    if (ptrViEChannel->SetToS(DSCP, useSetSockOpt) != 0)
#endif
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// GetToS
//
// Gets values set by SetToS
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetSendToS(const int videoChannel, int& DSCP,
                               bool& useSetSockOpt)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetToS((WebRtc_Word32&) DSCP, useSetSockOpt) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// GQoS
// ============================================================================

// ----------------------------------------------------------------------------
// SetSendGQoS
//
// Sets settings for GQoS. Must be called after SetSendCodec to get correct
// bitrate setting.
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetSendGQoS(const int videoChannel, const bool enable,
                                const int serviceType, const int overrideDSCP)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, enable: %d, serviceType: %d, "
                 "overrideDSCP: %d)",
                 __FUNCTION__, videoChannel, enable, serviceType, overrideDSCP);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

#if (defined(_WIN32) || defined(_WIN64))
    // Sanity check. We might crash if testing and relying on an OS socket error
    if (enable
        && (serviceType != SERVICETYPE_BESTEFFORT)
        && (serviceType != SERVICETYPE_CONTROLLEDLOAD)
        && (serviceType != SERVICETYPE_GUARANTEED) &&
        (serviceType != SERVICETYPE_QUALITATIVE))
    {
        WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s: service type %d not supported", __FUNCTION__,
                     videoChannel, serviceType);
        SetLastError(kViENetworkServiceTypeNotSupported);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    ViEEncoder* ptrVieEncoder = cs.Encoder(videoChannel);
    if (ptrVieEncoder == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    VideoCodec videoCodec;
    if (ptrVieEncoder->GetEncoder(videoCodec) != 0)
    {
        // Could not get
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel),
                     "%s: Could not get max bitrate for the channel",
                     __FUNCTION__);
        SetLastError(kViENetworkSendCodecNotSet);
        return -1;
    }
    if(ptrViEChannel->SetSendGQoS(enable, serviceType, videoCodec.maxBitrate,
                                  overrideDSCP)!=0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;

#else
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s: Not supported",
                 __FUNCTION__);
    SetLastError(kViENetworkNotSupported);
    return -1;
#endif
}

// ----------------------------------------------------------------------------
// GetSendGQoS
//
// Gets the settigns set by SetSendGQoS
// ----------------------------------------------------------------------------
int ViENetworkImpl::GetSendGQoS(const int videoChannel, bool& enabled,
                                int& serviceType, int& overrideDSCP)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->GetSendGQoS(enabled, (WebRtc_Word32&) serviceType,
                                   (WebRtc_Word32&) overrideDSCP) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;

}

// ============================================================================
// Network settings
// ============================================================================

// ----------------------------------------------------------------------------
// SetMTU
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetMTU(int videoChannel, unsigned int mtu)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d, mtu: %u)",
                 __FUNCTION__, videoChannel, mtu);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->SetMTU(mtu) != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;

}

// ============================================================================
// Packet timout notification
// ============================================================================

// ----------------------------------------------------------------------------
// SetPacketTimeoutNotification
//
// Sets the time for packet timout notifications
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetPacketTimeoutNotification(const int videoChannel,
                                                 bool enable,
                                                 int timeoutSeconds)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, enable: %d, timeoutSeconds: %u)",
                 __FUNCTION__, videoChannel, enable, timeoutSeconds);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->SetPacketTimeoutNotification(enable, timeoutSeconds)
        != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// Periodic dead-or-alive reports
// ============================================================================

// ----------------------------------------------------------------------------
// RegisterObserver
// ----------------------------------------------------------------------------
int ViENetworkImpl::RegisterObserver(const int videoChannel,
                                     ViENetworkObserver& observer)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->RegisterNetworkObserver(&observer) != 0)
    {
        SetLastError(kViENetworkObserverAlreadyRegistered);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// DeregisterObserver
// ----------------------------------------------------------------------------
int ViENetworkImpl::DeregisterObserver(const int videoChannel)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel), "%s(channel: %d)",
                 __FUNCTION__, videoChannel);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (!ptrViEChannel->NetworkObserverRegistered())
    {
        SetLastError(kViENetworkObserverNotRegistered);
        return -1;
    }
    return ptrViEChannel->RegisterNetworkObserver(NULL);
}

// ----------------------------------------------------------------------------
// SetPeriodicDeadOrAliveStatus
//
// Enables/disables the dead-or-alive callback
// ----------------------------------------------------------------------------
int ViENetworkImpl::SetPeriodicDeadOrAliveStatus(const int videoChannel,
                                                 bool enable,
                                                 unsigned int sampleTimeSeconds)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, enable: %d, sampleTimeSeconds: %ul)",
                 __FUNCTION__, videoChannel, enable, sampleTimeSeconds);

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (!ptrViEChannel->NetworkObserverRegistered())
    {
        SetLastError(kViENetworkObserverNotRegistered);
        return -1;
    }

    if (ptrViEChannel->SetPeriodicDeadOrAliveStatus(enable, sampleTimeSeconds)
        != 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// Send packet using the User Datagram Protocol (UDP)
// ============================================================================

// ----------------------------------------------------------------------------
// SendUDPPacket
//
// Send an extra UDP packet
// ----------------------------------------------------------------------------
int ViENetworkImpl::SendUDPPacket(const int videoChannel, const void* data,
                                  const unsigned int length,
                                  int& transmittedBytes,
                                  bool useRtcpSocket = false)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, videoChannel),
                 "%s(channel: %d, data: -, length: %d, transmitterBytes: -, "
                 "useRtcpSocket: %d)", __FUNCTION__, videoChannel, length,
                 useRtcpSocket);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    // Get the channel
    ViEChannelManagerScoped cs(_channelManager);
    ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
    if (ptrViEChannel == NULL)
    {
        // The channel doesn't exists
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, videoChannel), "Channel doesn't exist");
        SetLastError(kViENetworkInvalidChannelId);
        return -1;
    }
    if (ptrViEChannel->SendUDPPacket((const WebRtc_Word8*) data, length,
                                     (WebRtc_Word32&) transmittedBytes,
                                     useRtcpSocket) < 0)
    {
        SetLastError(kViENetworkUnknownError);
        return -1;
    }
    return 0;
}
} // namespace webrtc
