中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

好看的網(wǎng)站模板2022當(dāng)下社會(huì)熱點(diǎn)話題

好看的網(wǎng)站模板,2022當(dāng)下社會(huì)熱點(diǎn)話題,提供佛山網(wǎng)站制作,設(shè)計(jì)微信小程序多少錢基于ffmpeg和sdl2的簡(jiǎn)單視頻播放器制作 前言一、視頻播放器開發(fā)的基礎(chǔ)1.1 視頻播放原理1.2 開發(fā)所需的庫 二、FFmpeg庫詳解2.1 FFmpeg庫的組成2.2 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)2.3 打開視頻文件并獲取流信息2.4 查找視頻流和解碼器2.5 初始化解碼器 三、SDL庫詳解3.1 SDL庫的功能3.2 初始化SDL…

基于ffmpeg和sdl2的簡(jiǎn)單視頻播放器制作

    • 前言
    • 一、視頻播放器開發(fā)的基礎(chǔ)
      • 1.1 視頻播放原理
      • 1.2 開發(fā)所需的庫
    • 二、FFmpeg庫詳解
      • 2.1 FFmpeg庫的組成
      • 2.2 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
      • 2.3 打開視頻文件并獲取流信息
      • 2.4 查找視頻流和解碼器
      • 2.5 初始化解碼器
    • 三、SDL庫詳解
      • 3.1 SDL庫的功能
      • 3.2 初始化SDL
      • 3.3 創(chuàng)建窗口、渲染器和紋理
      • 3.4 事件處理
    • 四、視頻播放流程
      • 4.1 讀取和解碼視頻幀
      • 4.2 時(shí)間同步
      • 4.3 圖像格式轉(zhuǎn)換與渲染
    • 五、資源清理
    • 六、完整代碼示例
    • 七、總結(jié)與展望


前言

本文將簡(jiǎn)單探討視頻播放器的開發(fā)過程,通過一個(gè)完整的代碼示例,帶你領(lǐng)略從打開視頻文件到播放視頻畫面的每一個(gè)關(guān)鍵步驟。


一、視頻播放器開發(fā)的基礎(chǔ)

1.1 視頻播放原理

視頻,本質(zhì)上是一系列連續(xù)的圖像幀快速播放所形成的視覺效果。在數(shù)字視頻中,這些圖像幀被編碼壓縮以減小文件大小,便于存儲(chǔ)和傳輸。常見的視頻編碼格式有H.264、H.265等。同時(shí),視頻文件通常還包含音頻數(shù)據(jù),音頻也經(jīng)過特定的編碼方式,如AAC、MP3等。

當(dāng)我們播放視頻時(shí),播放器需要執(zhí)行以下主要步驟:

  1. 解封裝:從視頻文件中分離出視頻流和音頻流。視頻文件通常采用某種封裝格式,如MP4、AVI等,這些格式將視頻和音頻數(shù)據(jù)按照一定的結(jié)構(gòu)組織在一起。
  2. 解碼:對(duì)分離出的視頻流和音頻流進(jìn)行解碼,將壓縮的數(shù)據(jù)還原為原始的圖像幀和音頻樣本。這需要使用相應(yīng)的解碼器,不同的編碼格式需要不同的解碼器。
  3. 渲染:將解碼后的圖像幀顯示在屏幕上,同時(shí)將音頻樣本通過音頻設(shè)備播放出來。這涉及到圖形渲染和音頻輸出的相關(guān)技術(shù)。

1.2 開發(fā)所需的庫

在開發(fā)視頻播放器時(shí),我們需要借助一些強(qiáng)大的開源庫來簡(jiǎn)化開發(fā)過程。在本文的示例中,我們主要使用了以下兩個(gè)庫:

  1. FFmpeg:這是一個(gè)功能強(qiáng)大的開源多媒體框架,提供了豐富的工具和函數(shù),用于處理多媒體文件的解封裝、解碼、編碼等操作。它支持幾乎所有常見的多媒體格式,并且具有高效的性能。
  2. SDL (Simple DirectMedia Layer):這是一個(gè)跨平臺(tái)的多媒體開發(fā)庫,專注于提供硬件抽象層,用于創(chuàng)建窗口、渲染圖形、播放音頻以及處理輸入事件等。它使得我們能夠在不同的操作系統(tǒng)上輕松實(shí)現(xiàn)一致的多媒體交互功能。

