Reputation: 1
Use FFMpeg, When multiple threads use multiple h264_nvenc instances(one instance per thread), an exception crash occurs during release(avcodec_free_context), and the final exception occurs in libnvcuvid.so. I don't know what the reason is? Please help, thanks. The same problem exists: ffmpeg v5.0.1 + cuda v11.6 and ffmpeg v7.0.1 + cuda v12.2 operating system:Ubuntu 22.04.4 LTS
The specific code is as follows:
class NvencEncoder {
public:
NvencEncoder() {}
~NvencEncoder { Close(); }
bool Open() {
auto encoder = avcodec_find_encoder_by_name("h264_nvenc");
pCodecCtx_ = avcodec_alloc_context3(encoder);
if (!pCodecCtx_)
return false;
int width = 1920;
int height = 1080;
int bitrate = 1000000;
pCodecCtx_->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx_->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx_->width = width;
pCodecCtx_->height = height;
pCodecCtx_->bit_rate = bitrate;
pCodecCtx_->rc_min_rate = bitrate;
pCodecCtx_->rc_max_rate = bitrate;
pCodecCtx_->bit_rate_tolerance = bitrate;
pCodecCtx_->rc_buffer_size = bitrate / 2;
pCodecCtx_->time_base = AVRational{ 1, 90000 };
pCodecCtx_->framerate = AVRational{ 25, 1 };
pCodecCtx_->gop_size = 50;
pCodecCtx_->max_b_frames = 0;
pCodecCtx_->delay = 0;
pCodecCtx_->refs = 2;
pCodecCtx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
av_opt_set_int(pCodecCtx_->priv_data, "gpu", 0, 0);
av_opt_set(pCodecCtx_->priv_data, "preset", "llhp", 0);
av_opt_set(pCodecCtx_->priv_data, "rc", "cbr", 0);
av_opt_set(pCodecCtx_->priv_data, "profile", "main", 0);
av_opt_set(pCodecCtx_->priv_data, "zerolatency", "1", 0);
av_opt_set(pCodecCtx_->priv_data, "delay", "0", 0);
av_opt_set(pCodecCtx_->priv_data, "preset", "medium", 0);
int ret = avcodec_open2(pCodecCtx_, encoder, nullptr);
if (ret < 0)
return false;
pkt_ = av_packet_alloc();
if (!pkt_)
return false;
char output_mp4[] = "output.mp4";
ret = avformat_alloc_output_context2(&avMp4Context_, NULL, "mp4", output_mp4);
if (ret < 0)
return false;
mp4_stream_ = avformat_new_stream(avMp4Context_, nullptr);
if (!mp4_stream_)
return false;
ret = avcodec_parameters_copy(mp4_stream_->codecpar, out_stream_->codecpar);
if (ret < 0)
return false;
mp4_stream_->codecpar->codec_tag = 0;
if (!(avMp4Context_->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&avMp4Context_->pb, output_mp4_.c_str(), AVIO_FLAG_WRITE);
if (ret < 0) {
return false;
}
return true;
}
void Close() {
if (pCodecCtx_)
avcodec_free_context(&pCodecCtx_); // Crash will occur in libnvcuvid.so
if (avMp4Context_) {
if (avMp4Context_->oformat && !(avMp4Context_->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&avMp4Context_->pb);
}
avformat_free_context(avMp4Context_);
avMp4Context_ = nullptr;
}
if (pkt_)
av_packet_free(&pkt_);
}
bool InputFrame(AVFrame* frame) {
int ret = avcodec_send_frame(pEncoderVideoCodecCtx_, frame);
if (ret < 0)
return false;
while (ret >= 0) {
ret = avcodec_receive_packet(pEncoderVideoCodecCtx_, pkt_);
if (ret < 0)
break;
if (avNotHeadWrited_) {
ret = avformat_write_header(avMp4Context_, &opts);
if (ret < 0) {
av_packet_unref(pkt_);
break;
}
avNotHeadWrited_ = false;
}
av_packet_rescale_ts(pkt_, pCodecCtx_->time_base, mp4_stream_->time_base);
ret = av_write_frame(avMp4Context_, pkt_);
if (ret < 0) {
av_packet_unref(pkt_);
break;
}
av_packet_unref(pkt_);
}
return (ret >= 0);
}
private:
AVPacket* pkt_ = nullptr;
AVCodecContext* pCodecCtx_ = nullptr;
AVFormatContext* avMp4Context_ = nullptr;
AVStream* mp4_stream_ = nullptr;
avNotHeadWrited_ = true;
}
uint8_t* data = nullptr; //a frame of yuv420 data
void Run(int idx);
int main() {
//Fill a frame of yuv420 data here
...
std::thread th[3];
for (int i = 0; i < 3; i++) {
th[i] = std::thread(Run, i);
sleep(3);
}
sleep(35);
for (int i = 0; i < 3; i++) {
if (th[i].joinable()) {
printf("thread %d join()\n", i);
th[i].join();
}
}
free(data);
printf("Exit\n");
}
void Run(int idx) {
printf("Run() thread(%d)\n", idx);
//cudaSetDevice(0);
auto nvenc = new NvencEncoder(ffpar, FFOutputCB);
if (!nvenc->Open()) {
delete nvenc;
return;
}
auto avframe_ = av_frame_alloc();
avframe_->width = 1920;
avframe_->height = 1080;
avframe_->format = AV_PIX_FMT_YUV420P;
int ret = av_frame_get_buffer(avframe_, 0);
if (ret < 0) {
printf("av_frame_get_buffer() is error %d\n", ret);
delete nvenc;
av_frame_free(&avframe_);
return;
}
int frame_size = 1920 * 1080;
double one_frame_us = 1000000.0 / 25.0;
unsigned long frame_count = 0;
struct timeval t1, t2;
double timeuse;
AVRational timebase = { ffpar.timebase_num, ffpar.timebase_den };
std::int64_t llCalcDuration = (double)AV_TIME_BASE / 25.0;
double in_stream_timebase = av_q2d(timebase);
std::int64_t duration = (double)llCalcDuration / (double)(in_stream_timebase * AV_TIME_BASE);
avframe_->time_base = timebase;
gettimeofday(&t1, NULL);
while (frame_count < 25*30) { //30 seconds
avframe_->pts = (double)(frame_count * llCalcDuration) / (double(in_stream_timebase * AV_TIME_BASE));
//avframe_->duration = duration;
frame_count++;
ret = av_frame_make_writable(avframe_);
if (ret < 0) {
printf("av_frame_make_writable() is error %d\n", ret);
break;
}
// copy YUV420
memcpy(avframe_->data[0], data, frame_size);
memcpy(avframe_->data[1], data + frame_size, frame_size / 4);
memcpy(avframe_->data[2], data + frame_size * 5 / 4, frame_size / 4);
ret = nvenc->InputFrame(avframe_);
if (ret < 0) {
printf("InputFrame() is error: %d\n", ret);
break;
}
// frame rate
gettimeofday(&t2, NULL);
timeuse = (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec); //us
if (timeuse < one_frame_us) {
usleep(one_frame_us - timeuse);
}
gettimeofday(&t1, NULL);
}
if (frame_count > 0) {
nvenc->WriteTrailer();
}
printf("do Close() thread(%d)\n", idx);
nvenc->Close(); // Crash will occur
printf("Closed thread(%d)\n", idx);
delete nvenc;
av_frame_free(&avframe_);
}
Upvotes: 0
Views: 65