當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 碼農(nóng)愛(ài)學(xué)習(xí)
[導(dǎo)讀]本篇,將通過(guò)Qt程序,設(shè)計(jì)一個(gè)RTSP視頻播放器,來(lái)播放網(wǎng)絡(luò)視頻,并增加啟動(dòng)、暫停等操作按鈕。

上篇文章,介紹了FFmpeg的交叉編譯,以及在嵌入式Linux平臺(tái),運(yùn)行ffmpeg指令來(lái)播放視頻。

本篇,將通過(guò)Qt程序,設(shè)計(jì)一個(gè)RTSP視頻播放器,來(lái)播放網(wǎng)絡(luò)視頻,并增加啟動(dòng)、暫停等操作按鈕。

1 FFMPEG 庫(kù)介紹

1.1 ffmpeg的7個(gè)庫(kù)

ffmpeg有7個(gè)library,分別是:

  • avutil
  • swscale
  • swresample
  • avcodec
  • avformat
  • avdevice
  • avfilter

avutil 工具庫(kù)

avutil是一個(gè)實(shí)用的工具庫(kù)用于輔助可移植的多媒體編程。它包含安全的可移植的字符串函數(shù),隨機(jī)數(shù)生成器,數(shù)據(jù)結(jié)構(gòu),附加的數(shù)學(xué)函數(shù),密碼學(xué)和多媒體相關(guān)功能(例如像素和樣本格式的枚舉)。它不是 libavcodec 和 libavformat 都需要的代碼庫(kù)。

swscale 視頻像素?cái)?shù)據(jù)格式轉(zhuǎn)換

swscale庫(kù)執(zhí)行高度優(yōu)化的圖像縮放以及色彩空間和像素格式轉(zhuǎn)換操作,這個(gè)庫(kù)執(zhí)行以下轉(zhuǎn)換:

  • Recailing:是改變視頻大小的過(guò)程。有幾個(gè)重新縮放選項(xiàng)和算法可用。這通常是一個(gè)有損過(guò)程。

  • Pixel format conversion:是將圖像的圖像格式和色彩空間轉(zhuǎn)換的過(guò)程,例如從平面YUV420P 到RGB24 打包。它還處理打包方式轉(zhuǎn)換,即從Packed布局轉(zhuǎn)換為Planar布局。

    注意:如果源和目標(biāo)顏色空間不同,這通常是一個(gè)有損過(guò)程。

swresample 音頻采樣數(shù)據(jù)格式轉(zhuǎn)換

swresample庫(kù)執(zhí)行高度優(yōu)化的音頻重采樣,重矩陣化和樣本格式轉(zhuǎn)換操作,這個(gè)庫(kù)執(zhí)行以下轉(zhuǎn)換:

  • Resampling:是改變音頻碼率的過(guò)程,例如從一個(gè)高采樣率44100Hz轉(zhuǎn)化為8000Hz。音頻從高采樣率轉(zhuǎn)換為低采樣率是一個(gè)有損的過(guò)程。有幾種重采樣選項(xiàng)和算法可用。
  • Format conversion:是一個(gè)轉(zhuǎn)換樣本類(lèi)型的過(guò)程,例如從有符號(hào)16-bit(int16_t)樣本轉(zhuǎn)換為無(wú)符號(hào)8-bit(uint8_t)或浮點(diǎn)樣本。它也處理打包方式轉(zhuǎn)換,如從Packed布局轉(zhuǎn)換為Planar布局。
  • Rematrixing:是改變通道布局的過(guò)程,例如從立體聲到單聲道。當(dāng)輸入通道不能映射到輸出流時(shí),這個(gè)過(guò)程是有損的,因?yàn)樗婕安煌脑鲆嬉蜃雍突旌?。通過(guò)專(zhuān)用選項(xiàng)啟用各種其他音頻轉(zhuǎn)換(例如拉伸和填充)。

avcodec 編解碼

