关于ffmpeg分离mp4视频帧硬解码遇到的问题

    前段时间在WINCE6搞好一阵时间利用ffmpeg分离mp4出来的视频帧送到硬件解码去,但发现不少问题,我用的是三星的S5PC100处理器,想利用三星提供的MFC的API来进行硬解码,但是根本无法解码,根据调试输出的信息,少了信息头数据,上网查找了好久后,发现mp4/mkv/mov/flv封装的h.264,因为为了减少存储,减少了一些头信息,不是标准的ES流,而从av_read_frame出来的码流一般都是完整的一帧数据的

 

      经过研究的ffmpeg的原代码发现,一般视频帧的信息头数据都会保存在AVCodecContext结构中的extradata ,extradata_size的变量中,  如果是MPEG4编码的话,直接把extradata加在经过av_read_frame分离的视频数据帧前面就可以硬解码了,若是H264的话,我是运用如下函数得到标准ES流的.

 #define EINVAL          22
#define ENOMEM          12
#define AV_RB16(x)  ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1])
#define AV_RB32(x)  ((((const uint8_t*)(x))[0] << 24) | /
 (((const uint8_t*)(x))[1] << 16) | /
 (((const uint8_t*)(x))[2] <<  8) | /
 ((const uint8_t*)(x))[3])

#define AV_RB32(x)  ((((const uint8_t*)(x))[0] << 24) | /
 (((const uint8_t*)(x))[1] << 16) | /
 (((const uint8_t*)(x))[2] <<  8) | /
 ((const uint8_t*)(x))[3])

#define AV_WB32(p, d) do { /
 ((uint8_t*)(p))[3] = (d); /
 ((uint8_t*)(p))[2] = (d)>>8; /
 ((uint8_t*)(p))[1] = (d)>>16; /
 ((uint8_t*)(p))[0] = (d)>>24; } while(0)

 

typedef struct H264BSFContext {
 uint8_t  length_size;
 uint8_t  first_idr;
 uint8_t *sps_pps_data;
 uint32_t size;
} H264BSFContext;

 

static void alloc_and_copy(uint8_t **poutbuf,          int *poutbuf_size,
         const uint8_t *sps_pps, uint32_t sps_pps_size,
         const uint8_t *in,      uint32_t in_size)
{
 uint32_t offset = *poutbuf_size;
 uint8_t nal_header_size =  4;

 *poutbuf_size += sps_pps_size+in_size+nal_header_size;
 *poutbuf = (unsigned char *)av_realloc(*poutbuf, *poutbuf_size);
    if (*poutbuf==NULL)
        return ;
   
 if (sps_pps)
  memcpy(*poutbuf+offset, sps_pps, sps_pps_size);
 memcpy(*poutbuf+sps_pps_size+nal_header_size+offset, in, in_size);
 //if (!offset)
  AV_WB32(*poutbuf+offset+sps_pps_size, 1);
 //else
 //{
 // (*poutbuf+offset+sps_pps_size)[0] = (*poutbuf+offset+sps_pps_size)[1] = 0;
 // (*poutbuf+offset+sps_pps_size)[2] = 1;
 //}
}

