我有一组从 IP 摄像机的 RTSP 获取的原始 H264 AVPackets。 所以我将把它们放入 MP4 容器中(不是将文件放入缓冲区),然后将缓冲区传输到网络上。 我在 ffmpeg 网站上阅读了示例,并在 Stackoverflow 上浏览了很多内容,因此编写了以下代码,但最后当我将缓冲区中的 mp4 结果写入文件时,它无法播放。 但是,当我使用
avio_open2
并使用 libav 写入文件时,它工作正常并且文件可以播放。
#include <iostream>
#include <vector>
#include <queue>
#include "stdlib.h"
#include <fstream>
#include <list>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h>
#include <libswscale/swscale.h>
#include "libavutil/imgutils.h"
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
}
#define VIDEO_CODEC_ID AV_CODEC_ID_H264
#define AUDIO_CODEC_ID AV_CODEC_ID_AAC
#define BUFFER_SIZE 1024*1024*10 // 10 MB buffer size (adjust as needed)
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <pthread.h>
#include <assert.h>
#include <algorithm>
#include <limits>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <bitset>
using namespace std;
using namespace cv;
int main(int argc, char ** argv) {
av_register_all();
avcodec_register_all();
FILE *fp;
fp = fopen("./sample_iocontext.mp4", "wb");
// Muxer2
AVFormatContext* muxer2 = avformat_alloc_context();
muxer2->oformat = av_guess_format("mp4", NULL, NULL);
AVStream* video_track2 = avformat_new_stream(muxer2, NULL);
//Muxer2
avcodec_parameters_from_context(video_track2->codecpar, mycodec); //sample codec from another function
video_track2->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
video_track2->codec->codec_id = AV_CODEC_ID_H264;
video_track2->codec->codec_type = AVMEDIA_TYPE_VIDEO;
video_track2->codec->width = 1280;
video_track2->codec->height = 960;
video_track2->codec->time_base.den = 20;
video_track2->codec->time_base.num = 1;
video_track2->time_base = (AVRational) {1,20};
video_track2->avg_frame_rate = mycodec->framerate;
muxer2->oformat->video_codec = AV_CODEC_ID_H264;
avio_buffer = (uint8_t*)av_malloc(BUFFER_SIZE);
AVIOContext* ioContext = avio_alloc_context(avio_buffer, BUFFER_SIZE, 1, nullptr, nullptr, nullptr, nullptr);
ioContext->seekable = 1;
muxer2->pb = ioContext;
muxer2->flags = AVFMT_FLAG_CUSTOM_IO;
// Write MP4 container header to memory buffer
AVDictionary *options = NULL;
av_dict_set(&options, "live", "1", 0);
avformat_write_header(muxer2, &options);
int packetId=0;
for(int ii=0;ii<100;ii++){
AVPacket *temp_packet2; //get packet from my array of AVPacket
AVRational encoder_time_base = (AVRational) {1, 20};
temp_packet2->stream_index = video_track2->index;
int64_t scaled_pts2 = av_rescale_q(packetId, encoder_time_base, video_track2->time_base);
temp_packet2->pts = scaled_pts2;
int64_t scaled_dts2 = av_rescale_q(packetId, encoder_time_base, video_track2->time_base);
temp_packet2->dts = scaled_dts2;
packetId++;
av_interleaved_write_frame(muxer2, temp_packet2);
av_packet_free(&temp_packet2);
}
// Write MP4 container trailer to memory buffer
av_write_trailer(muxer2);
fwrite(avio_buffer, 1, ioContext->pos, fp);
fclose(fp);
av_freep(&avio_buffer);
avformat_free_context(muxer2);
return 0;
}
一周后我发现这是 mp4 容器的正常行为。 实际上 mp4 非常适合
VOD
或 offline playback
,而不是直播的合适选择。虽然mp4
容器压缩比较高,使用的B/W
较少,但mp4 muxer
需要所有AVPacket
来生成meta information
,所以LibAVFormat
等待调用av_write_trailer
,意味着没有视频帧并写入meta tags
,放置header/footer segment
。这就是为什么seekable
记忆对于mp4 muxer
至关重要。
我建议使用HLS
或DASH
通过网络发送直播数据包。