C++ 使用ffmpeg从网络摄像头捕获帧,从micro捕获音频并保存到文件

C++ 使用ffmpeg从网络摄像头捕获帧,从micro捕获音频并保存到文件,c++,c,qt,video,ffmpeg,C++,C,Qt,Video,Ffmpeg,在过去的几周里,我一直在努力使用ffmpeg API,因为我找不到清晰的文档,而且我也发现很难搜索,因为我在网上找到的所有解决方案都涉及ffmpeg.c命令行程序,而不是c API。我正在创建一个程序,需要从网络摄像头和音频捕获视频,在屏幕上显示帧,并将音频和帧记录到视频文件中。我也使用QT作为这个项目的框架 我已经能够在屏幕上显示帧,甚至录制它们,但我的问题是音频和视频的录制。我决定创建一个更简单的测试程序,从ffmpeg文档中的开始,只将流保存到文件中,而不在屏幕上显示帧。我的代码如下: /

在过去的几周里,我一直在努力使用ffmpeg API,因为我找不到清晰的文档,而且我也发现很难搜索,因为我在网上找到的所有解决方案都涉及ffmpeg.c命令行程序,而不是c API。我正在创建一个程序,需要从网络摄像头和音频捕获视频,在屏幕上显示帧,并将音频和帧记录到视频文件中。我也使用QT作为这个项目的框架

我已经能够在屏幕上显示帧,甚至录制它们,但我的问题是音频和视频的录制。我决定创建一个更简单的测试程序,从ffmpeg文档中的开始,只将流保存到文件中,而不在屏幕上显示帧。我的代码如下:

//This is the variables on the .h
AVOutputFormat *ofmt;
AVFormatContext *ifmt_ctx, *ofmt_ctx;

QString cDeviceName;
QString aDeviceName;

int audioStream, videoStream;
bool done;

//The .cpp
#include "cameratest.h"
#include <QtConcurrent/QtConcurrent>
#include <QDebug>

CameraTest::CameraTest(QString cDeviceName, QString aDeviceName, QObject *parent) :
    QObject(parent)
{
    done = false;
    this->cDeviceName = cDeviceName;
    this->aDeviceName = aDeviceName;
    av_register_all();
    avdevice_register_all();
}

void CameraTest::toggleDone() {
    done = !done;
}

int CameraTest::init() {
    ofmt = NULL;
    ifmt_ctx = NULL;
    ofmt_ctx = NULL;

    QString fullDName = cDeviceName.prepend("video=") + ":" + aDeviceName.prepend("audio="); 
    qDebug() << fullDName;
    AVInputFormat *fmt = av_find_input_format("dshow");

    int ret, i;

    if (avformat_open_input(&ifmt_ctx, fullDName.toUtf8().data(), fmt, NULL) < 0) {
       fprintf(stderr, "Could not open input file '%s'", fullDName.toUtf8().data());
       return -1;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
       fprintf(stderr, "Failed to retrieve input stream information");
       return -1;
    }
    av_dump_format(ifmt_ctx, 0, fullDName.toUtf8().data(), 0);
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, "test.avi");
    if (!ofmt_ctx) {
       fprintf(stderr, "Could not create output context\n");
       ret = AVERROR_UNKNOWN;
       return -1;
    }
    ofmt = ofmt_ctx->oformat;

    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
       AVStream *in_stream = ifmt_ctx->streams[i];
       AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);

       if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
           videoStream = i;
       }
       else if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
           audioStream = i;
       }

       if (!out_stream) {
           fprintf(stderr, "Failed allocating output stream\n");
           ret = AVERROR_UNKNOWN;
           return -1;
       }
       ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
       if (ret < 0) {
           fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
           return -1;
       }
       out_stream->codec->codec_tag = 0;
       if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
           out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
    av_dump_format(ofmt_ctx, 0, "test.avi", 1);
    if (!(ofmt->flags & AVFMT_NOFILE)) {
       ret = avio_open(&ofmt_ctx->pb, "test.avi", AVIO_FLAG_WRITE);
       if (ret < 0) {
           fprintf(stderr, "Could not open output file '%s'", "test.avi");
           return -1;
       }
    }
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
       fprintf(stderr, "Error occurred when opening output file\n");
       return -1;
    }
    QtConcurrent::run(this, &CameraTest::grabFrames);
    return 0;
}