static int h264_mp4toannexb_filter(H264BSFContext *ctx,  AVCodecContext *avctx, const char *args,
           uint8_t  **poutbuf, int *poutbuf_size,
           const uint8_t *buf, int      buf_size,
           int keyframe)
{

 uint8_t unit_type;
 uint32_t nal_size, cumul_size = 0;

 /* nothing to filter */
 if (!avctx->extradata || avctx->extradata_size < 6)
 {
  *poutbuf = (uint8_t*) buf;
  *poutbuf_size = buf_size;
  return 0;
 }

 /* retrieve sps and pps NAL units from extradata */
 if (!ctx->sps_pps_data)
 {
  uint16_t unit_size;
  uint32_t total_size = 0;
  uint8_t *out = NULL, unit_nb, sps_done = 0;
  const uint8_t *extradata = avctx->extradata+4;
  static const uint8_t nalu_header[4] = {0, 0, 0, 1};

  /* retrieve length coded size */
  ctx->length_size = (*extradata++ & 0x3) + 1;
  if (ctx->length_size == 3)
   return AVERROR(EINVAL);

  /* retrieve sps and pps unit(s) */
  unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
  if (!unit_nb)
  {
   unit_nb = *extradata++; /* number of pps unit(s) */
   sps_done++;
  }
  while (unit_nb--)
  {
   unit_size = AV_RB16(extradata);
   total_size += unit_size+4;
   if (extradata+2+unit_size > avctx->extradata+avctx->extradata_size)
   {
    av_free(out);
    return AVERROR(EINVAL);
   }
   out = (unsigned char *)av_realloc(out, total_size);
   if (!out)
    return AVERROR(ENOMEM);
   memcpy(out+total_size-unit_size-4, nalu_header, 4);
   memcpy(out+total_size-unit_size,   extradata+2, unit_size);
   extradata += 2+unit_size;

   if (!unit_nb && !sps_done++)
    unit_nb = *extradata++; /* number of pps unit(s) */
  }

  ctx->sps_pps_data = out;
  ctx->size = total_size;
  ctx->first_idr = 1;
  //char str[512]={0};
  //                           memcpy(str, out, total_size);
 }
 *poutbuf_size = 0;
 *poutbuf = NULL;
 do
 {
  if (ctx->length_size == 1)
   nal_size = buf[0];
  else if (ctx->length_size == 2)
   nal_size = AV_RB16(buf);
  else
  {
   nal_size = AV_RB32(buf);

   
  }

  buf += ctx->length_size;
  unit_type = *buf & 0x1f;

  /* prepend only to the first type 5 NAL unit of an IDR picture */
  if ( unit_type != 6 )
  {
   alloc_and_copy(poutbuf, poutbuf_size, ctx->sps_pps_data, ctx->size, buf, nal_size);
   ctx->first_idr = 0;
  }
  //else
  //{
  // alloc_and_copy(poutbuf, poutbuf_size, NULL, 0,  buf, nal_size);
  // if (!ctx->first_idr && unit_type == 1)
  //  ctx->first_idr = 1;
  //}

  buf += nal_size;
  cumul_size += nal_size + ctx->length_size;
 } while (cumul_size < buf_size);

 return 1;
}

 

通过调用 h264_mp4toannexb_filter函数后就可以得到标准的ES流了,先定义一个H264BSFContext变量,然后传给h264_mp4toannexb_filter函数,

如: 

 H264BSFContext *ctx=(H264BSFContext *) malloc(sizeof(H264BSFContext));
  memset(ctx, 0, sizeof(H264BSFContext));

  while(av_read_frame(pFormatCtx, &packet) >= 0)
  {    

       if(packet.stream_index==videoStream)
      { 
 
          unsigned char *lp=NULL;   //输入的指针
           int len;
           h264_mp4toannexb_filter(ctx, pCodecCtx, NULL, &lp, &len, packet.data, packet.size, 1);
           memcpy((LPBYTE)virInBuf, pCodecCtx->extradata, pCodecCtx->extradata_size);

       }

  }

硬件就可能解码了,这个我是在S5PC100测试成功的,如果是S3C6410应该是类似的,如果是avi文件很容易了,通过av_read_frame直接把它送硬件解码器进行解码就行了

