从零开始学习音视频编程技术(八) FFMPEG Qt视频播放器之音视频同步

原文地址:http://blog.yundiantech.com/?log=blog&id=11

=========================================================

面分别讲解了:

  1. 用FFMPEG和Qt解码视频并显示到界面上。

  2. 用FFMPEG+SDL解码播放音频

     

现在我们就将视频和音频合并,并让声音和画面同步。

 

加入音频的部分就不做讲解了,这里主要讲下声音和视频同步的步骤。

 

首先刚开始播放的时候通过av_gettime()获取系统主时钟,记录下来。

以后便不断调用av_gettime()获取系统时钟 减去之前记录下的差值,便得到了一个视频播放了多久的实际时间。

 

对于视频的同步我们这样做:

从视频读取出的数据中包含一个pts的信息(每一帧图像都会带有pts的信息,pts就是播放视频的时候此图像应该显示的时间)。 这样只需要使用pts和前面获取的时间进行对比,pts比实际时间大,就调用sleep函数等一等,否则就直接播放出来。这样就达到了某种意义上的同步了。

 

而对于音频:

从前面使用SDL的例子,其实就能够发现一个现象:我们读取音频的线程差不多就是瞬间读完放入队列的,但是音频播放速度却是正常的,并不是一下子播放完毕。因此可以看出,在音频播放上,SDL已经帮我们做了处理了,只需要将数据直接交给SDL就行了。

 

视频部分同步代码大致如下:

 

    int64_t start_time = av_gettime();
    int64_t pts = 0; //当前视频的pts

    while (1)
    {
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            break; //这里认为视频读取完了
        }

        int64_t realTime = av_gettime() - start_time; //主时钟时间
        while(pts > realTime)
        {
            SDL_Delay(10);
            realTime = av_gettime() - start_time; //主时钟时间
        }

        if (packet->stream_index == videoStream)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
            if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque&& *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE)
            {
                pts = *(uint64_t *) pFrame->opaque;
            }
            else if (packet->dts != AV_NOPTS_VALUE)
            {
                pts = packet->dts;
            }
            else
            {
                pts = 0;
            }

            pts *= 1000000 * av_q2d(mVideoState.video_st->time_base);
            pts = synchronize_video(&mVideoState, pFrame, pts);

            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const * const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);

                //把这个RGB数据 用QImage加载
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
                emit sig_GetOneFrame(image);  //发送信号
            }

            av_free_packet(packet);
        }
        else if( packet->stream_index == audioStream )
        {
            packet_queue_put(mVideoState.audioq, packet);
            //这里我们将数据存入队列 因此不调用 av_free_packet 释放
        }
        else
        {
            // Free the packet that was allocated by av_read_frame
            av_free_packet(packet);
        }

    }

synchronize_video函数是根据解码后的视频数据 计算出视频的pts:

static double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) {

    double frame_delay;

    if (pts != 0) {
        /* if we have pts, set video clock to it */
        is->video_clock = pts;
    } else {
        /* if we aren't given a pts, set it to the clock */
        pts = is->video_clock;
    }
    /* update the video clock */
    frame_delay = av_q2d(is->video_st->codec->time_base);
    /* if we are repeating a frame, adjust clock accordingly */
    frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
    is->video_clock += frame_delay;
    return pts;
}

 

到此,播放器已经有点样子了,已经可以播放大部分的视频了,是的,我说的是大部分,还有些特别的视频,播放的时候音视频同步有问题,后面再来完善它。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值