Skip to content

ffmpeg bgr转码编码h264再推送至rtsp服务器

C++
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/time.h>
}

#include <memory>
#include <fstream>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdio>

/*
    auto time_point = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(time_point);
    char time_buf[32]{0};
    auto time_size = strftime(time_buf, sizeof(time_buf), "%F %T", std::localtime(&time));
    time_buf[time_size] = ' ';
    time_buf[time_size + 1] = '\0';
    std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(time_point.time_since_epoch()) % 1000000;
    auto mils = us.count() / 1000;
    std::string out_time_str(time_buf);
    out_time_str += std::to_string(mils);
    printf("time: %s, get one frame, ", out_time_str.c_str());
    if (bufLen >= 8) {
      printf("naltype: %d, ", buf[4] & 0x1f);
      for (int i = 0; i < 8; ++i) {
        printf("%02x ", buf[i]);
      }
      if ((buf[4] & 0x1f) == 1) {
          if (!first_key) {
              printf("\nfirst frame is not key, skip");
              return;
          }
      } else {
          printf("\ngot key_frame");
          first_key = true;
      }
    }
  printf("\n");
*/

int readFile(const std::string &filename, char **data)
{
    int size = 0;
    std::fstream in(filename, std::ios::in);
    if (!in.is_open()) {
        printf("failed to opne file %s\n", filename.c_str());
        return 0;
    }

    in.seekg(0, std::ios::end);
    size = (int)in.tellg();
    in.seekg(0, std::ios::beg);

    *data = new char[size];
    in.read(*data, size);
    return size;
}

int main() {
    // 初始化编解码器库
    avcodec_register_all();

    int src_width = 1920;
    int src_height = 1080;
    int dst_width = 1280;
    int dst_height = 720;
    // 查找 H.264 编码器
 //   AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    AVCodec* codec = avcodec_find_encoder_by_name("libx264");
    if (!codec) {
        std::cerr << "Codec not found" << std::endl;
        return -1;
    }

    // 创建编码器上下文
    AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        std::cerr << "Could not allocate video codec context" << std::endl;
        return -1;
    }

    // 设置编码器参数
    codec_ctx->bit_rate = 2000000;
    codec_ctx->width = dst_width;
    codec_ctx->height = dst_height;
    codec_ctx->time_base = (AVRational){1, 1000};
    codec_ctx->framerate = (AVRational){25, 1};
    codec_ctx->gop_size = 25;
    codec_ctx->max_b_frames = 0;
    codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;

    // av_opt_set(codec_ctx->priv_data, "preset", "slow", 0);
    av_opt_set(codec_ctx->priv_data, "crf", "23", 0);
    av_opt_set(codec_ctx->priv_data, "qpmin", "10", 0);
    av_opt_set(codec_ctx->priv_data, "qpmax", "40", 0);
    av_opt_set(codec_ctx->priv_data, "forcecfr", "1", 0);
    av_opt_set(codec_ctx->priv_data, "x264-params", "scenecut=0:keyint=25:min-keyint=25", 0);
     av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0);
    av_opt_set(codec_ctx->priv_data, "preset", "ultrafast", 0);
    av_opt_set(codec_ctx->priv_data, "aud", "0", 0);


    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        std::cerr << "Could not open codec" << std::endl;
        return -1;
    }

    AVFormatContext* ofmt_ctx = nullptr;
    avformat_alloc_output_context2(&ofmt_ctx, nullptr, "rtsp", "rtsp://192.168.1.100:8554/stream");
    if (!ofmt_ctx) {
        fprintf(stderr, "Could not create output context.\n");
        return -1;
    }

    AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);


    printf("2\n");
    out_stream->time_base = codec_ctx->time_base;
    avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);

    AVDictionary* opts = nullptr;
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    av_dict_set(&opts, "muxdelay", "0.1", 0);

    printf("4\n");
    // Write the stream header
    if (avformat_write_header(ofmt_ctx, &opts) < 0) {
        fprintf(stderr, "Error occurred when opening output URL.\n");
        return -1;
    }

    // 分配和设置帧
    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        std::cerr << "Could not allocate video frame" << std::endl;
        return -1;
    }
    frame->format = codec_ctx->pix_fmt;
    frame->width = codec_ctx->width;
    frame->height = codec_ctx->height;

    if (av_image_alloc(frame->data, frame->linesize, dst_width, dst_height, AV_PIX_FMT_YUV420P, 32) < 0) {
        return -1;
    }
    // 创建图像格式转换上下文
    struct SwsContext* sws_ctx = sws_getContext(
        src_width, src_height, AV_PIX_FMT_BGR24,
        dst_width, dst_height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC, NULL, NULL, NULL);

    if (!sws_ctx) {
        std::cerr << "Could not initialize the conversion context" << std::endl;
        return -1;
    }

    // 创建一个虚拟的 BGR24 图像
    // std::vector<uint8_t> bgr24_data(width * height * 3, 0);

    char *image = nullptr;
    int needsize = src_width * src_height * 3;
    int size = readFile("./xx.raw", &image);
