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;
}