二、FFmpeg庫詳解

2.1 FFmpeg庫的組成

FFmpeg庫由多個(gè)模塊組成,每個(gè)模塊都有其特定的功能:

  1. libavformat:負(fù)責(zé)處理多媒體文件的格式,包括解封裝和封裝操作。它能夠識(shí)別各種常見的視頻和音頻封裝格式,如MP4、AVI、FLV等,并從中提取出視頻流和音頻流。
  2. libavcodec:這是FFmpeg的核心編解碼模塊,支持眾多的音頻和視頻編碼格式。它包含了各種解碼器和編碼器,能夠?qū)嚎s的多媒體數(shù)據(jù)進(jìn)行解碼或編碼操作。
  3. libavutil:提供了一系列通用的工具函數(shù)和數(shù)據(jù)結(jié)構(gòu),如內(nèi)存管理、錯(cuò)誤處理、數(shù)學(xué)運(yùn)算等。這些工具函數(shù)在整個(gè)FFmpeg庫的其他模塊中被廣泛使用。
  4. libswscale:用于圖像的縮放和格式轉(zhuǎn)換。在視頻播放過程中,由于解碼后的圖像格式可能與顯示設(shè)備所需的格式不一致,需要使用該模塊進(jìn)行轉(zhuǎn)換。
  5. libswresample:主要用于音頻的重采樣和格式轉(zhuǎn)換。它可以將音頻數(shù)據(jù)從一種采樣率、聲道數(shù)或樣本格式轉(zhuǎn)換為另一種,以適應(yīng)不同的音頻輸出設(shè)備。

2.2 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

  1. AVFormatContext:這個(gè)結(jié)構(gòu)體是FFmpeg中用于管理多媒體文件格式的上下文。它包含了文件的各種信息,如流的數(shù)量、每個(gè)流的參數(shù)等。在打開視頻文件時(shí),我們會(huì)創(chuàng)建一個(gè)AVFormatContext對(duì)象,并使用它來讀取文件的信息和解封裝數(shù)據(jù)。
  2. AVCodecContext:代表編解碼器的上下文,包含了編解碼所需的各種參數(shù),如編碼格式、分辨率、幀率等。在找到合適的解碼器后,我們需要?jiǎng)?chuàng)建一個(gè)AVCodecContext對(duì)象,并將其與解碼器進(jìn)行關(guān)聯(lián)。
  3. AVFrame:用于存儲(chǔ)解碼后的音頻或視頻數(shù)據(jù)。對(duì)于視頻來說,它包含了一幀圖像的像素?cái)?shù)據(jù);對(duì)于音頻來說,它包含了音頻樣本數(shù)據(jù)。
  4. AVPacket:用于存儲(chǔ)從文件中讀取的壓縮數(shù)據(jù),這些數(shù)據(jù)在經(jīng)過解封裝后以AVPacket的形式存在,然后被傳遞給解碼器進(jìn)行解碼。

2.3 打開視頻文件并獲取流信息

在我們的代碼示例中,首先需要打開視頻文件并獲取其流信息:

AVFormatContext* fmt_ctx = NULL;
std::string file_path = "F:/QT/mp4_flv/x.mp4";// 打開視頻文件
int ret = avformat_open_input(&fmt_ctx, file_path.c_str(), NULL, NULL);
if (ret < 0) {handle_ffmpeg_error(ret, "Failed to open video file.");return -1;
}// 讀取視頻流信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
if (ret < 0) {handle_ffmpeg_error(ret, "Error in obtaining video stream information.");avformat_close_input(&fmt_ctx);return -1;
}

這里,avformat_open_input函數(shù)用于打開指定路徑的視頻文件,并將文件信息存儲(chǔ)在fmt_ctx中。如果打開失敗,會(huì)調(diào)用handle_ffmpeg_error函數(shù)進(jìn)行錯(cuò)誤處理。接著,avformat_find_stream_info函數(shù)用于讀取視頻文件中的流信息,包括視頻流和音頻流的參數(shù)等。同樣,如果讀取失敗,也會(huì)進(jìn)行相應(yīng)的錯(cuò)誤處理。

2.4 查找視頻流和解碼器