### FFmpeg 6.0 中使用 Qt 实现码并将拉流和码功能分配到不同线程的决方案 以下内容详细介绍了如何在 FFmpeg 6.0 中结合 Qt 使用多线程实现视频拉流与码功能分离。通过将拉流和码分配到不同线程,可以有效避免主线程阻塞,提升应用性能。 --- #### 1. 环境准备 确保已安装以下依赖项: - FFmpeg 6.0(支持件加速的版本) - Qt 5 或更高版本 - 目标件驱动程序(如 NVIDIA CUDA 驱动) --- #### 2. 示例代码:FFmpeg 6.0 + Qt 多线程实现拉流与码 以下是一个完整的示例代码,展示如何在 FFmpeg 6.0 中结合 Qt 使用多线程实现拉流与码功能分离。 ```cpp #include <QApplication> #include <QMainWindow> #include <QImage> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <QThread> #include <QDebug> class VideoDecoder : public QObject { Q_OBJECT public: explicit VideoDecoder(QObject *parent = nullptr) : QObject(parent), m_formatCtx(nullptr), m_codecCtx(nullptr), m_frame(nullptr), m_swsCtx(nullptr) {} void startDecoding(const QString &amp;filePath) { m_filePath = filePath; m_thread = new QThread(); moveToThread(m_thread); connect(m_thread, &amp;QThread::started, this, &amp;VideoDecoder::decodeVideo); connect(this, &amp;VideoDecoder::frameDecoded, this, &amp;VideoDecoder::processFrame, Qt::QueuedConnection); m_thread->start(); } private: QString m_filePath; AVFormatContext *m_formatCtx; AVCodecContext *m_codecCtx; AVFrame *m_frame; SwsContext *m_swsCtx; QThread *m_thread; void decodeVideo() { if (avformat_open_input(&amp;m_formatCtx, m_filePath.toStdString().c_str(), nullptr, nullptr) != 0) { qWarning() << "Could not open input file"; return; } if (avformat_find_stream_info(m_formatCtx, nullptr) < 0) { qWarning() << "Failed to retrieve input stream information"; avformat_close_input(&amp;m_formatCtx); return; } int videoStreamIndex = -1; for (unsigned int i = 0; i < m_formatCtx->nb_streams; i++) { if (m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { qWarning() << "Did not find a video stream"; avformat_close_input(&amp;m_formatCtx); return; } AVCodecParameters *codecParams = m_formatCtx->streams[videoStreamIndex]->codecpar; const AVCodec *codec = avcodec_find_decoder(codecParams->codec_id); if (!codec) { qWarning() << "Codec not found"; avformat_close_input(&amp;m_formatCtx); return; } m_codecCtx = avcodec_alloc_context3(codec); if (!m_codecCtx) { qWarning() << "Failed to allocate codec context"; avformat_close_input(&amp;m_formatCtx); return; } if (avcodec_parameters_to_context(m_codecCtx, codecParams) < 0) { qWarning() << "Failed to copy codec parameters to codec context"; avcodec_free_context(&amp;m_codecCtx); avformat_close_input(&amp;m_formatCtx); return; } if (avcodec_open2(m_codecCtx, codec, nullptr) < 0) { qWarning() << "Failed to open codec"; avcodec_free_context(&amp;m_codecCtx); avformat_close_input(&amp;m_formatCtx); return; } m_frame = av_frame_alloc(); if (!m_frame) { qWarning() << "Failed to allocate frame"; avcodec_free_context(&amp;m_codecCtx); avformat_close_input(&amp;m_formatCtx); return; } AVPacket packet; while (av_read_frame(m_formatCtx, &amp;packet) >= 0) { if (packet.stream_index == videoStreamIndex) { int ret = avcodec_send_packet(m_codecCtx, &amp;packet); if (ret < 0) { qWarning() << "Error sending a packet for decoding"; break; } while (ret >= 0) { ret = avcodec_receive_frame(m_codecCtx, m_frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { qWarning() << "Error during decoding"; break; } emit frameDecoded(QImage()); } } av_packet_unref(&amp;packet); } av_frame_free(&amp;m_frame); avcodec_free_context(&amp;m_codecCtx); avformat_close_input(&amp;m_formatCtx); } void processFrame(const QImage &amp;image) { // 在主线程中处理码后的帧 qDebug() << "Frame processed in main thread"; } signals: void frameDecoded(QImage image); }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QMainWindow window; QLabel label(&amp;window); label.setScaledContents(true); label.setGeometry(0, 0, 800, 600); VideoDecoder decoder; QObject::connect(&amp;decoder, &amp;VideoDecoder::frameDecoded, [&amp;label](const QImage &amp;image) { label.setPixmap(QPixmap::fromImage(image)); }); decoder.startDecoding("input.mp4"); window.show(); return app.exec(); } #include "main.moc" ``` --- #### 3. 关键点析 1. **多线程设计** 拉流和码操作被放置在独立的线程中执行,避免阻塞主线程。通过 `QThread` 和信号槽机制实现线程间通信[^2]。 2. **FFmpeg 初始化** 使用 `AVFormatContext` 打开输入文件或流,并通过 `avcodec_find_decoder` 查找合适的码器[^3]。 3. **资源释放** 在码完成后,必须正确释放所有分配的资源,包括 `AVPacket`、`AVFrame` 和 `AVCodecContext`。 4. **信号槽机制** 码后的帧通过信号槽机制传递到主线程进行处理和显示,确保 UI 更新的安全性。 --- #### 4. 注意事项 - 硬解码依赖于件支持,确保目标设备驱动程序已正确安装。 - 根据目标平台选择合适的硬解码器名称。 - 在多线程环境中使用 FFmpeg 时,需注意线程安全问题。 --- ###
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值