//    int size = readFile("./out.bgr", &image);
    if (image == nullptr || size <= 0) {
        printf("error reading file\n");
        return -1;
    }
    int frames = size / needsize;

   printf("7\n");


    // auto outyuv = std::make_unique<std::ofstream>();
    // outyuv->open("./o.yuv", std::ios::out | std::ios::binary);
    auto out = std::make_unique<std::ofstream>();
    out->open("./o.h264", std::ios::out | std::ios::binary);
    // 转换 BGR24 到 YUV420P
    int loops = 0;
    int64_t start_time = av_gettime();
    for (int i = 0; i < frames;) {
        uint8_t* bgr24_planes[1] = { (uint8_t*)(image + i * needsize) };
        int bgr24_linesize[1] = { 3 * src_width };
        sws_scale(sws_ctx, bgr24_planes, bgr24_linesize, 0, src_height, frame->data, frame->linesize);

//      frame->data[0] = (uint8_t*)(image + i * needsize);
 //     frame->linesize[0] = frame->width * 3;
//        frame->pts = i * 1000 / 30;


        // int y_size = dst_height * dst_width;
        // int uv_width = dst_width / 2;
        // int uv_height = dst_height / 2;
        // int uv_size  = uv_width * uv_height;
        // printf("%d\n", y_size + uv_size + uv_size);

        // outyuv->write((const char*)(frame->data[0]), y_size);
        // outyuv->write((const char*)(frame->data[0] + y_size), uv_size);
        // outyuv->write((const char*)(frame->data[0] + y_size + uv_size), uv_size);
        // outyuv->flush();
        // outyuv->close();
        AVPacket pkt;
        av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;

        int ret = avcodec_send_frame(codec_ctx, frame);
        if (ret < 0) {
            std::cerr << "Error sending a frame for encoding: " << ret << std::endl;
            break;
        }
        while (ret >= 0) {
            ret = avcodec_receive_packet(codec_ctx, &pkt);
            if (ret == AVERROR(EAGAIN)) {
                continue;
            } else if (ret < 0) {
                std::cerr << "Error during encoding: " << ret << std::endl;
                break;
            }


            pkt.pts = (i + loops * frames) * 1000 / 25;     
            pkt.dts = pkt.pts;                                                                                          
            pkt.duration = 1000 / 25;                                                                             

            pkt.pts = av_rescale_q(pkt.pts, codec_ctx->time_base, out_stream->time_base);                               
            pkt.dts = av_rescale_q(pkt.dts, codec_ctx->time_base, out_stream->time_base);                               
            pkt.duration = av_rescale_q(pkt.duration, codec_ctx->time_base, out_stream->time_base);    

//            av_packet_rescale_ts(&pkt, codec_ctx->time_base, out_stream->time_base);
            pkt.stream_index = out_stream->index;
            std::cout << "Encoded frame size: " << pkt.size << std::endl;
            // if (pkt.size > 0) {
            //     out->write((const char*)pkt.data, pkt.size);
            //     out->flush();
            // }
            if (pkt.flags & AV_PKT_FLAG_KEY) {
                printf("\e[92m got keyframe\e[0m\n");
            } else {
                printf("\e[34m non-key \e[0m\n");
            }

            /*
            pkt.pts = pkt.dts = (av_gettime() - start_time) / 1000;
            pkt.duration = 1000 / 25;
            */
            if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0) {
                break;
            }

            av_packet_unref(&pkt);
            i++;
            if (i == frames) {
                ++loops;
                i = 0;
            }
        }
    }

    av_write_trailer(ofmt_ctx);
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&ofmt_ctx->pb);
    }

    avformat_free_context(ofmt_ctx);
    avformat_network_deinit();

    out->close();
    av_frame_free(&frame);
    avcodec_free_context(&codec_ctx);
    sws_freeContext(sws_ctx);

    return 0;
}