Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————H264压缩视频

Posted rootming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————H264压缩视频相关的知识,希望对你有一定的参考价值。

H264视频压缩主要步骤

  1. 压缩前的一些初始化
  2. 压缩帧再写入文件
  3. 压缩完成后资源的一些清理
/* encode.c */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <x264.h>
#include <pthread.h>
#include "config.h"
#include "encode.h"
#include "queue.h"


#define CONFIG_CSP X264_CSP_I422

typedef enum {
    ENCODE_STATE_STOP,
    ENCODE_STATE_START
} eEncodeState;

struct {
    x264_nal_t* pNals;
    x264_t* pHandle;
    x264_picture_t* pPicIn;
    x264_param_t* pParam;
    eEncodeState State;
    pthread_t EncodeThreadId;
    FILE* pFile;
    sQueue QueuePrivateData;
} sEncodePrivateData;


static int H264FrameEncodeBegin(uint32_t Width, uint32_t Height)
{
    QueueInit(&sEncodePrivateData.QueuePrivateData);
    sEncodePrivateData.pPicIn = (x264_picture_t*)malloc(sizeof(x264_picture_t));
    sEncodePrivateData.pParam = (x264_param_t*)malloc(sizeof(x264_param_t));

    x264_param_default(sEncodePrivateData.pParam);
    x264_param_default_preset(sEncodePrivateData.pParam, "fast" , "zerolatency" );

    /* 设置参数 */
    sEncodePrivateData.pParam->i_csp = CONFIG_CSP;
    sEncodePrivateData.pParam->i_width = Width;
    sEncodePrivateData.pParam->i_height = Height; 
    sEncodePrivateData.pParam->i_fps_num = CONFIG_CAPTURE_FPS;
    sEncodePrivateData.pParam->i_fps_den = 1;

    sEncodePrivateData.pParam->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;
    sEncodePrivateData.pParam->i_keyint_max = 10;

    sEncodePrivateData.pParam->rc.i_bitrate = CONFIG_ENCODE_BITRATE;
    sEncodePrivateData.pParam->rc.i_rc_method = X264_RC_ABR;

    x264_param_apply_profile(sEncodePrivateData.pParam, "high422");

    sEncodePrivateData.pHandle = x264_encoder_open(sEncodePrivateData.pParam);

    x264_picture_alloc(sEncodePrivateData.pPicIn, CONFIG_CSP, 
        sEncodePrivateData.pParam->i_width, sEncodePrivateData.pParam->i_height);

    return 0;
}

static int H264FrameEncodeEnd(void)
{
    printf("Encode end, now clear resource\n");
    if (sEncodePrivateData.pHandle) {
        x264_encoder_close(sEncodePrivateData.pHandle);
    }

    if (sEncodePrivateData.pPicIn) {
        x264_picture_clean(sEncodePrivateData.pPicIn);
        free(sEncodePrivateData.pPicIn);
        sEncodePrivateData.pPicIn = NULL;
    }

    if (sEncodePrivateData.pParam) {
        free(sEncodePrivateData.pParam);
        sEncodePrivateData.pParam = NULL;
    }
    if(sEncodePrivateData.pFile) {
        fclose(sEncodePrivateData.pFile);
        sEncodePrivateData.pFile = NULL;
    }

    return 0;
}

