2014-12-13 1 views
5

За последние несколько недель я боролся с API-интерфейсом ffmpeg, так как я не могу найти четкую документацию, и мне также сложно найти все решения, которые я нахожу в Интернете, включают не API c, а программу командной строки ffmpeg.c. Я создаю программу, которая должна захватывать видео с веб-камеры и аудио, показывать кадры на экране и записывать как аудио, так и кадры в видеофайл. Я также использую QT в качестве основы для этого проекта.Использование ffmpeg для захвата фреймов с веб-камеры и звука из микро и сохранения в файл

Я смог показать кадры на экране и даже записать их, но моя проблема - запись как аудио, так и видео. Я решил создать более простую программу для тестов, которая только сохраняет поток в файл, не показывая кадры на экране, начиная с 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, если кто-то может привести меня в правильном направлении это было бы прекрасно!

Спасибо!

ответ

1

Ваша проблема, кажется, несколько специфична для DirectShow. К сожалению, у меня нет доступа к системе с DirectShow, но из-за симптома похоже, что захват не является вашей проблемой. Что не так, это мультиплексирующая часть. Может быть, формат видеопакетов напрямую не поддерживается в AVI, или могут быть временные метки на пакетах.

Я рекомендую несколько вещей, которые вы должны попробовать, по одному за раз:

  • Попробуйте использовать av_write_frame вместо av_interleaved_write_frame.
  • Используйте лучший контейнер, например MP4 или MKV.
  • Не пытайтесь подключить входной пакет к AVI-файлу. В grabFrames возьмите необработанные видеопакеты и выгрузите их в файл. Это должно дать вам файл, который можно воспроизвести с помощью ffplay. (Вероятно, вам нужно будет указать разрешение, формат и формат изображения в команде ffplay.)
  • Выведенный выше результат в воспроизводимом видеофайле? Если да, то я рекомендую вам декодировать отдельные видеопакеты, конвертировать цветовое пространство и кодировать их с помощью общего кодека. (Я рекомендую yuv420p в h264.) База кода FFmpeg имеет два примера, которые должны быть полезны - demuxing_decoding.c и decoding_encoding.c. Это должно дать вам правильный видеофайл. (Играется большинством игроков.)

Я ничего не знаю о DirectShow, и я не знаю вашего прецедента. Поэтому мои рекомендации сосредоточены на API FFmpeg. Некоторые из них могут быть чрезмерными/могут не делать то, что вы хотите.

+0

Благодарим вас за ответ. Мне удалось решить проблему, посмотрев примеры muxing.c и transcoding.c в документах ffmpeg. У меня теперь есть проблема с синхронизацией видео и аудио, а также с уменьшением использования ЦП, но я задал эти вопросы в другом потоке. – Solidus

+0

Убедитесь, что вы правильно задали time_base, pts и dts. Возможно, вам придется установить time_base как на уровне контейнера, так и на кодеке. Если вы перекодируете/трансмуксируете, используйте av_rescale_q для преобразования временных меток. – Shakkhar

+0

Я считаю, что делаю это правильно, по крайней мере, согласно примерам в ffmpeg. Я разместил этот вопрос здесь http://stackoverflow.com/questions/31617598/ffmpeg-api-recording-video-and-audio-syncing-problems – Solidus

Смежные вопросы