重复ffmpeg流(libavcodec / libavformat)

时间:2017-08-05 20:24:47

标签: c++ video ffmpeg

我正在使用ffmpeg的各种API在我的应用程序中绘制视频。到目前为止,这非常有效。因为我也有GIF,我想循环而不必一遍又一遍地加载文件。

在我的代码中,解码器循环如下所示:

 AVPacket packet = {};
 av_init_packet(&packet);
 while (mIsRunning) {
     int error = av_read_frame(mContext, &packet);
     if (error == AVERROR_EOF) {
         if(mRepeat) {
             logger.info("EOF-repeat");
             auto stream = mContext->streams[mVideoStream];
             av_seek_frame(mContext, mVideoStream, 0, 0);
             continue;
         }
         if (mReadVideo) {
             avcodec_send_packet(mVideoCodec, nullptr);
         }
         if (mReadAudio) {
             avcodec_send_packet(mAudioCodec, nullptr);
         }
         break;
     }

     if (error < 0) {
         char err[AV_ERROR_MAX_STRING_SIZE];
         av_make_error_string(err, AV_ERROR_MAX_STRING_SIZE, error);
         logger.error("Failed to read next frame from stream: ", err);
         throw std::runtime_error("Stream reading failed");
     }

     if (packet.stream_index == mVideoStream && mReadVideo) {
         int32 err;
         {
             std::lock_guard<std::mutex> l(mVideoLock);
             err = avcodec_send_packet(mVideoCodec, &packet);
         }
         mImageEvent.notify_all();
         while (err == AVERROR(EAGAIN) && mIsRunning) {
             {
                 std::unique_lock<std::mutex> l(mReaderLock);
                 mReaderEvent.wait(l);
             }
             {
                 std::lock_guard<std::mutex> l(mVideoLock);
                 err = avcodec_send_packet(mVideoCodec, &packet);
             }
        } 
     }

     av_packet_unref(&packet);
 }

将视频读到最后的效果非常好,如果我没有将mRepeat设置为true,则可以正确显示EOF并停止解析。但是,当我使用循环时,会发生以下情况:

  • 视频结束
  • AVERROR_EOF发生在av_read_frame
  • EOF-repeat已打印
  • 从流中读取随机帧(并呈现)
  • AVERROR_EOF发生在av_read_frame
  • EOF-repeat已打印
  • 从流中读取随机帧(并呈现)
  • ...

你可以想象它就像我有一个旋转地球仪的GIF,经过一整圈之后,它开始随机跳跃,有时是正常的一小部分,有时是向后,有时只是随机到处。

我还尝试了avformat_seek_file的几个版本,还有什么方法可以将所有内容重置为开头并从头开始重新开始?

1 个答案:

答案 0 :(得分:5)

我发现我还需要将我的IO上下文重置为开头:

if(mRepeat) {
    auto stream = mContext->streams[mVideoStream];
    avio_seek(mContext->pb, 0, SEEK_SET);
    avformat_seek_file(mContext, mVideoStream, 0, 0, stream->duration, 0);
    continue;
}

现在视频正常循环:)