static int H264FrameEncode(const uint8_t* pData, uint32_t Width, uint32_t Height, uint32_t Length)
{
    x264_picture_t* pPicOut = (x264_picture_t*)malloc(sizeof(x264_picture_t));
    if(!pPicOut) {
        perror("Malloc failed");
        return -1;
    }
    x264_picture_init(pPicOut);

    int IndexY, IndexU, IndexV;
    int YuyvLength = Width * Height * 2 - 4;
    int NalCnt = 0;
    int Result = 0;
    static long int pts = 0;
    // uint8_t* pOut = pDataOut;
    uint8_t* pY = sEncodePrivateData.pPicIn->img.plane[0];
    uint8_t* pU = sEncodePrivateData.pPicIn->img.plane[1];
    uint8_t* pV = sEncodePrivateData.pPicIn->img.plane[2];

    // if(YuyvLength != Length) {
    //  printf("Param invalid, Length not match\n");
    //  return -1;
    // }

    IndexY = 0;
    IndexU = 0;
    IndexV = 0;

    for(int i = 0; i < YuyvLength; i += 4) {
            *(pY + (IndexY++)) = *(pData + i);
            *(pU + (IndexU++)) = *(pData + i + 1);
            *(pY + (IndexY++)) = *(pData + i + 2);
            *(pV + (IndexV++)) = *(pData + i + 3);
    }

    sEncodePrivateData.pPicIn->i_type = X264_TYPE_AUTO;

    sEncodePrivateData.pPicIn->i_pts = pts++;

    if (x264_encoder_encode(sEncodePrivateData.pHandle, &(sEncodePrivateData.pNals), &NalCnt, sEncodePrivateData.pPicIn, pPicOut) < 0) {
        free(pPicOut);
        return -1;
    }
    for (int i = 0; i < NalCnt; i++) {
        // memcpy(pOut, sEncodePrivateData.pNals[i].p_payload, sEncodePrivateData.pNals[i].i_payload);
        // pOut += sEncodePrivateData.pNals[i].i_payload;
        fwrite(sEncodePrivateData.pNals[i].p_payload, 1, sEncodePrivateData.pNals[i].i_payload, sEncodePrivateData.pFile);
        Result += sEncodePrivateData.pNals[i].i_payload;
    }

    free(pPicOut);

    return Result;
}

static void* H264EncodeThread(void* pParam)
{
    int Ret = -1;
    while(sEncodePrivateData.State == ENCODE_STATE_START) {
        sQueueData QueueData;
        Ret = QueuePopData(&sEncodePrivateData.QueuePrivateData, &QueueData);
        if(Ret) {
            continue;
        }
        H264FrameEncode(QueueData.pData, 
                CONFIG_CAPTURE_WIDTH, CONFIG_CAPTURE_HEIGHT, 
                QueueData.Length);
        QueueCallback(&QueueData);
    }
    H264FrameEncodeEnd();
    return NULL;
}

int EncodeStart(const char* pFilename)
{
    H264FrameEncodeBegin(CONFIG_CAPTURE_WIDTH, CONFIG_CAPTURE_HEIGHT);

    if(sEncodePrivateData.State) {
        printf("Encode thread already start\n");
        return -1;
    }
    sEncodePrivateData.State = ENCODE_STATE_START;

    sEncodePrivateData.pFile = fopen(pFilename, "w+");
    if(!sEncodePrivateData.pFile) {
        perror("Create h264 file failed");
        return -1;
    }

    pthread_create(&sEncodePrivateData.EncodeThreadId, NULL, H264EncodeThread, (void *)NULL);

    return 0;
}

int EncodeStop(void)
{
    sEncodePrivateData.State = ENCODE_STATE_STOP;
    pthread_join(sEncodePrivateData.EncodeThreadId, NULL);
    printf("Encode thread stop\n");
    return 0;
}
/* encode.h */
#ifndef ENCODE_H
#define ENCODE_H

int EncodeStart(const char* pFilename);

int EncodeStop(void);

#endif

以上是关于Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————H264压缩视频的主要内容,如果未能解决你的问题,请参考以下文章

视频压缩格式H.264中时间冗余与运动补偿问题

如何使用 c++ 使用 v4l2 API 连续捕获 h264 视频?

音视频入门——H.264编码(宏块+片+帧)浅析

H 264简单介绍

Android 音视频01 --- H264的基本原理01

H.264/AVC视频编解码技术详解二十五帧间预测编码:参考帧列表