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

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

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

//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  #include  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)) oformat; for (i = 0; i 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 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) { 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() <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返回video数据包的错误。 结束文件只显示第一帧,但音频似乎没问题。

在控制台上,这是打印的内容:

 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的系统,但从症状看起来捕获不是你的问题。 有什么问题是复用部分。 可能是AVI中不直接支持video数据包的格式,或者可能是数据包上的时间戳被破坏。

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

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

我对DirectShow一无所知,也不知道你的用例。 所以我的建议主要关注FFmpeg API。 其中一些可能是矫枉过正/可能不会做你想要的。