avcodec庫(kù)提供了一個(gè)通用的編碼/解碼框架,并且包含用于音頻、視頻、字幕流的多個(gè)編解器和解碼器享架構(gòu)提供從比特 I/O 到 DSP 優(yōu)化的各種服務(wù),使其適用于實(shí)現(xiàn)魯棒和快速的編解碼器以及實(shí)驗(yàn)。

avformat 封裝格式處理

libavformat庫(kù)為音頻、視頻和字幕流的復(fù)用和解復(fù)用(muxing and demuxing)提供了一個(gè)通用框架。它包含多個(gè)用于媒體容器格式的多個(gè)復(fù)用器和解復(fù)用器,它還支持多種輸入和輸出協(xié)議來(lái)訪問(wèn)媒體資源。

avdevice 設(shè)備的輸入輸出

avdevice 庫(kù)提供了一個(gè)通用框架,用于從許多常見(jiàn)的多媒體輸入/輸出設(shè)備進(jìn)行抓取和渲染,并支持多種輸入和輸出設(shè)備,包括 Video4Linux2、VfW、DShow 和 ALSA。

avfilter 濾鏡特效處理

avfilter 庫(kù)提供了一個(gè)通用的音頻/視頻過(guò)濾框架,其中包含多個(gè)過(guò)濾器、源和接收器。

1.2 win平臺(tái)FFmpeg庫(kù)下載

Win平臺(tái)的Qt Creator需要用到Visual Stdio的功能,我電腦的Visual Stdio的2015版(對(duì)應(yīng)的是msvc14),因此,我下載的FFmpeg是4.4版的,再高的版本就沒(méi)有msvc14的了。

https://github.com/ShiftMediaProject/FFmpeg/releases/tag/4.4.r101753

2 Qt程序設(shè)計(jì)

2.1 RTSP解碼與視頻播放流程

先來(lái)看下FFmpeg對(duì)RTSP解碼的處理流程:

2.2 視頻解碼

對(duì)照上面的流程圖,使用FFmpeg對(duì)RTSP視頻流的解碼如下:

