Getting and decoding video by RTP H264

132 Views Asked by At

Parsing RTP H264. WINAPI C. A queue from C++ has been applied, I repent.

RTP is generated using FFMPEG with the following command: ffmpeg.exe -f gdigrab -framerate 25 -i desktop -s 853x480 -b:v 120000 -c:v libx264 -f rtp rtp://127.0.0.1:8080

Parsing the incoming RTP/h264 stream and converting it to an RGB matrix.

video_H264_decode.h

#pragma once
#ifndef _VIDEO_H264_DECODE_SEND_H // Блокируем повторное включение этого модуля
#define _VIDEO_H264_DECODE_SEND_H
//******************************************************************************
// Section include
//******************************************************************************
#include "main.h"
#include <Windows.h>
//******************************************************************************
// Constants
//******************************************************************************

//******************************************************************************
// Type
//******************************************************************************
typedef struct {
    unsigned char* data;
    int size;
}RTPData_DType;

typedef struct 
{
    union
    {
        struct
        {
            char V:2;               //Версия
            char P:1;               //заполнение
            char X:1;               //расширение
            char CC:4;              //количество CSRC

            char M:1;               //маркер (флаг последнего пакета AU),
            char PT:7;              //полезная нагрузка (тип данных носителя полезной нагрузки RTP, H264 = 96)

            short sequence_number;  //Порядковый номер: порядковый номер пакета RTP, увеличенный на 1.
            int time_stamp;         //временная метка выборки медиа. 
            int SSRC;               //Пакет данных имеет одинаковое происхождение.
        };
        unsigned char data[12];
    };
}RTPHeader_DType;
//******************************************************************************
// Global var
//******************************************************************************

//******************************************************************************
// Local function prototype
//******************************************************************************
UCHAR rtp_H264_recive_init(void);
UCHAR RTPStop(void);
//******************************************************************************
// Macros
//******************************************************************************
#define BYTE2_SWAP(X)   ((((short)(X) & 0xff00) >> 8) |(((short)(X) & 0x00ff) << 8))
#endif
//******************************************************************************
// ENF OF FILE
//******************************************************************************

video_H264_decode.c

//******************************************************************************
//include
//******************************************************************************
#include "main.h"
/*#include "video_H264_decode.h"
#include <Windows.h>
#include <strsafe.h>
#include <chrono>
#include <stdlib.h>

#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>*/
#include <iostream>
#include <queue>

extern "C" {
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libpostproc/postprocess.h"
#include "libavcodec/avcodec.h"
}

#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avdevice.lib")
#pragma comment(lib,"avfilter.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"postproc.lib")
#pragma comment(lib,"swresample.lib")
#pragma comment(lib,"swscale.lib")

#pragma warning(disable: 4996)
//******************************************************************************
// Section for determining the variables used in the module
//******************************************************************************
//------------------------------------------------------------------------------
// Global
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Local
//------------------------------------------------------------------------------
const int inteval = 0x01000000;
BOOL FlagRTPActive = TRUE;

HANDLE hMutexRTPRecive;
HANDLE hSemaphoreRTP;
HANDLE hTreadRTPRecive;
HANDLE hTreadRTPDecode;


SOCKET RTPSocket; //socket UDP RTP
RTPData_DType packet;
RTPData_DType FU_buffer = { 0 };

std::queue<RTPData_DType> q;
std::set<int> seq;

AVFormatContext* pAVFormatContext;
AVCodecContext* pAVCodecContext;
const AVCodec* pAVCodec;
AVFrame* pAVFrame;
AVFrame* AVFrameRGG;
SwsContext* pSwsContext;
AVPacket *pAVPacket;
AVCodecParserContext* pAVCodecParserContext;

