OpenCV采集的视频流转化成H264格式裸码流

  本文通过OpenCV库采集摄像头视频,随后通过libx264库把帧转化成264格式的码流。

头文件:

#ifndef _X264_ENCODER_H
#define _X264_ENCODER_H

#include <stdint.h>
#include "x264.h"
#include "opencv/cv.h"
#include "opencv/highgui.h"

struct x264_encoder{
    x264_param_t    param;
    char            preset[20];
    char            tune[20];
    char            profile[20];
    x264_t*            h;
    x264_picture_t    pic_in;
    x264_picture_t    pic_out;
    long            colorspace;
    x264_nal_t*        nal;
    int             iframe;
    int             iframe_size;
    int                inal;
};

class x264Encoder
{
public:

    x264Encoder();

    x264Encoder(int videoWidth, int videoHeight, int channel, int fps);

    ~x264Encoder();

    /** 创建X264编码器
     * @param[in] videoWidth  视频宽度
     * @param[in] videoHeight 视频高度
     * @param[in] fps 帧率
     * @return 成功返回true, 失败返回false.
     */
    bool Create(int videoWidth, int videoHeight, int channel = 3, int fps = 30);

    /** 编码一帧
     * @param[in] frame 输入的一帧图像
     * @return 返回编码后数据尺寸, 0表示编码失败
     */
    int EncodeOneFrame(const cv::Mat& frame);

    /** 获取编码后的帧数据
     * 说明: EncodeOneFrame 后调用
     * @return 返回裸x264数据
     */
    uchar* GetEncodedFrame() const;

    /** 销毁X264编码器
     */
    void Destory();

    // 编码器是否可用
    bool IsValid() const;

private:

    void Init();

public:
    int m_width;
    int m_height;
    int m_channel;
    int m_fps;

protected:

    int m_widthstep;
    int m_lumaSize;
    int m_chromaSize;

    x264_encoder*  m_encoder;
};

#endif

x264_encoder.h

源文件:

#include "x264_encoder.h"
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "opencv2/imgproc.hpp"

#define ENCODER_TUNE   "zerolatency"
#define ENCODER_PROFILE  "baseline"
#define ENCODER_PRESET "veryfast"
#define ENCODER_COLORSPACE X264_CSP_I420
#define CLEAR(x) (memset((&x),0,sizeof(x)))

x264Encoder::x264Encoder()
{
    Init();
}

x264Encoder::x264Encoder(int videoWidth, int videoHeight, int channel, int fps)
{
    Init();
    Create(videoWidth, videoHeight, channel, fps);
}

x264Encoder::~x264Encoder()
{
    Destory();
}

void x264Encoder::Init()
{
    m_width = 0;
    m_height = 0;
    m_channel = 0;
    m_widthstep = 0;
    m_fps = 30;
    m_lumaSize = 0;
    m_chromaSize = 0;
    m_encoder = NULL;
}

