【问题标题】:Raw H264 frames in mpegts container using libavcodec使用 libavcodec 的 mpegts 容器中的原始 H264 帧
【发布时间】:2011-08-23 06:38:52
【问题描述】:

非常感谢您对以下问题的帮助:

我有一个带摄像头的小工具,可以生成 H264 压缩视频帧,这些帧被发送到我的应用程序。这些帧不在容器中,只是原始数据。

我想用ffmpeg和libav函数创建一个视频文件,以后可以使用。

如果我解码帧,然后对它们进行编码,一切正常,我得到一个有效的视频文件。 (解码/编码步骤是常用的libav命令,这里没什么花哨的,我从万能的互联网上拿来的,它们坚如磐石)......但是,我在解码和编码上浪费了很多时间,所以我想跳过这一步,直接将帧放入输出流中。现在,问题来了。

这里是我想出的用于生成编码的代码:

AVFrame* picture;

avpicture_fill((AVPicture*) picture, (uint8_t*)frameData, 
                 codecContext->pix_fmt, codecContext->width,
                 codecContext->height);
int outSize = avcodec_encode_video(codecContext, videoOutBuf, 
                 sizeof(videoOutBuf), picture);
if (outSize > 0) 
{
    AVPacket packet;
    av_init_packet(&packet);
    packet.pts = av_rescale_q(codecContext->coded_frame->pts,
                  codecContext->time_base, videoStream->time_base);
    if (codecContext->coded_frame->key_frame) 
    {
        packet.flags |= PKT_FLAG_KEY;
    }
    packet.stream_index = videoStream->index;
    packet.data =  videoOutBuf;
    packet.size =  outSize;

    av_interleaved_write_frame(context, &packet);
    put_flush_packet(context->pb);
}

变量在哪里:

frameData 是解码后的帧数据,来自摄像头,在上一步中已解码,videoOutBuf 是用于保存数据的普通 uint8_t 缓冲区

我已经修改了应用程序,以便不对帧进行解码,而是简单地传递如下数据:

    AVPacket packet;
    av_init_packet(&packet);

    packet.stream_index = videoStream->index;
    packet.data = (uint8_t*)frameData;
    packet.size = currentFrameSize;

    av_interleaved_write_frame(context, &packet);
    put_flush_packet(context->pb);

在哪里

frameData 是原始 H264 帧 currentFrameSize 是原始 H264 帧的大小,即。我从小工具中获得的每一帧的字节数。

突然应用程序无法正常工作,制作的视频无法播放。这很明显,因为我没有为数据包设置正确的 PTS。我所做的是以下(我很绝望,你可以从这种方法中看到它:))

    packet.pts = timestamps[timestamp_counter ++];

timestamps 实际上是由上面的工作代码生成并写入文件的 PTS 列表(是的,你没看错,我记录了所有 PTS 的 10 分钟会话,并想使用它们) .

应用程序仍然无法运行。

现在,我不知道该怎么做,所以这是一个问题:

我想使用 libav 函数创建一个“mpegts”视频流,在流中插入已经编码的视频帧并用它创建一个视频文件。我该怎么做?

谢谢, f.