void VideoPlayer::run() {
    AVFormatContext *pFormatCtx; //音視頻封裝格式上下文結(jié)構(gòu)體 AVCodecContext  *pCodecCtx; //音視頻編碼器上下文結(jié)構(gòu)體 AVCodec *pCodec; //音視頻編碼器結(jié)構(gòu)體 AVFrame *pFrame; //存儲(chǔ)一幀解碼后像素?cái)?shù)據(jù) AVFrame *pFrameRGB;
    AVPacket *pPacket; //存儲(chǔ)一幀壓縮編碼數(shù)據(jù) uint8_t *pOutBuffer; static struct SwsContext *pImgConvertCtx; avformat_network_init(); //初始化FFmpeg網(wǎng)絡(luò)模塊 av_register_all(); //初始化FFMPEG  調(diào)用了這個(gè)才能正常適用編碼器和解碼器 //Allocate an AVFormatContext. pFormatCtx = avformat_alloc_context(); //AVDictionary AVDictionary *avdic=nullptr; char option_key[]="rtsp_transport"; char option_value[]="udp";
    av_dict_set(&avdic,option_key,option_value,0); char option_key2[]="max_delay"; char option_value2[]="100";
    av_dict_set(&avdic,option_key2,option_value2,0); if (avformat_open_input(&pFormatCtx, m_strFileName.toLocal8Bit().data(), nullptr, &avdic) != 0)
    { printf("can't open the file. \n"); return;
    } if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
    { printf("Could't find stream infomation.\n"); return;
    } //查找視頻中包含的流信息,音頻流先不處理 int videoStreamIdx = -1;
    qDebug("apFormatCtx->nb_streams:%d", pFormatCtx->nb_streams); for (int i = 0; i < pFormatCtx->nb_streams; i++)
    { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStreamIdx = i; //視頻流 }
    } if (videoStreamIdx == -1)
    { printf("Didn't find a video stream.\n"); //沒(méi)有找到視頻流 return;
    } //查找解碼器 qDebug("avcodec_find_decoder...");
    pCodecCtx = pFormatCtx->streams[videoStreamIdx]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == nullptr)
    { printf("Codec not found.\n"); return;
    }
    pCodecCtx->bit_rate =0; //初始化為0 pCodecCtx->time_base.num=1; //下面兩行:一秒鐘25幀 pCodecCtx->time_base.den=10;
    pCodecCtx->frame_number=1; //每包一個(gè)視頻幀 //打開(kāi)解碼器 if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
    { printf("Could not open codec.\n"); return;
    } //將解碼后的YUV數(shù)據(jù)轉(zhuǎn)換成RGB32 pImgConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                                     pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                                     AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr); int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);

    pFrame     = av_frame_alloc();
    pFrameRGB  = av_frame_alloc();
    pOutBuffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, pOutBuffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    pPacket = (AVPacket *) malloc(sizeof(AVPacket)); //分配一個(gè)packet int y_size = pCodecCtx->width * pCodecCtx->height;
    av_new_packet(pPacket, y_size); //分配packet的數(shù)據(jù) while (1)
    { if (av_read_frame(pFormatCtx, pPacket) < 0)
        { break; //這里認(rèn)為視頻讀取完了 } if (pPacket->stream_index == videoStreamIdx)
        { int got_picture; int ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,pPacket); if (ret < 0)
            { printf("decode error.\n"); return;
            } if (got_picture)
            {
                sws_scale(pImgConvertCtx, (uint8_t const * const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); //把這個(gè)RGB數(shù)據(jù) 用QImage加載 QImage tmpImg((uchar *)pOutBuffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把圖像復(fù)制一份 傳遞給界面顯示 emit sig_GetOneFrame(image); //發(fā)送信號(hào) }
        }
        av_free_packet(pPacket); //msleep(0.02); }

    av_free(pOutBuffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

解碼出一幀圖像后,發(fā)送信號(hào)給圖像顯示線程顯示

2.3 視頻顯示

這里是圖像顯示的處理:

void MainWindow::slotGetOneFrame(QImage img) {
    ui->labelCenter->clear(); if(m_kPlayState == RPS_PAUSE)
    { return;
    }

    m_Image = img;
    update(); //調(diào)用update將執(zhí)行paintEvent函數(shù) } void MainWindow::paintEvent(QPaintEvent *event) { QPainter painter(this); int showWidth = this->width() - 100; int showHeight = this->height() - 50;

    painter.setBrush(Qt::white);
    painter.drawRect(0, 0, this->width(), this->height()); //先畫(huà)成白色 if (m_Image.size().width() <= 0)
    { return;
    } //將圖像按比例縮放 QImage img = m_Image.scaled(QSize(showWidth, showHeight),Qt::KeepAspectRatio);
    img = img.mirrored(m_bHFlip, m_bVFlip); int x = this->width() - img.width(); int y = this->height() - img.height();
    x /= 2;
    y /= 2;

    painter.drawImage(QPoint(x-40,y+20),img); //畫(huà)出圖像 }

2.4 按鍵操作處理

客戶端界面中,有啟動(dòng)、暫停播放和視頻畫(huà)面翻轉(zhuǎn)按鈕,對(duì)應(yīng)的處理邏輯如下:

void MainWindow::on_pushButton_toggled(bool checked) { if (checked) //第一次按下為啟動(dòng),后續(xù)則為繼續(xù) { if(m_kPlayState == RPS_IDLE)
        {
            ui->lineEditUrl->setEnabled(false);
            m_strUrl = ui->lineEditUrl->text();
            m_pPlayer->startPlay(m_strUrl);

            ui->labelCenter->setText("rtsp網(wǎng)絡(luò)連接中...");
        }
        m_kPlayState = RPS_RUNNING;
        ui->pushButton->setText("暫停");
    } else {
        m_kPlayState = RPS_PAUSE;
        ui->pushButton->setText("播放");
    }
} void MainWindow::on_checkBoxVFlip_clicked(bool checked) {
    m_bVFlip = checked;
} void MainWindow::on_checkBoxHFlip_clicked(bool checked) {
    m_bHFlip = checked;
}

2.5 pro文件

因?yàn)橐玫紽Fmpeg庫(kù),因此需要注意以下對(duì)FFmpeg庫(kù)的引用,需要修改Qt工程的pro文件

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = rtspPlayer
TEMPLATE = app

SOURCES += main.cpp \
    videoplayer.cpp \
    mainwindow.cpp

HEADERS  += \
    videoplayer.h \
    mainwindow.h

FORMS    += \
    mainwindow.ui

INCLUDEPATH+=$$PWD/ffmpeg/include

LIBS += $$PWD/ffmpeg/lib/x64/avcodec.lib \
        $$PWD/ffmpeg/lib/x64/avdevice.lib \
        $$PWD/ffmpeg/lib/x64/avfilter.lib \
        $$PWD/ffmpeg/lib/x64/avformat.lib \
        $$PWD/ffmpeg/lib/x64/avutil.lib \
        $$PWD/ffmpeg/lib/x64/postproc.lib \
        $$PWD/ffmpeg/lib/x64/swresample.lib \
        $$PWD/ffmpeg/lib/x64/swscale.lib

3 運(yùn)行測(cè)試

3.1 Win平臺(tái)測(cè)試

在Win10平臺(tái)上測(cè)試效果如下:

3.2 嵌入式Linux平臺(tái)測(cè)試

在嵌入式Linux平臺(tái)運(yùn)行,也需要先進(jìn)行FFmpeg運(yùn)行環(huán)境的搭建,上篇文章已介紹如何交叉編譯FFmpeg源碼以及在嵌入式Linux平臺(tái)搭建FFmpeg運(yùn)行環(huán)境。

3.2.1 需要安裝4.4版本的庫(kù)

由于不同版本FFmpeg的API函數(shù)有些差別,上篇使用的是較新版本的FFmpeg源碼,與4.4版本的可能不太一樣,因此,需要參考上篇文章,重新在嵌入式Linux環(huán)境中安裝4.4版本的FFmpeg。

4.4版本的源碼可從如下鏈接下載:https://ffmpeg.org/download.html

3.2.2 修改pro文件

然后就是將Qt程序拷貝到Ubuntu中進(jìn)行交叉編譯,在編譯之前,還要修改pro文件,使程序能夠鏈接到linux版本的FFmpeg庫(kù),具體的修改如下,主要路徑要修改為自己的ffmpeg庫(kù)的安裝位置。

INCLUDEPATH+=$$PWD/../ffmpeg442_install/include \
             $$PWD/../x264_install/include

LIBS += $$PWD/../ffmpeg442_install/lib/libavcodec.so \
        $$PWD/../ffmpeg442_install/lib/libavdevice.so \
        $$PWD/../ffmpeg442_install/lib/libavfilter.so \
        $$PWD/../ffmpeg442_install/lib/libavformat.so \
        $$PWD/../ffmpeg442_install/lib/libavutil.so \
        $$PWD/../ffmpeg442_install/lib/libpostproc.so \
        $$PWD/../ffmpeg442_install/lib/libswresample.so \
        $$PWD/../ffmpeg442_install/lib/libswscale.so \
		$$PWD/../x264_install/lib/libx264.so

3.3 演示視頻

4 總結(jié)

本篇介紹了通過(guò)Qt程序,設(shè)計(jì)一個(gè)RTSP視頻播放器,運(yùn)行在嵌入式Linux平臺(tái)上,來(lái)播放網(wǎng)絡(luò)視頻,并增加啟動(dòng)、暫停、畫(huà)面翻轉(zhuǎn)等操作按鈕。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