參考100行代碼實(shí)現(xiàn)最簡(jiǎn)單的基于FFMPEG+SDL的視頻播放器(SDL1.x)
雷神的代碼用在VS2022編譯需要做些調(diào)整
平臺(tái)環(huán)境:windows VS 2022
#pragma comment(lib, "legacy_stdio_definitions.lib") //此為添加的代碼
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
FILE __iob_func[3] = { *stdin,*stdout,*stderr };
};
以及在
項(xiàng)目->項(xiàng)目屬性->鏈接器->命令行,在右側(cè)其他選項(xiàng)中添加“/SAFESEH:NO”,這樣就不會(huì)再報(bào)錯(cuò)了。
使用ffmpeg進(jìn)行解碼的一般步驟
1.初始化FFmpeg庫(kù):
在代碼中引入相關(guān)的FFmpeg頭文件,并調(diào)用初始化函數(shù)。例如:
av_register_all();
avcodec_register_all();
avformat_network_init();
2.打開(kāi)輸入文件:
使用avformat_open_input,avformat_open_input 函數(shù)是FFmpeg(一個(gè)開(kāi)源的多媒體處理庫(kù))中的一個(gè)函數(shù),用于打開(kāi)音視頻文件或者網(wǎng)絡(luò)流,并將其封裝格式的相關(guān)信息填充到一個(gè) AVFormatContext 結(jié)構(gòu)體中。這個(gè)函數(shù)通常是在處理音視頻文件時(shí)的初始步驟之一。
AVFormatContext *pFormatCtx;
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
參數(shù)解釋:
ps: 用于存儲(chǔ) AVFormatContext 結(jié)構(gòu)體指針的指針。AVFormatContext 是一個(gè)保存音視頻文件或流相關(guān)信息的結(jié)構(gòu)體,就是句柄。
url: 要打開(kāi)的音視頻文件或者網(wǎng)絡(luò)流的URL。
fmt: 指定輸入格式。通??梢栽O(shè)置為 NULL,表示由FFmpeg自動(dòng)檢測(cè)輸入格式。
options: 一個(gè)指向 AVDictionary 結(jié)構(gòu)體指針的指針,用于傳遞附加的選項(xiàng)。可以為 NULL,表示沒(méi)有額外選項(xiàng)。
3.獲取流信息
使用 avformat_find_stream_info函數(shù),avformat_find_stream_info 函數(shù)是FFmpeg庫(kù)中用于獲取音視頻流詳細(xì)信息的函數(shù)。在打開(kāi)音視頻文件或者網(wǎng)絡(luò)流后,通常需要調(diào)用這個(gè)函數(shù)來(lái)獲取音視頻流的相關(guān)信息,如編碼格式、分辨率、幀率等。
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
參數(shù)解釋:
ic: AVFormatContext 結(jié)構(gòu)體,它包含了音視頻文件或者網(wǎng)絡(luò)流的相關(guān)信息,通常是通過(guò) avformat_open_input 打開(kāi)的。
options: 一個(gè)指向 AVDictionary 結(jié)構(gòu)體指針的指針,用于傳遞附加的選項(xiàng)??梢詾?NULL,表示沒(méi)有額外選項(xiàng)。
函數(shù)返回值是一個(gè)整數(shù),表示函數(shù)執(zhí)行的結(jié)果。通常,如果函數(shù)成功執(zhí)行,則返回 0,否則返回一個(gè)負(fù)數(shù)表示錯(cuò)誤。
4.找到視頻流索引并獲取解碼器上下文:
通過(guò)遍歷流信息,找到視頻流的索引,然后獲取視頻解碼器的上下文。
int videoStreamIndex = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
5.查找并打開(kāi)解碼器:
使用avcodec_find_decoder函數(shù)查找合適的解碼器,并使用avcodec_open2打開(kāi)解碼器。
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec, NULL);
6.創(chuàng)建幀和包:
創(chuàng)建用于存儲(chǔ)解碼后的幀的AVFrame對(duì)象以及用于存儲(chǔ)解碼前的數(shù)據(jù)包的AVPacket對(duì)象。
AVFrame *pFrame = av_frame_alloc();
AVPacket *pPacket = av_packet_alloc();
7.解碼:
使用循環(huán)讀取每個(gè)數(shù)據(jù)包,將數(shù)據(jù)包發(fā)送到解碼器進(jìn)行解碼,得到解碼后的幀。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-819660.html
while (av_read_frame(pFormatCtx, pPacket) >= 0) {
if (pPacket->stream_index == videoStreamIndex) {
avcodec_receive_frame(pCodecCtx, pFrame);
// 處理解碼后的幀(顯示、保存等)
}
av_packet_unref(pPacket);
}
8.釋放資源
avformat_close_input(&pFormatCtx);
avcodec_free_context(&pCodecCtx);
av_frame_free(&pFrame);
av_packet_free(&pPacket);
使用ffmpeg解碼視頻文件
本例子使用FFmpeg庫(kù)解碼視頻文件,并將解碼后的幀保存為PPM圖像文件。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-819660.html
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#define IN_FILE_PATH "input_video.mp4"
#define OUT_FILE_PATH "output_frame.ppm"
int main() {
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket packet;
struct SwsContext *pSwsCtx = NULL;
// Register all formats and codecs
av_register_all();
// Open input file
if (avformat_open_input(&pFormatCtx, IN_FILE_PATH, NULL, NULL) != 0) {
fprintf(stderr, "Could not open input file\n");
return -1;
}
// Retrieve stream information
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
return -1;
}
// Find the first video stream
int videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
if (videoStream == -1) {
fprintf(stderr, "Could not find a video stream\n");
return -1;
}
// Get a pointer to the codec context for the video stream
pCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id);
if (pCodec == NULL) {
fprintf(stderr, "Unsupported codec\n");
return -1;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) {
fprintf(stderr, "Failed to copy codec parameters to codec context\n");
return -1;
}
// Open codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return -1;
}
// Allocate video frame
pFrame = av_frame_alloc();
// Setup scaler context to convert video frames to RGB
pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, NULL, NULL, NULL);
if (pSwsCtx == NULL) {
fprintf(stderr, "Failed to allocate SwsContext\n");
return -1;
}
// Open output file for writing PPM
FILE *ppmFile = fopen(OUT_FILE_PATH, "wb");
if (ppmFile == NULL) {
fprintf(stderr, "Could not open output file\n");
return -1;
}
// Read frames and save as PPM
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
// Decode video frame
int response = avcodec_send_packet(pCodecCtx, &packet);
if (response < 0) {
fprintf(stderr, "Error decoding frame (avcodec_send_packet)\n");
break;
}
while (response >= 0) {
response = avcodec_receive_frame(pCodecCtx, pFrame);
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
break;
} else if (response < 0) {
fprintf(stderr, "Error decoding frame (avcodec_receive_frame)\n");
break;
}
// Convert the frame to RGB
uint8_t *buffer = NULL;
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
buffer = av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(pFrame->data, pFrame->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
sws_scale(pSwsCtx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrame->data, pFrame->linesize);
// Save the frame as PPM
fprintf(ppmFile, "P6\n%d %d\n255\n", pCodecCtx->width, pCodecCtx->height);
fwrite(buffer, 1, numBytes, ppmFile);
av_free(buffer);
}
}
av_packet_unref(&packet);
}
// Clean up
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
sws_freeContext(pSwsCtx);
fclose(ppmFile);
return 0;
}
到了這里,關(guān)于ffmpeg實(shí)現(xiàn)視頻解碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!