bool x264Encoder::Create(int videoWidth, int videoHeight, int channel, int fps)
{
    int ret;
    int imgSize;

    if (videoWidth <= 0 || videoHeight <= 0 || channel < 0 || fps <= 0){
        printf("wrong input param\n");
        return false;
    }
    m_width = videoWidth;
    m_height = videoHeight;
    m_channel = channel;
    m_fps = fps;
    m_widthstep = videoWidth * channel;
    m_lumaSize = m_width * m_height;
    m_chromaSize = m_lumaSize / 4;
    imgSize = m_lumaSize * channel;

    m_encoder = (x264_encoder *)malloc(sizeof(x264_encoder));
    if (!m_encoder){
        printf("cannot malloc x264_encoder !\n");
        return false;
    }
    CLEAR(*m_encoder);
    m_encoder->iframe = 0;
    m_encoder->iframe_size = 0;

    strcpy(m_encoder->preset, ENCODER_PRESET);
    strcpy(m_encoder->tune, ENCODER_TUNE);

    /*初始化编码器*/
    CLEAR(m_encoder->param);
    x264_param_default(&m_encoder->param);

    ret = x264_param_default_preset(&m_encoder->param, m_encoder->preset, m_encoder->tune);
    if (ret < 0){
        printf("x264_param_default_preset error!\n");
        return false;
    }

    /*cpuFlags 去空缓冲区继续使用不死锁保证*/
    m_encoder->param.i_threads = X264_SYNC_LOOKAHEAD_AUTO;
    /*视频选项*/
    m_encoder->param.i_csp = X264_CSP_I420;
    m_encoder->param.i_width = m_width;    // 要编码的图像的宽度
    m_encoder->param.i_height = m_height;    // 要编码的图像的高度
    m_encoder->param.i_frame_total = 0;    // 要编码的总帧数,不知道用0
    m_encoder->param.i_keyint_max = 10*fps;// 关键帧间隔
    /*流参数*/
    m_encoder->param.i_bframe = 5;
    m_encoder->param.b_open_gop = 0;
    m_encoder->param.i_bframe_pyramid = 0;
    m_encoder->param.i_bframe_adaptive = X264_B_ADAPT_TRELLIS;

    /*log参数,不需要打印编码信息时直接注释掉*/
    m_encoder->param.i_log_level = X264_LOG_NONE;

    m_encoder->param.i_fps_num = fps;//码率分子
    m_encoder->param.i_fps_den = 1;    //码率分母

    m_encoder->param.b_intra_refresh = 1;
    m_encoder->param.b_annexb = 1;
    m_encoder->param.rc.f_rf_constant = 24;
    m_encoder->param.rc.i_rc_method = X264_RC_CRF;
    /////////////////////////////////////////////////////////////////////////////////////////////////////

    strcpy(m_encoder->profile, ENCODER_PROFILE);
    ret = x264_param_apply_profile(&m_encoder->param, m_encoder->profile);
    if (ret < 0){
        printf("x264_param_apply_profile error!\n");
        return false;
    }
    /*打开编码器*/
    m_encoder->h = x264_encoder_open(&m_encoder->param);
    m_encoder->colorspace = ENCODER_COLORSPACE;

    /*初始化pic*/
    ret = x264_picture_alloc(&m_encoder->pic_in, m_encoder->colorspace, m_width, m_height);
    if ( ret < 0 ){
        printf("x264_picture_alloc error! ret=%d\n", ret);
        return false;
    }

    m_encoder->pic_in.img.i_csp = m_encoder->colorspace;
    m_encoder->pic_in.img.i_plane = 3;
    m_encoder->pic_in.i_type = X264_TYPE_AUTO;

    m_encoder->inal = 0;
    m_encoder->nal = (x264_nal_t *)calloc(2, sizeof(x264_nal_t));
    if (!m_encoder->nal){
        printf("malloc x264_nal_t error!\n");
        return false;
    }
    CLEAR(*(m_encoder->nal));

    return true;
}

int x264Encoder::EncodeOneFrame(const cv::Mat& frame)
{
    if (frame.empty()){
        return 0;
    }
    cv::Mat bgr(frame), yuv;

    if(1 == frame.channels()){
        cv::cvtColor(frame, bgr, CV_GRAY2BGR);
    }
    cv::cvtColor(bgr, yuv, CV_BGR2YUV_I420);

    memcpy(m_encoder->pic_in.img.plane[0], yuv.data, m_lumaSize);
    memcpy(m_encoder->pic_in.img.plane[1], yuv.data + m_lumaSize, m_chromaSize);
    memcpy(m_encoder->pic_in.img.plane[2], yuv.data + m_lumaSize + m_chromaSize, m_chromaSize);
    m_encoder->pic_in.i_pts = m_encoder->iframe ++;

    m_encoder->iframe_size = x264_encoder_encode(m_encoder->h, &m_encoder->nal, &m_encoder->inal, &m_encoder->pic_in, &m_encoder->pic_out);

    return m_encoder->iframe_size;
}

uchar* x264Encoder::GetEncodedFrame() const
{
    return m_encoder->nal->p_payload;
}

void x264Encoder::Destory()
{
    if (m_encoder){
        if (m_encoder->h){
            x264_encoder_close(m_encoder->h);
            m_encoder->h = NULL;
        }
        free(m_encoder);
        m_encoder = NULL;
    }
}

bool x264Encoder::IsValid() const
{
    return ((m_encoder != NULL) && (m_encoder->h != NULL));
}

x264_encoder.cpp

时间: 2025-01-02 14:04:32

OpenCV采集的视频流转化成H264格式裸码流的相关文章

C# 播放H264裸码流

要播放H264裸码流,可以分拆为以下三个工作: 1.解码H264裸码流获取YUV数据 2.将YUV数据转换为RGB数据填充图片 3.将获取的图片进行显示 要完成工作1,我们可以直接使用海思的解码库,由于海思的解码库是C++的动态库,要完成在C#中进行调用可以参考海思h264解码库这篇文章,介绍的很详细.但是对于该博文只介绍了一种帧解码的方法,并没有介绍真正实用的流式解码方法.自己根据解码库的参考文档写了一份C#版的流式解码算法. //初始化 // 这是解码器输出图像信息 hiH264_DEC_F