const AVCodec* codec = NULL;
int video_stream_idx = -1;
for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_idx = i;codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id);if (!codec) {fprintf(stderr, "Video decoder not found\n");avformat_close_input(&fmt_ctx);return -1;}break;}
}

這段代碼通過遍歷fmt_ctx中的所有流,找到類型為視頻流(AVMEDIA_TYPE_VIDEO)的流,并獲取其索引video_stream_idx。然后,根據(jù)流的編碼ID,使用avcodec_find_decoder函數(shù)查找對(duì)應(yīng)的解碼器。如果找不到解碼器,會(huì)輸出錯(cuò)誤信息并關(guān)閉文件。

2.5 初始化解碼器

找到解碼器后,需要對(duì)其進(jìn)行初始化:

AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {fprintf(stderr, "Decoder context allocation failed\n");avformat_close_input(&fmt_ctx);return -1;
}ret = avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_idx]->codecpar);
if (ret < 0) {handle_ffmpeg_error(ret, "Copying codec parameters failed!");avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}ret = avcodec_open2(codec_ctx, codec, NULL);
if (ret < 0) {handle_ffmpeg_error(ret, "Decoder open failed!");avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}

首先,使用avcodec_alloc_context3函數(shù)分配一個(gè)AVCodecContext對(duì)象。然后,將流的編碼參數(shù)復(fù)制到codec_ctx中,最后使用avcodec_open2函數(shù)打開解碼器。每一步操作都進(jìn)行了錯(cuò)誤處理,確保解碼器能夠正確初始化。

三、SDL庫詳解

3.1 SDL庫的功能

SDL庫主要用于創(chuàng)建圖形窗口、渲染圖像以及處理用戶輸入事件。它提供了一系列簡(jiǎn)單易用的函數(shù),使得我們能夠在不同的操作系統(tǒng)上實(shí)現(xiàn)一致的圖形界面和交互功能。

  1. 窗口管理:SDL可以創(chuàng)建和管理窗口,設(shè)置窗口的大小、位置、標(biāo)題等屬性。它還支持窗口的最小化、最大化、關(guān)閉等操作。
  2. 圖形渲染:提供了多種渲染方式,包括軟件渲染和硬件加速渲染。可以將圖像數(shù)據(jù)渲染到窗口上,實(shí)現(xiàn)視頻畫面的顯示。
  3. 事件處理:能夠捕獲和處理各種用戶輸入事件,如鼠標(biāo)點(diǎn)擊、鍵盤按鍵、窗口關(guān)閉等事件,使得我們的程序能夠響應(yīng)用戶的操作。

3.2 初始化SDL

在使用SDL之前,需要先對(duì)其進(jìn)行初始化:

if (SDL_Init(SDL_INIT_VIDEO) < 0) {fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}

這里使用SDL_Init函數(shù)初始化SDL庫的視頻子系統(tǒng)。如果初始化失敗,會(huì)輸出錯(cuò)誤信息并釋放之前分配的資源。

3.3 創(chuàng)建窗口、渲染器和紋理

SDL_Window* window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,codec_ctx->width, codec_ctx->height, SDL_WINDOW_SHOWN);
if (!window) {fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError());SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,codec_ctx->width, codec_ctx->height);
if (!texture) {fprintf(stderr, "Texture could not be created! SDL_Error: %s\n", SDL_GetError());SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;
}

這段代碼依次創(chuàng)建了窗口、渲染器和紋理。SDL_CreateWindow函數(shù)用于創(chuàng)建一個(gè)指定大小和標(biāo)題的窗口。SDL_CreateRenderer函數(shù)創(chuàng)建一個(gè)渲染器,用于將圖像渲染到窗口上,這里使用了硬件加速渲染(SDL_RENDERER_ACCELERATED)。最后,SDL_CreateTexture函數(shù)創(chuàng)建一個(gè)紋理,用于存儲(chǔ)視頻圖像數(shù)據(jù),以便后續(xù)渲染。同樣,每一步創(chuàng)建操作都進(jìn)行了錯(cuò)誤處理,如果創(chuàng)建失敗,會(huì)釋放之前創(chuàng)建的資源。

3.4 事件處理

在視頻播放過程中,需要處理用戶的輸入事件,如關(guān)閉窗口事件:

SDL_Event event;
while (SDL_PollEvent(&event)) {if (event.type == SDL_QUIT) {goto cleanup;}
}