void CameraTest::grabFrames() {
    AVPacket pkt;
    int ret;
    while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
        AVStream *in_stream, *out_stream;
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        /* copy packet */
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        int ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
           qDebug() << "Error muxing packet";
           //break;
        }
        av_free_packet(&pkt);

        if(done) break;
    }
    av_write_trailer(ofmt_ctx);

    avformat_close_input(&ifmt_ctx);
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
       avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        //return -1;
       //fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
    }
}
在我看来,这似乎是一个需要解决的简单问题,但我对ffmpeg API几乎一无所知,如果有人能引导我走向正确的方向,那就太好了


谢谢

您的问题似乎有点特定于DirectShow。不幸的是,我无法使用DirectShow访问系统,但从症状来看,捕获似乎不是您的问题。错的是木星部分。可能是AVI中不直接支持视频包的格式,或者可能是包上的时间戳被破坏

我将推荐几件您应该尝试的事情,一次一件:

  • 尝试使用
    av_write_frame
    而不是
    av_interleaved_write_frame
  • 使用更好的容器,如MP4或MKV
  • 不要尝试将输入数据包多路复用到avi文件。在
    grabFrames
    中,获取原始视频包并将其转储到文件中。这将为您提供一个可由ffplay播放的文件。(您可能需要在ffplay命令中指定分辨率、像素格式和格式。)
  • 上述结果是否产生了可播放的视频文件?如果是的话,我建议您对单个视频包进行解码,转换颜色空间并使用通用编解码器对其进行编码。(我推荐h264中的yuv420p。)FFmpeg代码库有两个示例,它们应该很有用-
    解复用\解码.c
    解码\编码.c
    。这会给你一个合适的视频文件。(大多数玩家都可以玩。)

我对DirectShow一无所知,也不知道您的用例。因此,我的建议侧重于FFmpeg API。其中一些可能有些过火/可能做不到您想要的。

谢谢您的回答。通过查看ffmpeg文档中的muxing.c和transcoding.c示例,我成功地解决了这个问题。我现在在同步视频和音频以及降低CPU使用率方面遇到了一些问题,但我在不同的线程中提出了这些问题。请确保正确设置了时基、pts和dts。您可能需要在容器和编解码器级别设置时间基准。如果您正在转码/转换移动,请使用av_rescale_q转换时间戳。我相信我这样做是正确的,至少根据ffmpeg中的示例。我已经把这个问题贴在这里了
Input #0, dshow, from 'video=Integrated Camera:audio=Microfone interno (Conexant 206':
  Duration: N/A, start: 146544.738000, bitrate: 1411 kb/s
    Stream #0:0: Video: rawvideo, bgr24, 640x480, 30 tbr, 10000k tbn, 30 tbc
    Stream #0:1: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
Output #0, avi, to 'test.avi':
    Stream #0:0: Video: rawvideo, bgr24, 640x480, q=2-31, 30 tbc
    Stream #0:1: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s

[avi @ 0089f660] Using AVStream.codec.time_base as a timebase hint to the muxer is deprecated. Set AVStream.time_base instead.
[avi @ 0089f660] Using AVStream.codec.time_base as a timebase hint to the muxer is deprecated. Set AVStream.time_base instead.
[avi @ 0089f660] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 4396365 >= 4396365
[avi @ 0089f660] Too large number of skipped frames 4396359 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396360 > 60000
[avi @ 0089f660] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 4396390 >= 4396390
[avi @ 0089f660] Too large number of skipped frames 4396361 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396362 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396364 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396365 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396366 > 60000
[avi @ 0089f660] Too large number of skipped frames 4396367 > 60000