UINT port;
//******************************************************************************
// Section of prototypes of local functions
//******************************************************************************
DWORD WINAPI rtp_H264_recive_Procedure(CONST LPVOID lpParam);
DWORD WINAPI rtp_decode_Procedure(CONST LPVOID lpParam);
char RTPSocketInit(void);
void RTPPacketParser(void);
char RTPSocketRecive(void);
void Decode_NaluToAVFrameRGG();
//******************************************************************************
// Section of the description of functions
//******************************************************************************
UCHAR rtp_H264_recive_init(void)
{
    hSemaphoreRTP = CreateSemaphore(
        NULL,           // default security attributes
        0,              // initial count
        1,              // maximum count
        NULL);          // unnamed semaphore

    hMutexRTPRecive = CreateMutex(
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex

    hTreadRTPRecive = CreateThread(NULL, NULL, rtp_H264_recive_Procedure, NULL, NULL, NULL);
    hTreadRTPDecode = CreateThread(NULL, NULL, rtp_decode_Procedure, NULL, NULL, NULL);

    return 0;
}
//------------------------------------------------------------------------------
UCHAR RTPStop(void)
{
    FlagRTPActive = FALSE;

    if (hSemaphoreRTP) CloseHandle(hSemaphoreRTP);
    if (hMutexRTPRecive) CloseHandle(hMutexRTPRecive);
    if (hTreadRTPRecive) CloseHandle(hTreadRTPRecive);

    closesocket(RTPSocket);  

    return 0;
}
//------------------------------------------------------------------------------
DWORD WINAPI rtp_H264_recive_Procedure(CONST LPVOID lpParam)
{
    while (RTPSocketInit() == 0)
        Sleep(2000);
       
    while (1)
    {
        RTPSocketRecive();
        RTPPacketParser();
        ReleaseSemaphore(hSemaphoreRTP, 1, NULL);
    }
}
//------------------------------------------------------------------------------
DWORD WINAPI rtp_decode_Procedure(CONST LPVOID lpParam)
{
    port = param.Option.VideoPort;

    pAVPacket = av_packet_alloc();
    if (!pAVPacket)
    {
        MessageBox(NULL, L"ERROR Could not allocate pAVPacket", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }
    av_init_packet(pAVPacket);

    /* find the MPEG-1 video decoder */
    pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!pAVCodec)
    {
        MessageBox(NULL, L"ERROR Codec not found", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    pAVCodecParserContext = av_parser_init(pAVCodec->id);
    if (!pAVCodecParserContext)
    {
        MessageBox(NULL, L"ERROR Parser not found", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    pAVCodecContext = avcodec_alloc_context3(pAVCodec);
    if (!pAVCodecContext) 
    {
        MessageBox(NULL, L"ERROR Could not allocate video codec context", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }
               
    if (avcodec_open2(pAVCodecContext, pAVCodec, NULL) < 0)
    {
        MessageBox(NULL, L"ERROR Could not open codec", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    pAVFrame = av_frame_alloc();
    if (!pAVFrame) 
    {
        MessageBox(NULL, L"ERROR Could not allocate video frame", L"Init decoder error", MB_OK | MB_ICONERROR);
        exit(1);
    }    
    
    while (FlagRTPActive)
    {
        if(port != param.Option.VideoPort)
            closesocket(RTPSocket);

        WaitForSingleObject(hSemaphoreRTP, 500);        
        Decode_NaluToAVFrameRGG();
    }

    avformat_free_context(pAVFormatContext);
    av_frame_free(&pAVFrame);
    avcodec_close(pAVCodecContext);
    av_packet_free(&pAVPacket);

    
    if (hTreadRTPDecode) CloseHandle(hTreadRTPDecode);

    return 0;
}

//------------------------------------------------------------------------------
char RTPSocketInit(void)
{    
    sockaddr_in RTPSocketAddr;
    
    RTPSocketAddr.sin_family = AF_INET;
    RTPSocketAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    RTPSocketAddr.sin_port = htons(param.Option.VideoPort);

    RTPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (RTPSocket == INVALID_SOCKET)
    {
        MessageBox(NULL, L"ERROR Invalid RTP Socket", L"UDP Socket", MB_OK | MB_ICONERROR);
        return 0;
    }

    int option = 12000;
    if (setsockopt(RTPSocket, SOL_SOCKET, SO_RCVBUF, (char*)&option, sizeof(option)) < 0)
    {
        printf("setsockopt failed\n");

    }

    /*option = TRUE;
    if (setsockopt(RTPSocket, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char*)&option, sizeof(option)) < 0)
    {
        printf("setsockopt failed\n");

    }*/

    if (bind(RTPSocket, (sockaddr*)&RTPSocketAddr, sizeof(RTPSocketAddr)) == SOCKET_ERROR)
    {
        MessageBox(NULL, L"ERROR bind", L"UDP Socket", MB_OK | MB_ICONERROR);
        closesocket(RTPSocket);
        return 0;
    }
    return 1;
}

//------------------------------------------------------------------------------
char RTPSocketRecive(void)
{
    static char RecvBuf[2000];
    static int BufLen = 2000;

    int iResult = 0;
    struct sockaddr_in SenderAddr;
    int SenderAddrSize = sizeof(SenderAddr);
    TCHAR szErrorMsg[100];

    iResult = recvfrom(RTPSocket, RecvBuf, BufLen, 0, NULL, NULL);
    if (iResult == SOCKET_ERROR && FlagRTPActive == TRUE)
    {
        StringCchPrintf(szErrorMsg, 100, L"ERROR recvfrom Ошибка:%d", WSAGetLastError());
        MessageBox(NULL, szErrorMsg, L"UDP Socket", MB_OK | MB_ICONERROR);
        return -1;
    }

    packet.data = (unsigned char*)RecvBuf;
    packet.size = iResult;

    return 1;
}

//------------------------------------------------------------------------------
void RTPPacketParser(void)
{
    RTPHeader_DType RTPHeader;
    RTPData_DType NALU;
    unsigned char* buffer = packet.data;
    int pos = 0;
    static int count = 0;
    static short lastSequenceNumber = 0xFFFF;
    short type;
    char payload_header;

    //read rtp header
    memcpy(&RTPHeader, buffer, sizeof(RTPHeader));
    RTPHeader.sequence_number = BYTE2_SWAP(RTPHeader.sequence_number);
    pos += 12;    
  
    if (RTPHeader.X) {
        //profile extension
        short define;
        short length;
        length = buffer[pos + 3];//suppose not so long extension
        pos += 4;
        pos += (length * 4);
    }

    payload_header = buffer[pos];
    type = payload_header & 0x1f; //Тип полезной нагрузки RTP
    pos++;
    
    //STAP-A
    if (type == 24)
    {        
        while (pos < packet.size)
        {
            unsigned short NALU_size;
            memcpy(&NALU_size, buffer + pos, 2);
            NALU_size = BYTE2_SWAP(NALU_size);
            pos += 2;
            char NAL_header = buffer[pos];
            short NAL_type = NAL_header & 0x1f;

            if (NAL_type == 7) 
            {
                count++;
                //cout<<"SPS, sequence number: "<<seq_num<<endl;
            }
            else if (NAL_type == 8) 
            {
                //cout<<"PPS, sequence number: "<<seq_num<<endl;
            }
            else if (NAL_type == 10) 
            {
                //cout<<"end of sequence, sequence number: "<<seq_num<<endl;
            }

            if (count > 0) 
            {
                NALU.data = (unsigned char*) malloc(NALU_size + 4);
                NALU.size = NALU_size + 4;
                memcpy(NALU.data, &inteval, 4);
                memcpy(NALU.data + 4, &buffer[pos], NALU_size);

                WaitForSingleObject(hMutexRTPRecive, INFINITE);
                q.push(NALU);
                ReleaseMutex(hMutexRTPRecive);
            }

            pos += NALU_size;
        }
    }
    //FU-A Fragmentation unit
    else if (type == 28)
    {        
        //FU header
        char FU_header = buffer[pos];
        bool fStart = FU_header & 0x80;
        bool fEnd = FU_header & 0x40;

        //NAL header
        char NAL_header = (payload_header & 0xe0) | (FU_header & 0x1f);
        short NAL_type = FU_header & 0x1f;
        if (NAL_type == 7) 
        {
            count++;
            //SPS
        }
        else if (NAL_type == 8) 
        {
            //PPS
        }
        else if (NAL_type == 10) 
        {
            //end of sequence
        }
        pos++;

        int size = packet.size - pos;
        
        if (count > 0)
        {
            if (fStart) 
            {
                if (FU_buffer.size != 0)
                {
                    free(FU_buffer.data);
                    FU_buffer.size = 0;
                }
                FU_buffer.data = (unsigned char*)malloc(size + 5);
                if (FU_buffer.data == NULL)
                    return;
                FU_buffer.size = size + 5;
                memcpy(FU_buffer.data, &inteval, 4);
                memcpy(FU_buffer.data + 4, &NAL_header, 1);
                memcpy(FU_buffer.data + 5, buffer + pos, size);
            }
            else
            {
                unsigned char* temp = (unsigned char*)malloc(FU_buffer.size + size);
                memcpy(temp, FU_buffer.data, FU_buffer.size);
                memcpy(temp + FU_buffer.size, buffer + pos, size);
                if (FU_buffer.size != 0) free(FU_buffer.data);
                FU_buffer.data = temp;
                FU_buffer.size += size;
            }

            if (fEnd)
            {
                NALU.data = (unsigned char*)malloc(FU_buffer.size);
                NALU.size = FU_buffer.size;
                memcpy(NALU.data, FU_buffer.data, FU_buffer.size);

                WaitForSingleObject(hMutexRTPRecive, INFINITE);
                q.push(NALU);
                ReleaseMutex(hMutexRTPRecive);

                free(FU_buffer.data);
                FU_buffer.size = 0;
            }
        }
        
    }
    else 
    {
        //other type
        short NAL_type = type;
        if (NAL_type == 7) 
        {
            count++;
            //SPS
        }
        else if (NAL_type == 8) 
        {
            //PPS
        }
        else if (NAL_type == 10) 
        {
            //end of sequence
        }

        int size = packet.size - pos + 1;
        if (count > 0)
        {
            NALU.data = (unsigned char*)malloc(size+4);
            NALU.size = size + 4;
            memcpy(NALU.data, &inteval, 4);
            memcpy(NALU.data + 4, &buffer[12], size);

            WaitForSingleObject(hMutexRTPRecive, INFINITE);
            q.push(NALU);
            ReleaseMutex(hMutexRTPRecive);
        }
    }
}

//------------------------------------------------------------------------------
void Decode_NaluToAVFrameRGG()
{
    unsigned char cntNALU;
    int len = 0;
    static int size = 0;
    unsigned char* data = NULL;
    int ret;
    RTPData_DType NALU;

    av_frame_unref(pAVFrame);
    av_packet_unref(pAVPacket);

    while(1)
    {
        WaitForSingleObject(hMutexRTPRecive, INFINITE);
        if (q.empty())
        {
            ReleaseMutex(hMutexRTPRecive);
            break;
        }
        NALU = q.front();
        q.pop();
        ReleaseMutex(hMutexRTPRecive);

        data = NALU.data;
        size = NALU.size;

        while(size)
        {            
            len = av_parser_parse2(pAVCodecParserContext, pAVCodecContext, &pAVPacket->data, &pAVPacket->size,
                (uint8_t*)data, size,
                AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);

            data = len ? data + len : data;
            size -= len;
            
            if (pAVPacket->size)
            {
                ret = avcodec_send_packet(pAVCodecContext, pAVPacket);

                if(ret == AVERROR_EOF)
                    MessageBox(NULL, L"the codec has been fully flushed, and there will be no more output frames", L"avcodec_send_packet", MB_OK | MB_ICONERROR);

                if (ret < 0)
                {
                    MessageBox(NULL, L"ERROR sending a packet for decoding", L"Decode", MB_OK | MB_ICONERROR);
                    //exit(1);
                }

                while (ret >= 0) 
                {
                    ret = avcodec_receive_frame(pAVCodecContext, pAVFrame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        break;
                    else if (ret < 0) {
                        MessageBox(NULL, L"ERROR during decoding", L"Decode", MB_OK | MB_ICONERROR);
                        //exit(1);
                    }
                    
                    AVFrameRGG = av_frame_alloc();                                       
                    pSwsContext = sws_getContext(pAVCodecContext->width, pAVCodecContext->height, pAVCodecContext->pix_fmt,
                        pAVCodecContext->width, pAVCodecContext->height, AV_PIX_FMT_RGB24, SWS_SPLINE,
                        0, 0, 0);

                    sws_scale_frame(pSwsContext, AVFrameRGG, pAVFrame);
                                        
                    ImgBufferSet(AVFrameRGG->data[0], pAVCodecContext->height, pAVCodecContext->width, AVFrameRGG->linesize[0]);

                    sws_freeContext(pSwsContext);
                    av_frame_free(&AVFrameRGG);
                }
            }
        }
        free(NALU.data);
        NALU.size = 0;
    }    
}

It's DECIDED

0

There are 0 best solutions below