這里使用SDL_PollEvent函數(shù)不斷檢查是否有事件發(fā)生。如果檢測(cè)到SDL_QUIT事件(即用戶點(diǎn)擊了窗口的關(guān)閉按鈕),則跳轉(zhuǎn)到cleanup標(biāo)簽處,進(jìn)行資源清理操作。

四、視頻播放流程

4.1 讀取和解碼視頻幀

在初始化完成后,進(jìn)入視頻播放的主循環(huán),不斷從視頻文件中讀取數(shù)據(jù)包并進(jìn)行解碼:

AVPacket pkt;
while (av_read_frame(fmt_ctx, &pkt) >= 0) {if (pkt.stream_index == video_stream_idx) {ret = avcodec_send_packet(codec_ctx, &pkt);if (ret < 0) {handle_ffmpeg_error(ret, "send data decoder error.");av_packet_unref(&pkt);continue;}while (ret >= 0) {ret = avcodec_receive_frame(codec_ctx, frame);if (ret == 0) {// 處理解碼后的幀}else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;}}}av_packet_unref(&pkt);
}

在循環(huán)中,使用av_read_frame函數(shù)從視頻文件中讀取一個(gè)數(shù)據(jù)包pkt。如果數(shù)據(jù)包屬于視頻流(通過pkt.stream_index判斷),則將其發(fā)送給解碼器進(jìn)行解碼。avcodec_send_packet函數(shù)將數(shù)據(jù)包發(fā)送給解碼器,avcodec_receive_frame函數(shù)從解碼器中接收解碼后的幀。如果解碼成功(ret == 0),則可以對(duì)解碼后的幀進(jìn)行進(jìn)一步處理;如果解碼器需要更多數(shù)據(jù)(ret == AVERROR(EAGAIN))或已經(jīng)到達(dá)文件末尾(ret == AVERROR_EOF),則退出內(nèi)層循環(huán)。每次處理完數(shù)據(jù)包后,使用av_packet_unref函數(shù)釋放數(shù)據(jù)包的引用。

4.2 時(shí)間同步

為了保證視頻播放的流暢性和音頻視頻的同步,需要進(jìn)行時(shí)間同步:

int64_t start_time = av_gettime();// 在解碼幀的處理部分
int64_t pts = frame->pts;
if (pts == AV_NOPTS_VALUE) {pts = av_rescale_q(pkt.dts, fmt_ctx->streams[video_stream_idx]->time_base, { 1, 1000000 });
}
else {pts = av_rescale_q(frame->pts, fmt_ctx->streams[video_stream_idx]->time_base, { 1, 1000000 });
}int64_t now = av_gettime();
if (pts > now - start_time) {SDL_Delay((pts - (now - start_time)) / 1000);
}

這里使用av_gettime函數(shù)獲取當(dāng)前時(shí)間。通過幀的顯示時(shí)間戳(pts)和當(dāng)前時(shí)間的比較,計(jì)算出需要延遲的時(shí)間,使用SDL_Delay函數(shù)進(jìn)行延遲,以確保視頻幀按照正確的時(shí)間順序顯示。

4.3 圖像格式轉(zhuǎn)換與渲染

解碼后的幀需要進(jìn)行格式轉(zhuǎn)換并渲染到窗口上:

SDL_Rect rect = { 0, 0, codec_ctx->width, codec_ctx->height };
sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);// 在解碼幀的處理部分
SDL_UpdateYUVTexture(texture, &rect,frame->data[0], frame->linesize[0],frame->data[1], frame->linesize[1],frame->data[2], frame->linesize[2]);SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);

首先,使用sws_getContext函數(shù)創(chuàng)建一個(gè)圖像格式轉(zhuǎn)換上下文sws_ctx,將解碼后的幀格式轉(zhuǎn)換為適合SDL渲染的格式(這里是AV_PIX_FMT_YUV420P)。然后,使用SDL_UpdateYUVTexture函數(shù)將轉(zhuǎn)換后的幀數(shù)據(jù)更新到紋理中。接著,使用SDL_RenderClear函數(shù)清空渲染器,SDL_RenderCopy函數(shù)將紋理數(shù)據(jù)復(fù)制到渲染器上,最后使用SDL_RenderPresent函數(shù)將渲染器的內(nèi)容顯示在窗口上。

五、資源清理

在視頻播放結(jié)束后,需要釋放所有分配的資源,以避免內(nèi)存泄漏:

cleanup:sws_freeContext(sws_ctx);SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);

