swscaler — плохие указатели на изображения src для macOS

#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 is EAGAIN . avcodec_send_packet Возвращает 0, в то время avcodec_receive_frame как возвращает -541478725. Есть идеи?

Ответ №1:

Я думал, что проблема изначально была в avcodec_receive_frame том, но вместо этого была в av_read_frame том, что вернулось EAGAIN .

Теперь я исправляю, проверяя эту ошибку с if-statement помощью, и когда EAGAIN она возвращается, я делаю простое continue . Теперь запись идет плавно!