#c #macos #qt #ffmpeg #c 17
Вопрос:
Я пишу экранный рекордер, который работает на другой платформе, и я застрял на версии macOS.
Здесь есть фрагмент кода, который выполняет кодирование видео
void ScreenRecorder::StartEncode() { int ret; AVFrame *inputFrame = av_frame_alloc(); AVPacket *inputPacket = av_packet_alloc(); AVFrame *outputFrame = av_frame_alloc(); AVPacket *outputPacket = av_packet_alloc(); uint64_t audioFrameCount = 0; uint64_t videoFrameCount = 0; int64_t nextVideoPTS = 0, nextAudioPTS = 0; auto src_pix_fmt = correct_for_deprecated_pixel_format(videoInCodecCtx-gt;pix_fmt); auto swsContext = sws_getContext(width, height,src_pix_fmt, width, height, PIX_SWS_CONTEXT, SWS_BICUBIC, nullptr, nullptr, nullptr); while (isRun) { int choice = 0; if (videoInFormatCtx amp;amp; audioInFormatCtx) choice = av_compare_ts(nextVideoPTS, videoOutStream-gt;time_base, nextAudioPTS, audioOutStream-gt;time_base); else if (videoInFormatCtx) choice = -1; else if (audioInFormatCtx) choice = 1; if (choice == -1) { // Video packet av_read_frame(videoInFormatCtx, inputPacket); avcodec_send_packet(videoInCodecCtx, inputPacket); avcodec_receive_frame(videoInCodecCtx, inputFrame); outputFrame-gt;format = PIX_OUTPUT_FMT; outputFrame-gt;width = width; outputFrame-gt;height = height; ret = av_frame_get_buffer(outputFrame, 0); if (ret lt; 0) { throw std::runtime_error("Unable to allocate video frame"); } sws_scale(swsContext, inputFrame-gt;data, inputFrame-gt;linesize, 0, height, outputFrame-gt;data, outputFrame-gt;linesize); outputFrame-gt;pts = videoFrameCount * videoOutStream-gt;time_base.den / framerate; avcodec_send_frame(videoOutCodecCtx, outputFrame); if (avcodec_receive_packet(videoOutCodecCtx, outputPacket) == AVERROR(EAGAIN)) continue; outputPacket-gt;stream_index = videoOutStream-gt;index; outputPacket-gt;duration = 0; //videoOutStream-gt;time_base.den / 30; outputPacket-gt;dts = outputPacket-gt;pts = videoFrameCount * videoOutStream-gt;time_base.den / framerate; // std::cerr lt;lt; "tVideo::PTS (" lt;lt; outputFrame-gt;pts lt;lt; ") timebase " lt;lt; videoOutStream-gt;time_base.num lt;lt; "/" lt;lt; videoOutStream-gt;time_base.den lt;lt; " real: " lt;lt; (outputPacket-gt;pts / (double)videoOutStream-gt;time_base.den) lt;lt; std::endl; nextVideoPTS = outputFrame-gt;pts; av_interleaved_write_frame(outFormatCtx, outputPacket); //av_write_frame(outFormatCtx, outputPacket); av_frame_unref(inputFrame); av_packet_unref(inputPacket); av_frame_unref(outputFrame); av_packet_unref(outputPacket); } else { // decoding ret = av_read_frame(audioInFormatCtx, inputPacket); if (ret lt; 0) { throw std::runtime_error("can not read frame"); } ret = avcodec_send_packet(audioInCodecCtx, inputPacket); if (ret lt; 0) { throw std::runtime_error("can not send pkt in decoding"); } ret = avcodec_receive_frame(audioInCodecCtx, inputFrame); if (ret lt; 0) { throw std::runtime_error("can not receive frame in decoding"); } //-------------------------------- // encoding uint8_t **cSamples = nullptr; ret = av_samples_alloc_array_and_samples(amp;cSamples, NULL, audioOutCodecCtx-gt;channels, inputFrame-gt;nb_samples, requireAudioFmt, 0); if (ret lt; 0) { throw std::runtime_error("Fail to alloc samples by av_samples_alloc_array_and_samples."); } ret = swr_convert(audioConverter, cSamples, inputFrame-gt;nb_samples, (const uint8_t **)inputFrame-gt;extended_data, inputFrame-gt;nb_samples); if (ret lt; 0) { throw std::runtime_error("Fail to swr_convert."); } if (av_audio_fifo_space(audioFifo) lt; inputFrame-gt;nb_samples) { throw std::runtime_error("audio buffer is too small."); } ret = av_audio_fifo_write(audioFifo, (void **)cSamples, inputFrame-gt;nb_samples); if (ret lt; 0) { throw std::runtime_error("Fail to write fifo"); } av_freep(amp;cSamples[0]); av_frame_unref(inputFrame); av_packet_unref(inputPacket); while (av_audio_fifo_size(audioFifo) gt;= audioOutCodecCtx-gt;frame_size) { AVFrame *outputFrame = av_frame_alloc(); outputFrame-gt;nb_samples = audioOutCodecCtx-gt;frame_size; outputFrame-gt;channels = audioInCodecCtx-gt;channels; outputFrame-gt;channel_layout = av_get_default_channel_layout(audioInCodecCtx-gt;channels); outputFrame-gt;format = requireAudioFmt; outputFrame-gt;sample_rate = audioOutCodecCtx-gt;sample_rate; ret = av_frame_get_buffer(outputFrame, 0); assert(ret gt;= 0); ret = av_audio_fifo_read(audioFifo, (void **)outputFrame-gt;data, audioOutCodecCtx-gt;frame_size); assert(ret gt;= 0); outputFrame-gt;pts = audioFrameCount * audioOutStream-gt;time_base.den * audioOutCodecCtx-gt;frame_size / audioOutCodecCtx-gt;sample_rate; ret = avcodec_send_frame(audioOutCodecCtx, outputFrame); if (ret lt; 0) { throw std::runtime_error("Fail to send frame in encoding"); } av_frame_free(amp;outputFrame); ret = avcodec_receive_packet(audioOutCodecCtx, outputPacket); if (ret == AVERROR(EAGAIN)) { continue; } else if (ret lt; 0) { throw std::runtime_error("Fail to receive packet in encoding"); } outputPacket-gt;stream_index = audioOutStream-gt;index; outputPacket-gt;duration = audioOutStream-gt;time_base.den * audioOutCodecCtx-gt;frame_size / audioOutCodecCtx-gt;sample_rate; outputPacket-gt;dts = outputPacket-gt;pts = audioFrameCount * audioOutStream-gt;time_base.den * audioOutCodecCtx-gt;frame_size / audioOutCodecCtx-gt;sample_rate; audioFrameCount ; nextAudioPTS = outputPacket-gt;pts; // std::cerr lt;lt; "Audio::PTS (" lt;lt; nextAudioPTS lt;lt; ") timebase " lt;lt; audioOutStream-gt;time_base.num lt;lt; "/" lt;lt; audioOutStream-gt;time_base.den lt;lt; " real: " lt;lt; (outputPacket-gt;pts / (double)videoOutStream-gt;time_base.den) lt;lt; std::endl; ret = av_write_frame(outFormatCtx, outputPacket); av_packet_unref(outputPacket); av_frame_unref(outputFrame); } } } av_packet_free(amp;inputPacket); av_packet_free(amp;outputPacket); av_frame_free(amp;inputFrame); av_frame_free(amp;outputFrame); sws_freeContext(swsContext); printf("encode %lu audio packets in total.n", audioFrameCount); }
Когда я начинаю запись, я получаю ошибку * [swscaler] плохие указатели src*, и в результате получается полностью зеленое видео, за исключением первого кадра, который является окном, которое я записывал. После некоторой отладки я заметил, что проблема в avcodec_receive_frame (videoInCodecCtx, inputFrame)
том, что неправильно установлен inputFrame-gt; data
указатель, который полностью равен НУЛЮ, но я не знаю, как это исправить, есть идеи?
Комментарии:
1. Проверьте код ошибки
avcodec_send_packet
, который может вернутьсяEAGAIN
.2. Я проверил ошибку
av_read_frame
,avcodec_send_packet
иavcodec_receive_packet
. Когда захват кадра прерывается и я получаю плохую ошибку указателя src, возвращается только значениеav_read_frame
isEAGAIN
.avcodec_send_packet
Возвращает 0, в то времяavcodec_receive_frame
как возвращает -541478725. Есть идеи?
Ответ №1:
Я думал, что проблема изначально была в avcodec_receive_frame
том, но вместо этого была в av_read_frame
том, что вернулось EAGAIN
.
Теперь я исправляю, проверяя эту ошибку с if-statement
помощью, и когда EAGAIN
она возвращается, я делаю простое continue
. Теперь запись идет плавно!