這里依次釋放了圖像格式轉(zhuǎn)換上下文、紋理、渲染器、窗口、SDL庫資源、視頻幀、解碼器上下文以及視頻文件格式上下文。

六、完整代碼示例


#include <iostream>
#include <string>
#include <SDL2\SDL.h>
extern "C" {
#include <libavcodec\avcodec.h>
#include <libavformat\avformat.h>
#include <libavutil\avutil.h>
#include <libswscale\swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil\pixfmt.h>
#include <libavutil/imgutils.h>
#include <libavutil/time.h>
}
#include <chrono> // 用于時(shí)間同步
#include <thread>void handle_ffmpeg_error(int ret, const char* msg) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);fprintf(stderr, "%s: %s\n", msg, errbuf);
}
#undef main
int main() {AVFormatContext* fmt_ctx = NULL;std::string file_path = "F:/QT/mp4_flv/x.mp4";// 打開視頻文件int ret = avformat_open_input(&fmt_ctx, file_path.c_str(), NULL, NULL);if (ret < 0) {handle_ffmpeg_error(ret, "Failed to open video file.");return -1;}// 讀取視頻流信息ret = avformat_find_stream_info(fmt_ctx, NULL);if (ret < 0) {handle_ffmpeg_error(ret, "Error in obtaining video stream information.");avformat_close_input(&fmt_ctx);return -1;}// 查找視頻流和解碼器const AVCodec* codec = NULL;int video_stream_idx = -1;for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_idx = i;codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id);if (!codec) {fprintf(stderr, "Video decoder not found\n");avformat_close_input(&fmt_ctx);return -1;}break;}}AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Decoder context allocation failed\n");avformat_close_input(&fmt_ctx);return -1;}ret = avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_idx]->codecpar);if (ret < 0) {handle_ffmpeg_error(ret, "Copying codec parameters failed!");avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}ret = avcodec_open2(codec_ctx, codec, NULL);if (ret < 0) {handle_ffmpeg_error(ret, "Decoder open failed!");avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}AVFrame* frame = av_frame_alloc();SwsContext* sws_ctx = NULL;if (SDL_Init(SDL_INIT_VIDEO) < 0) {fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}SDL_Window* window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,codec_ctx->width, codec_ctx->height, SDL_WINDOW_SHOWN);if (!window) {fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);if (!renderer) {fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError());SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,codec_ctx->width, codec_ctx->height);if (!texture) {fprintf(stderr, "Texture could not be created! SDL_Error: %s\n", SDL_GetError());SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}SDL_Rect rect = { 0, 0, codec_ctx->width, codec_ctx->height };sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);int64_t start_time = av_gettime();AVPacket pkt;while (av_read_frame(fmt_ctx, &pkt) >= 0) {if (pkt.stream_index == video_stream_idx) {ret = avcodec_send_packet(codec_ctx, &pkt);if (ret < 0) {handle_ffmpeg_error(ret, "send data decoder error.");av_packet_unref(&pkt);continue;}while (ret >= 0) {ret = avcodec_receive_frame(codec_ctx, frame);if (ret == 0) {int64_t pts = frame->pts;if (pts == AV_NOPTS_VALUE) {pts = av_rescale_q(pkt.dts, fmt_ctx->streams[video_stream_idx]->time_base, { 1, 1000000 });}else {pts = av_rescale_q(frame->pts, fmt_ctx->streams[video_stream_idx]->time_base, { 1, 1000000 });}int64_t now = av_gettime();if (pts > now - start_time) {SDL_Delay((pts - (now - start_time)) / 1000);}SDL_UpdateYUVTexture(texture, &rect,frame->data[0], frame->linesize[0],frame->data[1], frame->linesize[1],frame->data[2], frame->linesize[2]);SDL_RenderClear(renderer);SDL_RenderCopy(renderer, texture, NULL, NULL);SDL_RenderPresent(renderer);}else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;}}}av_packet_unref(&pkt);SDL_Event event;while (SDL_PollEvent(&event)) {if (event.type == SDL_QUIT) {goto cleanup;}}}cleanup:sws_freeContext(sws_ctx);SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return 0;
}

