gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 18:39:03 25 4
gpt4 key购买 nike

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

我已经能够在屏幕上显示帧甚至记录它们,但我的问题是音频和视频的记录。我决定创建一个更简单的测试程序,它只将流保存到文件而不在屏幕上显示帧,从 remuxing.c example 开始在 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));
}
}

av_interleaved_write_frame 返回视频数据包错误。结束文件只显示第一帧,但音频似乎没问题。

在控制台上打印的是:

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

在我看来,这似乎是一个需要解决的简单问题,但我对 ffmpeg API 真的一无所知,如果有人能引导我走向正确的方向,那就太好了!

谢谢!

最佳答案

您的问题似乎是 DirectShow 特有的。不幸的是,我无法访问带有 DirectShow 的系统,但从症状来看,捕获不是你的问题。错误的是 muxing 部分。可能是 AVI 不直接支持视频数据包的格式,也可能是数据包上的时间戳已损坏。

我会推荐一些你应该尝试的东西,一次一个:

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

我对 DirectShow 一无所知,我也不知道你的用例。所以我的建议集中在 FFmpeg API 上。其中一些可能矫枉过正/可能无法满足您的要求。

关于c++ - 使用 ffmpeg 从网络摄像头捕获帧和从 micro 捕获音频并保存到文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27463337/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com