FFMpeg rtmp 推送本地yuv文件

发布于:2024-07-02 ⋅ 阅读:(139) ⋅ 点赞:(0)

可以借鉴的:C++使用FFmpeg实现YUV数据编码转视频文件_C 语言_脚本之家

yuv文件下载地址:YUV Sequences

代码:

#include <stdio.h>
#include <unistd.h>
#include <iostream>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
};
using namespace std;

const char* input_file = "/mnt/hgfs/shareVM/BigBuckBunny_CIF_24fps.yuv";
const char* output_rtmp_url = "rtmp://10.10.18.94:1935/live/test";

int main(int argc, char *argv[]) {
    AVFormatContext *pFormatCtx = nullptr;
    AVOutputFormat *fmt = nullptr;
    AVStream *video_st = nullptr;
    AVCodecContext *pCodecCtx = nullptr;
    AVCodec *pCodec = nullptr;
    uint8_t *picture_buf = nullptr;

    int size;
    //打开视频文件
    FILE *in_file = fopen(input_file, "rb");
    if (!in_file) {
        cout << "can not open file!" << endl;
        return -1;
    }

    //[1] --注册所有ffmpeg组件
    avcodec_register_all();
    av_register_all();
    //[2] --初始化AVFormatContext结构体,根据文件名获取到合适的封装格式
    avformat_alloc_output_context2(&pFormatCtx, NULL, "flv", output_rtmp_url);
    fmt = pFormatCtx->oformat;
    //[3] --打开文件
    if (avio_open(&pFormatCtx->pb, output_rtmp_url, AVIO_FLAG_READ_WRITE)) {
        cout << "output file open fail!";
        return -1;
    }
    //[3]
    //[4] --初始化视频码流
    video_st = avformat_new_stream(pFormatCtx, 0);
    if (video_st == NULL)
    {
        printf("failed allocating output stram\n");
        return -1;
    }
    video_st->time_base.num = 1;
    video_st->time_base.den = 25;
    //[4]
    //[5] --编码器Context设置参数
    pCodecCtx = video_st->codec;
    pCodecCtx->codec_id = fmt->video_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    pCodecCtx->width = 352;
    pCodecCtx->height = 288;
    pCodecCtx->time_base = {1, 25};
    pCodecCtx->framerate = {25, 1};
    pCodecCtx->bit_rate = 400000;
    pCodecCtx->gop_size = 50;
    //[5]
    //[6] --寻找编码器并打开编码器
    pCodec = avcodec_find_encoder(AV_CODEC_ID_FLV1);
    if (!pCodec)
    {
        cout << "no right encoder!" << endl;
        return -1;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        cout << "open encoder fail!" << endl;
        return -1;
    }
    //[6]
    //输出格式信息
    av_dump_format(pFormatCtx, 0, output_rtmp_url, 1);
    //初始化帧
    AVFrame *picture = av_frame_alloc();
    picture->width = pCodecCtx->width;
    picture->height = pCodecCtx->height;
    picture->format = pCodecCtx->pix_fmt;
    av_frame_get_buffer(picture, 32);
    size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    picture_buf = (uint8_t*)av_malloc(size);
    avpicture_fill((AVPicture*)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    //[7] --写头文件
    avformat_write_header(pFormatCtx, NULL);
    //[7]
    //[8] --循环编码每一帧
    AVPacket pkt; //创建已编码帧
    int frame_count = 0;

    while (!feof(in_file)) {
           // 读取一帧 YUV 数据
           uint8_t yuv_buf[pCodecCtx->width * pCodecCtx->height * 3 / 2];
           size_t read_size = fread(yuv_buf, 1, pCodecCtx->width * pCodecCtx->height * 3 / 2, in_file);
           if (read_size <= 0) {
               break;
           }
           av_init_packet(&pkt);
           pkt.data = NULL;
           pkt.size = 0;

           // 将 YUV 数据编码为 H.264
           memcpy(picture->data[0], yuv_buf, pCodecCtx->width * pCodecCtx->height);
           memcpy(picture->data[1], yuv_buf + pCodecCtx->width * pCodecCtx->height, pCodecCtx->width * pCodecCtx->height / 4);
           memcpy(picture->data[2], yuv_buf + pCodecCtx->width * pCodecCtx->height * 5 / 4, pCodecCtx->width * pCodecCtx->height / 4);

           picture->pts = frame_count;
           int got_picture = 0;
           //编码
           int ret = avcodec_encode_video2(pCodecCtx, &pkt, picture, &got_picture);
           if (ret < 0)
           {
               cout << "encoder fail!" << endl;
               return -1;
           }
           if (!got_picture)
           {
               ret = 0;
               break;
           }

           cout << "encoder success! " <<picture->pts<< endl;
           // parpare packet for muxing
           pkt.stream_index = video_st->index;
           pkt.pts = frame_count * (pCodecCtx->time_base.den) / ((pCodecCtx->time_base.num) * 25);
           pkt.dts = pkt.pts;

           av_packet_rescale_ts(&pkt, pCodecCtx->time_base, video_st->time_base);
           pkt.pos = -1;
           ret = av_interleaved_write_frame(pFormatCtx, &pkt);
           if(ret < 0)
               break;

           av_free_packet(&pkt);

           frame_count++;
           usleep(30*1000);
       }
    //[8]

    //[9] --写文件尾
    av_write_trailer(pFormatCtx);
    //[9]

    //释放内存
    if (video_st)
    {
        avcodec_close(video_st->codec);
        av_free(picture);
        av_free(picture_buf);
    }
    if (pFormatCtx)
    {
        avio_close(pFormatCtx->pb);
        avformat_free_context(pFormatCtx);
    }
    fclose(in_file);
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到