在這里插入圖片描述

七、總結(jié)與展望

通過本文的詳細(xì)介紹和代碼示例,我們深入了解了視頻播放器的開發(fā)過程。從視頻播放的基本原理,到使用FFmpeg庫進(jìn)行視頻文件的解封裝、解碼,再到利用SDL庫進(jìn)行窗口創(chuàng)建、圖形渲染和事件處理,每一個(gè)環(huán)節(jié)都緊密相扣,共同構(gòu)成了一個(gè)完整的視頻播放系統(tǒng)。

然而,這只是一個(gè)簡(jiǎn)單的視頻播放器示例,實(shí)際應(yīng)用中的視頻播放器還需要具備更多的功能和優(yōu)化。例如,支持更多的視頻和音頻格式、實(shí)現(xiàn)音頻的播放和同步、添加播放控制功能(如暫停、快進(jìn)、快退等)、優(yōu)化性能以適應(yīng)不同的硬件環(huán)境等。

http://www.risenshineclean.com/news/48856.html

相關(guān)文章:

  • 深圳市光明區(qū)住房和建設(shè)局網(wǎng)站網(wǎng)店seo關(guān)鍵詞
  • 百度推廣網(wǎng)站誰做百度競(jìng)價(jià)價(jià)格
  • 油漆網(wǎng)站mobanseo整站優(yōu)化方案案例
  • 企業(yè)建站技術(shù)軟文營(yíng)銷名詞解釋
  • 辦網(wǎng)站租服務(wù)器東莞優(yōu)化排名公司
  • 哪個(gè)網(wǎng)站可以做身份核驗(yàn)大地資源網(wǎng)在線觀看免費(fèi)
  • 企業(yè)做網(wǎng)站建設(shè)百度一下瀏覽器
  • icp網(wǎng)站建設(shè)seo手機(jī)搜索快速排名
  • W做網(wǎng)站怎么建立自己的企業(yè)網(wǎng)站
  • 做的網(wǎng)站怎么放到域名模板網(wǎng)站如何建站
  • 購物商城網(wǎng)站建設(shè)電商網(wǎng)站建設(shè)開發(fā)
  • 黑龍江省建設(shè)造價(jià)協(xié)會(huì)網(wǎng)站在線網(wǎng)站seo診斷
  • 什么網(wǎng)站發(fā)布找做效果圖的電腦優(yōu)化是什么意思
  • wordpress的文件結(jié)構(gòu)百度刷seo關(guān)鍵詞排名
  • 網(wǎng)站建設(shè)行業(yè)數(shù)據(jù)seo推薦
  • 商洛免費(fèi)做網(wǎng)站百度廣告聯(lián)系方式
  • 豐金網(wǎng)絡(luò) 做網(wǎng)站數(shù)字營(yíng)銷網(wǎng)站
  • 怎么做培訓(xùn)班網(wǎng)站石家莊關(guān)鍵詞優(yōu)化軟件
  • 什么是網(wǎng)絡(luò)營(yíng)銷包含哪些內(nèi)容全網(wǎng)營(yíng)銷與seo
  • 清理網(wǎng)站數(shù)據(jù)庫源碼交易平臺(tái)
  • 南昌那個(gè)公司做網(wǎng)站好今日最新國(guó)際新聞?lì)^條
  • 金昌網(wǎng)站seo合肥seo推廣培訓(xùn)班
  • 蘇州專業(yè)做網(wǎng)站公司有哪些小說推廣關(guān)鍵詞怎么弄
  • 做網(wǎng)站需要的照片網(wǎng)站建設(shè)公司哪個(gè)好呀
  • 建站公司 萬維科技外鏈交換平臺(tái)
  • 網(wǎng)站設(shè)置密碼百度網(wǎng)站域名注冊(cè)
  • 給企業(yè)做宣傳網(wǎng)站的好處百度上如何發(fā)廣告
  • 開發(fā)平臺(tái)游戲名詞解釋搜索引擎優(yōu)化
  • 貴陽疫情最新消息今天寧波seo排名優(yōu)化培訓(xùn)
  • 網(wǎng)站建設(shè)公司信息搜索引擎營(yíng)銷的優(yōu)勢(shì)和劣勢(shì)