FFmpeg 获取h264裸码流

有时候我们需要获取h264裸码流进行分析.本文介绍如何通过FFmpeg 获取h264 码流.获取到的h264码流文件 可以直接通过vlc 等播放器直接播放. 如下图 是通过WinHex工具 分析的一个h264文件 ffmpeg 获取h264 思路如下: 1,写4位头(00,00,00,01) 2,写sps 3,写4位头(00,00,00,01) 4,写pps 5,将读到的AVPacket.data 的前4位替换成(00,00,00,01)写文件. sps pps的获取参考上篇博文. 详细代码如下

【Android 多媒体应用】使用MediaCodec将摄像头采集的视频编码为h264

MainActivity.java import android.app.Activity; import android.graphics.ImageFormat; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; import android.os.Bundle; import android.vi

android下实现4分屏播放4路高清h264格式的rtsp流

====================问题描述==================== 用videoview做显示的话.播放一路一点问题都没有,不卡,很实时,但是,多了就播放不了了,还报错.自动弹出无法播放的对话框.用surfaceview也一样.听说是因为android底层只支持一路解码.难道非得移植ffmpeg才行么?可是这是软解码啊,效率太低了吧,而且貌似相当复杂. 谁有更好的办法呢. ====================解决方案1==================== 引用 4 

【转载】视频编码(H264概述)

一视频编码介绍 1.1 视频压缩编码的目标 1)保证压缩比例 2)保证恢复的质量 3)易实现,低成本,可靠性 1.2 压缩的出发点(可行性) 1)时间相关性 在一组视频序列中,相邻相邻两帧只有极少的不同之处,这便是时间相关性. 2)空间相关性 在同一帧中,相邻象素之间有很大的相关性,两象素越近,侧相关性越强. 根据采用的信源的模型分类: 1)基于波形的编码 如果采用“一幅图像由许多象素构成”的信源模型,这种信源模型的参数就是象素的亮度和色度的幅度值,对这些参数进行编码的技术即为基于波形编码. 2

利用ffmpeg0.6.1把.h264纯码流打包成.mp4 .avi等格式 (转载)

转自:http://cache2.weidaohang.org/h/index.php?q=aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1cWluZ183MzkvYXJ0aWNsZS9kZXRhaWxzLzY2MzY4NTc= 一直比较困惑一个问题,都说ffmpeg功能很强大,但是自己一直没有去研究一下,今天终于见识了一下它的强大之处了! 首先当然是在linux下编译和安装成功ffmpeg,关于具体的安装流程,可以参考我前面的一篇博文! 这里就直接介绍怎么把.h264纯码流打包

H264码流中SPS PPS详解&lt;转&gt;

转载地址:https://zhuanlan.zhihu.com/p/27896239 1 SPS和PPS从何处而来? 2 SPS和PPS中的每个参数起什么作用? 3 如何解析SDP中包含的H.264的SPS和PPS串? 1 客户端抓包 在做客户端视频解码时,一般都会使用Wireshark抓包工具对接收的H264码流进行分析,如下所示: 在这里我们可以看到对解码视频起关键作用的SPS和PPS. 双击SPS内容如下: 双击PPS内容如下: 那么从上面的sps中我们知道图像的宽,高. 宽=(19+1

采集音频和摄像头视频并实时H264编码及AAC编码

0. 前言 我在前两篇文章中写了DirectShow捕获音视频然后生成avi,再进行264编码的方法.那种方法有一些局限性,不适合实时性质的应用,如:视频会议.视频聊天.视频监控等.本文所使用的技术,适用于这种实时性的应用,通过处理采集出来的音视频的每一帧,实现实时编码,实时输出.这是我做直播系列应用的一部分,目前的情况是输入端采用DirectShow技术捕获音视频,然后对视频进行h.264编码,对音频进行aac编码,输出端则是生成文件,接下来还要进一步扩展输入端和输出端,以支持文件.桌面输入,

树莓派环境下使用python将h264格式的视频转为mp4

个人博客 地址:https://www.wenhaofan.com/article/20190430144809 下载安装MP4Box 命令行下执行以下指令安装MP4Box   sudo apt-get install gpac  代码 核心代码为使用 os.system 模块通过调用系统命令使用MP4Box将H264格式的视频转为MP4 import os import random import time import ctcmsconf #初始化文件夹 def init_video_fol