在上一篇Qt绘制音频波纹图 中,我们使用Qt/FFmpeg绘制了简单的音频波纹图,本文将使用Qt的qwt库绘制音频解码的所有点。
本文的主要目的,是测试Qt/qwt的绘图最大能力。
测试配置为:
1 2 3 4 5 6 7 8 9 Manjaro Linux stable Linux kernel 5.15.2 CPU AMD 5950 16 Core 32 Thread 金士顿 32G GPU GeForce GTS 450 显示器 Redmi 27英寸 RMMNT27NF 1080P 16:9 GTG6ms 60-75Hz qwt 6.2.0 Qt 5.15.2 GCC 11.1.0
Qt paintevent刷新频率为 QWT刷新频率为:
各个组件的刷新频率同步很重要,如果显示器频率为60Hz,那么就算软件刷新为5Hz,也需要60ms来刷新显示。
在MindViewer-TGAM模块数据图形化软件 中,我们简单使用过QWT库。
解码就是把之前的代码拿过来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 void FFmpegAudio::run () { AVFormatContext *pFormatCtx=avformat_alloc_context (); size_t i; int audioStream=-1 ; AVCodecContext *pCodecCtx=NULL ; AVCodec *pCodec=NULL ; AVCodecParameters *pCodecPara=NULL ; AVPacket *packet=av_packet_alloc (); AVFrame *pFrame=av_frame_alloc (); char filename[]="/home/jackey/Music/test.mp3" ; avformat_network_init (); if (avformat_open_input (&pFormatCtx,filename,NULL ,NULL )!=0 ){ printf ("Couldn't open file.\n" ); return ; } if (avformat_find_stream_info (pFormatCtx,NULL )<0 ){ printf ("Couldn't find stream information.\n" ); return ; } av_dump_format (pFormatCtx, 0 , filename, false ); for (i=0 ; i < pFormatCtx->nb_streams; i++){ if (pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){ audioStream=i; break ; } } if (audioStream==-1 ){ printf ("Didn't find a audio stream.\n" ); return ; } pCodecPara = pFormatCtx->streams[audioStream]->codecpar; pCodec=avcodec_find_decoder (pCodecPara->codec_id); if (pCodec==NULL ){ printf ("Codec not found.\n" ); return ; } pCodecCtx = avcodec_alloc_context3 (pCodec); if (avcodec_parameters_to_context (pCodecCtx,pCodecPara)<0 ){ printf ("Cannot alloc context." ); return ; } pCodecCtx->pkt_timebase = pFormatCtx->streams[audioStream]->time_base; if (avcodec_open2 (pCodecCtx, pCodec,NULL )<0 ){ printf ("Could not open codec.\n" ); return ; } printf ("Channels: %d \n" , pCodecCtx->channels); printf ("Sample per Second %d \n" , pCodecCtx->sample_rate); pCodecCtx->channels = 1 ; while (av_read_frame (pFormatCtx, packet)>=0 ){ if (packet->stream_index==audioStream){ if (avcodec_send_packet (pCodecCtx,packet)>=0 ){ while (avcodec_receive_frame (pCodecCtx,pFrame)>=0 ){ uint8_t *ptr =pFrame->data[0 ]; for (int i = 0 ; i < pFrame->linesize[0 ]; i ++) { vdataL.append (ptr[i]); vdataR.append (-ptr[i]); } } msleep (17 ); emit updateFrame (vdataL,vdataR) ; vdataL.clear (); vdataR.clear (); } } av_packet_unref (packet); } avcodec_close (pCodecCtx); avformat_close_input (&pFormatCtx); av_packet_free (&packet); av_frame_free (&pFrame); printf ("Decode done.\n" ); }
显示初始化界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 maxCnt=5000 ;for (int i=0 ;i<maxCnt;i++){ xdata.append (i); } canvas = new QwtPlotCanvas (this ); canvas->setPalette (Qt::white); canvas->setBorderRadius (10 );setCanvas (canvas);plotLayout ()->setAlignCanvasToScales (true );setAxisTitle (QwtPlot::yLeft,"Value" );setAxisTitle (QwtPlot::xBottom,"time: s" );setAxisScale (QwtPlot::yLeft,-254 ,255 );setAxisScale (QwtPlot::xBottom,0 ,maxCnt); QwtPlotGrid *grid = new QwtPlotGrid (); grid->enableX (true ); grid->enableY (true ); grid->setMajorPen (Qt::black,0 ,Qt::DotLine); grid->attach (this ); curveChannelLeft = new QwtPlotCurve ("left" ); curveChannelLeft->setPen (Qt::red,2 ); curveChannelLeft->setRenderHint (QwtPlotItem::RenderAntialiased,true ); curveChannelRight = new QwtPlotCurve ("right" ); curveChannelRight->setPen (Qt::green,2 ); curveChannelRight->setRenderHint (QwtPlotItem::RenderAntialiased,true ); QwtPlotItemList items=itemList (QwtPlotItem::Rtti_PlotCurve);for ( int i = 0 ; i < items.size (); i++ ){ items[i]->setVisible (true ); }replot ();setAutoReplot ( true );
接收到数据后,显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if (channelDataL.size ()>=maxCnt){ channelDataL.remove (0 ,4086 ); }if (channelDataR.size ()>=maxCnt){ channelDataR.remove (0 ,4086 ); } channelDataL.append (dl); channelDataR.append (dr); curveChannelLeft->setSamples (xdata,channelDataL); curveChannelLeft->attach (this ); curveChannelLeft->setLegendAttribute (curveChannelLeft->LegendShowLine); curveChannelRight->setSamples (xdata,channelDataR); curveChannelRight->attach (this ); curveChannelRight->setLegendAttribute (curveChannelLeft->LegendShowLine);replot ();
然后绑定信号槽
1 2 ffAudio = new FFmpegAudio;connect (ffAudio,&FFmpegAudio::updateFrame,ui->frame,&WaveCurve::updateChannelData);
使用的音频文件信息如下
1 2 3 4 5 6 7 8 9 10 11 12 13 Input Metadata: artist : 龙千玉 title : 阿郎 album : 闽南情缘 track : 8 encoder : Lavf56.4.101 comment : 163 key(Don't modify):L64FU3W4YxX3ZFTmbZ+8/RVLtWbDWHLNAhEGfl+Nf7pQiiGPszFV2YCIc7+l7fMpf4dVB11wt7woihWFgSEOPD0dL3MYVy6tLTECRlW+tDT92DXQ+WZfwIbT6ars1WfT0+Z4TY2HzdBJJaYYm/FUvM9ws7oMCSJpzl4s7bTtDo++fyxsnqTH2CRKf0jctmKyrObJZu+fT1ba6aomgqYAmyX5FbZQ5p26agom+DDI6 Duration: 00:04:28.83, start: 0.025056, bitrate: 327 kb/s Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 320 kb/s Stream #0:1: Video: png, rgba(pc), 414x414, 90k tbr, 90k tbn, 90k tbc (attached pic) Metadata: comment : Cover (front)
时长为4分28秒,即269秒。
共有10291帧,每帧有4608个数据,总计47420928个点。平均每秒显示176286个点。1毫秒是176个点。
解码一帧获得4608个点,每帧延时25毫秒,那么一秒就是解码40帧,共计184320个点。两通道就是368640个点
绘图效果为:
CPU占用率为:
将延时调整为17毫秒,即60hz。一秒解码60帧,共计276480个点,两个通道为552960个点
效果为:
CPU占用率为: