详细介绍:音视频学习(六十六):使用ffmpeg api将实时的264、265裸流封装为fmp4

示例

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include   // for file input
// 错误处理宏
#define CHECK_RET(ret, msg) do { \
if (ret = 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1) {
*start_code_len = 4;
return 0;
}
if (size >= 3 && buf[0] == 0 && buf[1] == 0 && buf[2] == 1) {
*start_code_len = 3;
return 0;
}
return -1;
}
// 解析 VPS/SPS/PPS 并存储到 extradata
static int parse_nalu_extradata(AVCodecParserContext* parser, AVCodecContext* codec_ctx,
const uint8_t* data, int size,
uint8_t** extradata, int* extradata_size) {
std::vector extradata_buf;
int offset = 0;
while (offset codec_id == AV_CODEC_ID_H264 ?
(nalu_data[start_code_len] & 0x1F) :
((nalu_data[start_code_len] >> 1) & 0x3F);
// H.264: SPS (7), PPS (8); H.265: VPS (32), SPS (33), PPS (34)
bool is_extradata_nalu = false;
if (codec_ctx->codec_id == AV_CODEC_ID_H264 &&
(nalu_type == 7 || nalu_type == 8)) {
is_extradata_nalu = true;
} else if (codec_ctx->codec_id == AV_CODEC_ID_HEVC &&
(nalu_type == 32 || nalu_type == 33 || nalu_type == 34)) {
is_extradata_nalu = true;
}
if (is_extradata_nalu) {
extradata_buf.insert(extradata_buf.end(), nalu_data, nalu_data + nalu_size);
}
offset = next_offset;
}
if (extradata_buf.empty()) {
return -1;  // 未找到 extradata
}
*extradata_size = extradata_buf.size();
*extradata = (uint8_t*)av_malloc(*extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!*extradata) {
return AVERROR(ENOMEM);
}
memcpy(*extradata, extradata_buf.data(), *extradata_size);
memset(*extradata + *extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
return 0;
}
int main(int argc, char** argv) {
if (argc != 4) {
std::cerr   \n";
std::cerr id = fmt_ctx->nb_streams - 1;
// 4. 初始化 codec 参数和解析器
const AVCodec* codec = avcodec_find_decoder(codec_id);
if (!codec) {
std::cerr codecpar;
codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
codecpar->codec_id = codec_id;
codecpar->format = AV_PIX_FMT_YUV420P;  // 假设
codecpar->width = 1920;   // 示例;可从 SPS 解析
codecpar->height = 1080;
codecpar->bit_rate = 5000000;
// 设置时间基
stream->time_base = {1, 30};  // 30fps
codec_ctx->time_base = stream->time_base;
// 5. 打开输入(文件或 UDP)
FILE* input_file = nullptr;
if (strncmp(input_filename, "udp://", 6) != 0) {
input_file = fopen(input_filename, "rb");
if (!input_file) {
std::cerr extradata = extradata;
codecpar->extradata_size = extradata_size;
// 7. 打开输出文件
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&fmt_ctx->pb, output_filename, AVIO_FLAG_WRITE);
CHECK_RET(ret, "Failed to open output file");
}
// 8. fMP4 选项
AVDictionary* opt = nullptr;
av_dict_set(&opt, "movflags", "frag_keyframe+empty_moov+default_base_moof", 0);
av_dict_set(&opt, "fragment_duration", "4000", 0);  // 4s 片段
av_dict_set(&opt, "flush_packets", "1", 0);
av_dict_set(&opt, "use_editlist", "0", 0);  // 避免 edit list
// 9. 写头
ret = avformat_write_header(fmt_ctx, &opt);
av_dict_free(&opt);
CHECK_RET(ret, "Failed to write header");
// 10. 实时循环:读取裸流,封装 AVPacket
av_init_packet(&pkt);
int64_t frame_count = 0;
int64_t start_time = av_gettime();
fseek(input_file, 0, SEEK_SET);  // 重置文件指针
while ((bytes_read = fread(buffer, 1, sizeof(buffer), input_file)) > 0) {
int offset = 0;
while (offset id;
// 时间戳:基于实时时间
int64_t current_time = av_gettime();
int64_t time_diff = (current_time - start_time) / 1000000.0 * 30;  // 30fps
pkt.pts = time_diff;
pkt.dts = pkt.pts;  // 假设无 B 帧
pkt.duration = 1;
// 检查关键帧 (H.264: IDR=5; H.265: IDR=19-21)
int nalu_type = codec_id == AV_CODEC_ID_H264 ?
(buffer[offset + start_code_len] & 0x1F) :
((buffer[offset + start_code_len] >> 1) & 0x3F);
if ((codec_id == AV_CODEC_ID_H264 && nalu_type == 5) ||
(codec_id == AV_CODEC_ID_HEVC && (nalu_type >= 19 && nalu_type oformat->flags & AVFMT_NOFILE)) {
avio_closep(&fmt_ctx->pb);
}
avformat_free_context(fmt_ctx);
return ret < 0 ? ret : 0;
}
posted @ 2025-10-10 16:24  yxysuanfa  阅读(10)  评论(0)    收藏  举报