【问题讨论】:

    标签: c h.264 libavcodec libavformat


    【解决方案1】:

    您可以创建一个进程来从控制台调用 ffmpeg。

    用于处理 000001.jpg、000002.jpg、000003.jpg、...等文件的命令行示例

    ffmpeg -i c:\frames\%06d.jpg -r 16 -vcodec mpeg4 -an -y c:\video\some_video.avi

    Other examples from ffmpeg docs

    【讨论】:

      【解决方案2】:

      我相信如果你设置了以下,你会看到视频播放。

      packet.flags |= AV_PKT_FLAG_KEY;
      packet.pts = packet.dts = 0;
      

      您真的应该根据 h264 数据包标头设置 packet.flags。您可以尝试this fellow stack overflowian's 直接从流中提取的建议。

      如果您还要添加音频,那么 pts/dts 会更重要。我建议你学习this tutorial

      编辑

      我抽出时间从我的测试应用中提取出对我有用的东西。出于某种原因,0 的 dts/pts 值对我有用,但 0 或 AV_NOPTS_VALUE 以外的值不适用。我想知道我们是否有不同版本的ffmpeg。我有来自 git://git.videolan.org/ffmpeg.git 的最新消息。

      fftest.cpp

      #include <string>
      
      #ifndef INT64_C
      #define INT64_C(c) (c ## LL)
      #define UINT64_C(c) (c ## ULL)
      #endif
      
      //#define _M
      #define _M printf( "%s(%d) : MARKER\n", __FILE__, __LINE__ )
      
      extern "C"
      {
          #include "libavcodec/avcodec.h"
          #include "libavformat/avformat.h"
      };
      
      
      AVFormatContext *fc = 0;
      int vi = -1, waitkey = 1;
      
      // < 0 = error
      // 0 = I-Frame
      // 1 = P-Frame
      // 2 = B-Frame
      // 3 = S-Frame
      int getVopType( const void *p, int len )
      {   
          if ( !p || 6 >= len )
              return -1;
      
          unsigned char *b = (unsigned char*)p;
      
          // Verify NAL marker
          if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
          {   b++;
              if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
                  return -1;
          } // end if
      
          b += 3;
      
          // Verify VOP id
          if ( 0xb6 == *b )
          {   b++;
              return ( *b & 0xc0 ) >> 6;
          } // end if
      
          switch( *b )
          {   case 0x65 : return 0;
              case 0x61 : return 1;
              case 0x01 : return 2;
          } // end switch
      
          return -1;
      }
      
      void write_frame( const void* p, int len )
      {
          if ( 0 > vi )
              return;
      
          AVStream *pst = fc->streams[ vi ];
      
          // Init packet
          AVPacket pkt;
          av_init_packet( &pkt );
          pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0;   
          pkt.stream_index = pst->index;
          pkt.data = (uint8_t*)p;
          pkt.size = len;
      
          // Wait for key frame
          if ( waitkey )
              if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) )
                  return;
              else
                  waitkey = 0;
      
          pkt.dts = AV_NOPTS_VALUE;
          pkt.pts = AV_NOPTS_VALUE;
      
      //  av_write_frame( fc, &pkt );
          av_interleaved_write_frame( fc, &pkt );
      }
      
      void destroy()
      {
          waitkey = 1;
          vi = -1;
      
          if ( !fc )
              return;
      
      _M; av_write_trailer( fc );
      
          if ( fc->oformat && !( fc->oformat->flags & AVFMT_NOFILE ) && fc->pb )
              avio_close( fc->pb ); 
      
          // Free the stream
      _M; av_free( fc );
      
          fc = 0;
      _M; 
      }
      
      int get_nal_type( void *p, int len )
      {
          if ( !p || 5 >= len )
              return -1;
      
          unsigned char *b = (unsigned char*)p;
      
          // Verify NAL marker
          if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
          {   b++;
              if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
                  return -1;
          } // end if
      
          b += 3;
      
          return *b;
      }
      
      int create( void *p, int len )
      {
          if ( 0x67 != get_nal_type( p, len ) )
              return -1;
      
          destroy();
      
          const char *file = "test.avi";
          CodecID codec_id = CODEC_ID_H264;
      //  CodecID codec_id = CODEC_ID_MPEG4;
          int br = 1000000;
          int w = 480;
          int h = 354;
          int fps = 15;
      
          // Create container
      _M; AVOutputFormat *of = av_guess_format( 0, file, 0 );
          fc = avformat_alloc_context();
          fc->oformat = of;
          strcpy( fc->filename, file );
      
          // Add video stream
      _M; AVStream *pst = av_new_stream( fc, 0 );
          vi = pst->index;
      
          AVCodecContext *pcc = pst->codec;
      _M; avcodec_get_context_defaults2( pcc, AVMEDIA_TYPE_VIDEO );
          pcc->codec_type = AVMEDIA_TYPE_VIDEO;
      
          pcc->codec_id = codec_id;
          pcc->bit_rate = br;
          pcc->width = w;
          pcc->height = h;
          pcc->time_base.num = 1;
          pcc->time_base.den = fps;
      
          // Init container
      _M; av_set_parameters( fc, 0 );
      
          if ( !( fc->oformat->flags & AVFMT_NOFILE ) )
              avio_open( &fc->pb, fc->filename, URL_WRONLY );
      
      _M; av_write_header( fc );
      
      _M; return 1;
      }
      
      int main( int argc, char** argv )
      {
          int f = 0, sz = 0;
          char fname[ 256 ] = { 0 };
          char buf[ 128 * 1024 ];
      
          av_log_set_level( AV_LOG_ERROR );
          av_register_all();
      
          do
          {
              // Raw frames in v0.raw, v1.raw, v2.raw, ...
      //      sprintf( fname, "rawvideo/v%lu.raw", f++ );
              sprintf( fname, "frames/frame%lu.bin", f++ );
              printf( "%s\n", fname );
      
              FILE *fd = fopen( fname, "rb" );
              if ( !fd )
                  sz = 0;
              else
              {
                  sz = fread( buf, 1, sizeof( buf ) - FF_INPUT_BUFFER_PADDING_SIZE, fd );
                  if ( 0 < sz )
                  {
                      memset( &buf[ sz ], 0, FF_INPUT_BUFFER_PADDING_SIZE );          
      
                      if ( !fc )
                          create( buf, sz );
      
                      if ( fc )
                          write_frame( buf, sz );
      
                  } // end if
      
                  fclose( fd );
      
              } // end else
      
          } while ( 0 < sz );
      
          destroy();
      }
      

      【讨论】:

      • 抱歉,这不起作用 :( [libx264 @ 0x7fb9500243e0] 无效 DTS:PTS 小于 DTS [mpegts @ 0x7fb950021650] 应用程序提供的无效,非单调增加的 dts 到流 0:3600 中的复用器 > = 3600 [libx264 @ 0x7fb9500243e0] 非严格单调 PTS -9223372036854775808
      • 我看到失败是在 ./libavformat/utils.c:2965 你能试试packet.pts = packet.dts = AV_NOPTS_VALUE 来躲避那个错误吗?我现在意识到这可能仍然不够,因为您正在处理 raw H264 数据包。您的流中是否有所有标题或只有 VOP...即...您的所有帧是否都以 0x000001b6 开头?您能否发布用于初始化视频流的代码?\
      • 帧开始:不,第一帧从 0x00 00 00 01 67 42 00 1E 8D 68 1E 0B FE 00 00 00 01 68 CE 开始,以下是 0x00 00 00 01 61 E0。初始化视频流的代码位于:typewith.me/eHbYEyUS91 ...。如果对您有帮助,我可以将二进制帧放在某个地方...您需要它们吗?
      • 0x00000167 是 SPS 而 0x00000168 是 PPS,整个事情(00 00 00 01 67 42 00 1E 8D 68 1E 0B FE 00 00 00 01 68 CE)应该设置在 extradata 字段中。如果你想压缩页眉和几帧,我将更改上面的代码。
      • 代码基本上对我有用,除了文件读取缓冲区太小。我得到了一个很好的键盘视频 :) 不过,关键帧没有被正确检测到,所以我修改了它。我不确定 FFMpeg 中是否有内置函数来进行关键帧检测,会很好。我在设置额外数据方面显然也不正确,当我这样做时,FFMpeg 仍然拒绝提取视频参数。当我有时间时,我将不得不对此进行更多研究,因为我认为我自己最终将需要类似的功能。必须直接解码 SEI 会很糟糕。
      猜你喜欢
      • 2016-07-10
      • 2014-02-06
      • 2013-12-03
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 2013-12-30
      • 2015-03-08
      • 2012-09-17
      相关资源
      最近更新 更多