Berechnen PTS und DTS richtig zu synchronisieren audio und video ffmpeg-C++

Ich versuche zu muxen von H264-kodierten Daten und G711-PCM-Daten in mov multimedia-container. Ich Schaffe AVPacket von kodierten Daten und zunächst die PTS-und DTS-Wert von video - /audio-frames entspricht AV_NOPTS_VALUE. So rechnete ich die DTS mit aktuellen Zeit-information. Mein code -

bool AudioVideoRecorder::WriteVideo(const unsigned char *pData, size_t iDataSize, bool const bIFrame) {
    .....................................
    .....................................
    .....................................
    AVPacket pkt = {0};
    av_init_packet(&pkt);
    int64_t dts = av_gettime();
    dts = av_rescale_q(dts, (AVRational){1, 1000000}, m_pVideoStream->time_base);
    int duration = 90000 / VIDEO_FRAME_RATE;
    if(m_prevVideoDts > 0LL) {
        duration = dts - m_prevVideoDts;
    }
    m_prevVideoDts = dts;

    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = m_currVideoDts;
    m_currVideoDts += duration;
    pkt.duration = duration;
    if(bIFrame) {
        pkt.flags |= AV_PKT_FLAG_KEY;
    }
    pkt.stream_index = m_pVideoStream->index;
    pkt.data = (uint8_t*) pData;
    pkt.size = iDataSize;

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt);

    if(ret < 0) {
        LogErr("Writing video frame failed.");
        return false;
    }

    Log("Writing video frame done.");

    av_free_packet(&pkt);
    return true;
}

bool AudioVideoRecorder::WriteAudio(const unsigned char *pEncodedData, size_t iDataSize) {
    .................................
    .................................
    .................................
    AVPacket pkt = {0};
    av_init_packet(&pkt);

    int64_t dts = av_gettime();
    dts = av_rescale_q(dts, (AVRational){1, 1000000}, (AVRational){1, 90000});
    int duration = AUDIO_STREAM_DURATION; //20
    if(m_prevAudioDts > 0LL) {
        duration = dts - m_prevAudioDts;
    }
    m_prevAudioDts = dts;
    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = m_currAudioDts;
    m_currAudioDts += duration;
    pkt.duration = duration;

    pkt.stream_index = m_pAudioStream->index;
    pkt.flags |= AV_PKT_FLAG_KEY;
    pkt.data = (uint8_t*) pEncodedData;
    pkt.size = iDataSize;

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt);
    if(ret < 0) {
        LogErr("Writing audio frame failed: %d", ret);
        return false;
    }

    Log("Writing audio frame done.");

    av_free_packet(&pkt);
    return true;
}

Und ich habe stream wie diese -

AVStream* AudioVideoRecorder::AddMediaStream(enum AVCodecID codecID) {
    ................................
    .................................   
    pStream = avformat_new_stream(m_pFormatCtx, codec);
    if (!pStream) {
        LogErr("Could not allocate stream.");
        return NULL;
    }
    pStream->id = m_pFormatCtx->nb_streams - 1;
    pCodecCtx = pStream->codec;
    pCodecCtx->codec_id = codecID;

    switch(codec->type) {
    case AVMEDIA_TYPE_VIDEO:
        pCodecCtx->bit_rate = VIDEO_BIT_RATE;
        pCodecCtx->width = PICTURE_WIDTH;
        pCodecCtx->height = PICTURE_HEIGHT;
        pStream->time_base = (AVRational){1, 90000};
        pStream->avg_frame_rate = (AVRational){90000, 1};
        pStream->r_frame_rate = (AVRational){90000, 1}; //though the frame rate is variable and around 15 fps
        pCodecCtx->pix_fmt = STREAM_PIX_FMT;
        m_pVideoStream = pStream;
        break;

    case AVMEDIA_TYPE_AUDIO:
        pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
        pCodecCtx->bit_rate = AUDIO_BIT_RATE;
        pCodecCtx->sample_rate = AUDIO_SAMPLE_RATE;
        pCodecCtx->channels = 1;
        m_pAudioStream = pStream;
        break;

    default:
        break;
    }

    /* Some formats want stream headers to be separate. */
    if (m_pOutputFmt->flags & AVFMT_GLOBALHEADER)
        m_pFormatCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;

    return pStream;
}

Gibt es mehrere Probleme mit dieser Berechnung:

  1. Das video ist laggy und hinkt als audio-zunehmend mit der Zeit.

  2. Angenommen, ein audio-frame empfangen wird (WriteAudio(..)) wenig Zeit wie 3 Sekunden, dann der spät-frame gestartet werden soll das Spiel mit 3 Sekunden Verzögerung, aber das ist es nicht. Die verzögerte frame gespielt wird nacheinander mit dem vorherigen frame.

  3. Manchmal zeichnete ich für ~40 Sekunden, aber die Dauer der Datei ist viel wie 2 Minuten, aber audio - /video-gespielt wird nur wenige Momente von 40 Sekunden und der rest der Datei enthält nichts und die seekbar-springt in de sofort nach 40 Sekunden (getestet in VLC).

EDIT:

Laut Ronald S. Bultje Vorschlag, was ich habe, verstehen:

