FFmpegPreRecordH264Provider is dependent on FFmpegH264Provider. It is designed for pre-recording. It supports setting the cache duration in milliseconds.
API Instructions
class FFmpegPreRecordH264Provider { public: FFmpegPreRecordH264Provider(std::shared_ptr<FFmpegVideoSource> yuvSource, int maxCacheTimeMs = 5000); ~FFmpegPreRecordH264Provider(); public: int prepare(VideoStreamParam_t param); void destroy(); AVCodecContext* getAVCodecContext(); int getPreRecordFrames(std::queue<AVPacket*>& frameQueue); }; }}}
Constructors
When creating FFmpegPreRecordH264Provider you can set the cache size or duration.
if maxCacheSize > 0, maxCacheTimeMs is valid, otherwise, use the default value of 5000.
/** * @param yuvSource provide yuv data. * * @param maxCacheTimeMs >0 is valid, otherwise, use the default value of 5000. * */ FFmpegPreRecordH264Provider(std::shared_ptr<FFmpegVideoSource> yuvSource, int maxCacheTimeMs = 5000);
prepare
Set encoding parameters, and prepare FFmpegH264Provider.
/** * Set encoding parameters and initialize all required resources. * * @param param set parameters of the encoder. * */ int prepare(VideoStreamParam_t param);
destroy
Destroy FFmpegH264Provider.
/** * Empty the cached data and release all resources. */ void destroy();
getAVCodecContext
Get AVCodecContext to get the info of the encoder, such as width, height, spspps, etc.
/** * get encoder context. */ AVCodecContext* getAVCodecContext();
getPreRecordFrames
Get all cached h264 frames from the queue. The packet must be freed with av_packet_unref()+av_packet_free() when it is no longer needed.
/** * Get all cached frames from the queue. * On success, the packet must be freed with av_packet_unref()+av_packet_free() when * it is no longer needed. * @return 0 if OK, < 0 on error. */ int getPreRecordFrames(std::queue<AVPacket*>& frameQueue);
Sample Code
This is a sample of how to pre-record MP4 dependent on FFmpegPreRecordH264Provider FFmpegH264Provider and FFmpegAVMuxer.
the flow of pre-record MP4:
create yuv source --> create pre-record h264 provider --> pre-record provider prepare --> recording 6s --> create h264 provider --> h264 provider prepare -> create MP4 muxer -> init muxer --> put all pre-record frames to muxer --> create the thread of get the frame from h264 provider to put to muxer --> recording 3s --> uninit muxer
void FFmpegPreRecordMP4Muxer_Test(const char* path) { auto videoSource = make_shared<FFmpegV4L2VideoSource>("/dev/video0"); AVDictionary *options = nullptr; av_dict_set(&options, "video_size", "1280x720", 0); av_dict_set(&options, "framerate", "30", 0); av_dict_set(&options, "pixel_format", "uyvy422", 0); int ret = videoSource->open(options); auto preRecordH264Provider = make_shared<FFmpegPreRecordH264Provider>(videoSource, 5000); VideoStreamParam_t param; param.width = videoSource->getAVStream()->codecpar->width; param.height = videoSource->getAVStream()->codecpar->height; param.pix_fmt = (enum AVPixelFormat)videoSource->getAVStream()->codecpar->format; param.time_base = videoSource->getAVStream()->time_base; param.gop = 30; param.bitrate = 1000000; param.fps = 30; ret = preRecordH264Provider->prepare(param); if (ret == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(10000)); } else { preRecordH264Provider->destroy(); videoSource->close(); return; } auto h264Provider = make_shared<FFmpegH264Provider>(videoSource); auto mp4Muxer = make_shared<FFmpegAVMuxer>(true); ret = h264Provider->prepare(param); string filepath = "ffmpeg_prerecord_mp4_muxer_test.mp4"; if (path != nullptr) { filepath = string(path); } auto muxMp4ThreadFunc = [&](){ int index = 0; bool initMuxer = false; while(!is_exit) { AVPacket* packet = nullptr; auto ret = h264Provider->getFrame(packet); if (ret < 0 || packet == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } if (!initMuxer) { std::queue<AVPacket*> preRecordFrames; ret = preRecordH264Provider->getPreRecordFrames(preRecordFrames); ret = mp4Muxer->init(filepath, h264Provider->getAVCodecContext()); initMuxer = true; int preRecordIndex = 0; while(!preRecordFrames.empty()) { AVPacket* prePacket = preRecordFrames.front(); printf("muxMp4Thread, preRecordFrames get h264 frame[%d] pts: %lld, size: %d, isKeyFrame: %d\n", index, prePacket->pts, prePacket->size, prePacket->flags & AV_PKT_FLAG_KEY); ret = mp4Muxer->putVideoFrame(prePacket); av_packet_unref(prePacket); av_packet_free(&prePacket); preRecordFrames.pop(); preRecordIndex++; } } printf("muxMp4Thread, get h264 frame[%d] pts: %lld, size: %d, isKeyFrame: %d\n", index, packet->pts, packet->size, packet->flags & AV_PKT_FLAG_KEY); ret = mp4Muxer->putVideoFrame(packet); av_packet_unref(packet); av_packet_free(&packet); index++; } mp4Muxer->flush(); mp4Muxer->uninit(); }; auto thread = make_shared<std::thread>(muxMp4ThreadFunc); _wait_exit("FFmpegPreRecordMP4Muxer_Test"); h264Provider->destroy(); preRecordH264Provider->destroy(); videoSource->close(); }
Test Result
./ffmpeg_sample mp4prerecord