m_pAudioStream->time_base = (AVRational){1, 9000}; //actually no need to set as 9000 is already default value for audio as you said
m_pVideoStream->time_base = (AVRational){1, 9000};

gesetzt werden sollte, als nun sowohl audio-und video-streams sind nun in der gleichen Zeit, die Basis-Einheiten.

Und für video:

...................
...................

int64_t dts = av_gettime(); //get current time in microseconds
dts *= 9000; 
dts /= 1000000; //1 second = 10^6 microseconds
pkt.pts = AV_NOPTS_VALUE; //is it okay?
pkt.dts = dts;
//and no need to set pkt.duration, right?

Und für audio: (genau die gleichen wie video, oder?)

...................
...................

int64_t dts = av_gettime(); //get current time in microseconds
dts *= 9000; 
dts /= 1000000; //1 second = 10^6 microseconds
pkt.pts = AV_NOPTS_VALUE; //is it okay?
pkt.dts = dts;
//and no need to set pkt.duration, right?

Und ich denke, Sie sind jetzt wie teilen currDts, richtig? Bitte korrigieren Sie mich, wenn ich falsch bin überall oder etwas fehlt.

Auch, wenn ich video stream Zeit-Basis als (AVRational){1, frameRate} - und audio-stream-Zeit-Basis als (AVRational){1, sampleRate}, wie der richtige code Aussehen sollte?

EDIT 2.0:

m_pAudioStream->time_base = (AVRational){1, VIDEO_FRAME_RATE};
m_pVideoStream->time_base = (AVRational){1, VIDEO_FRAME_RATE};

Und

bool AudioVideoRecorder::WriteAudio(const unsigned char *pEncodedData, size_t iDataSize) {
    ...........................
    ......................
    AVPacket pkt = {0};
    av_init_packet(&pkt);

    int64_t dts = av_gettime() / 1000; //convert into millisecond
    dts = dts * VIDEO_FRAME_RATE;
    if(m_dtsOffset < 0) {
        m_dtsOffset = dts;
    }

    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = (dts - m_dtsOffset);

    pkt.stream_index = m_pAudioStream->index;
    pkt.flags |= AV_PKT_FLAG_KEY;
    pkt.data = (uint8_t*) pEncodedData;
    pkt.size = iDataSize;

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt);
    if(ret < 0) {
        LogErr("Writing audio frame failed: %d", ret);
        return false;
    }

    Log("Writing audio frame done.");

    av_free_packet(&pkt);
    return true;
}

bool AudioVideoRecorder::WriteVideo(const unsigned char *pData, size_t iDataSize, bool const bIFrame) {
    ........................................
    .................................
    AVPacket pkt = {0};
    av_init_packet(&pkt);
    int64_t dts = av_gettime() / 1000;
    dts = dts * VIDEO_FRAME_RATE;
    if(m_dtsOffset < 0) {
        m_dtsOffset = dts;
    }
    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = (dts - m_dtsOffset);

    if(bIFrame) {
        pkt.flags |= AV_PKT_FLAG_KEY;
    }
    pkt.stream_index = m_pVideoStream->index;
    pkt.data = (uint8_t*) pData;
    pkt.size = iDataSize;

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt);

    if(ret < 0) {
        LogErr("Writing video frame failed.");
        return false;
    }

    Log("Writing video frame done.");

    av_free_packet(&pkt);
    return true;
}

Ist die Letzte änderung in Ordnung? Die video-und audio-scheint synchronisiert. Problem ist nur - der Ton gespielt wird, ohne die Verzögerung egal, das Paket kam in Verzug.
Wie -

Paket Anreise: 1 2 3 4... (dann nächsten frame kam nach 3 Sek) .. 5

audio wiedergegeben: 1 2 3 4 (keine Verzögerung) 5

EDIT 3.0:

genullt audio-sample-Daten:

AVFrame* pSilentData;
pSilentData = av_frame_alloc();
memset(&pSilentData->data[0], 0, iDataSize);

pkt.data = (uint8_t*) pSilentData;
pkt.size = iDataSize;

av_freep(&pSilentData->data[0]);
av_frame_free(&pSilentData);

Ist das okay? Aber nach diesem schreiben in Datei-container, gibt es dot dot Rauschen während der Medienwiedergabe. Was ist das problem?

EDIT 4.0:

Gut, Für µ-Law audio der null-Wert ist dargestellt als 0xff. So

memset(&pSilentData->data[0], 0xff, iDataSize);

mein problem lösen.

  • AFAIK Audio-sollte in der Regel kein dts, nur ein Pkt. Video sollte nur eine dts wenn die Quell-frame hat eine dts zu. (Wenn es verwendet wird, als eine Referenz von einem B-Frame).
  • Ihre Zeitbasis für die audio-und video entsprechen sollten, um Ihre sampling-Frequenz. Zum Beispiel, wenn Sie Proben Ihr video mit 25 frames pro Sekunde dann das skalieren ist von 1/25 bis 1/90000. Ich bin mir nicht sicher, warum Sie mit 100000 überall.
  • Können Sie bitte überprüfen Sie den EDIT-2.0?
InformationsquelleAutor Kaidul | 2015-08-12
Schreibe einen Kommentar