ffmpeg实现视频解码

参考100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)

雷神的代码用在VS2022编译需要做些调整

平台环境:windows VS 2022

c 复制代码
#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 };
};

以及在

项目->项目属性->链接器->命令行,在右侧其他选项中添加"/SAFESEH:NO",这样就不会再报错了。

使用ffmpeg进行解码的一般步骤

1.初始化FFmpeg库:

在代码中引入相关的FFmpeg头文件,并调用初始化函数。例如:

c 复制代码
av_register_all();
avcodec_register_all();
avformat_network_init();

2.打开输入文件:

使用avformat_open_input,avformat_open_input 函数是FFmpeg(一个开源的多媒体处理库)中的一个函数,用于打开音视频文件或者网络流,并将其封装格式的相关信息填充到一个 AVFormatContext 结构体中。这个函数通常是在处理音视频文件时的初始步骤之一。

c 复制代码
AVFormatContext *pFormatCtx;
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

参数解释:

ps: 用于存储 AVFormatContext 结构体指针的指针。AVFormatContext 是一个保存音视频文件或流相关信息的结构体,就是句柄。

url: 要打开的音视频文件或者网络流的URL。

fmt: 指定输入格式。通常可以设置为 NULL,表示由FFmpeg自动检测输入格式。

options: 一个指向 AVDictionary 结构体指针的指针,用于传递附加的选项。可以为 NULL,表示没有额外选项。

3.获取流信息

使用 avformat_find_stream_info函数,avformat_find_stream_info 函数是FFmpeg库中用于获取音视频流详细信息的函数。在打开音视频文件或者网络流后,通常需要调用这个函数来获取音视频流的相关信息,如编码格式、分辨率、帧率等。

c 复制代码
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

参数解释:

ic: AVFormatContext 结构体,它包含了音视频文件或者网络流的相关信息,通常是通过 avformat_open_input 打开的。

options: 一个指向 AVDictionary 结构体指针的指针,用于传递附加的选项。可以为 NULL,表示没有额外选项。

函数返回值是一个整数,表示函数执行的结果。通常,如果函数成功执行,则返回 0,否则返回一个负数表示错误。

4.找到视频流索引并获取解码器上下文:

通过遍历流信息,找到视频流的索引,然后获取视频解码器的上下文。

c 复制代码
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.查找并打开解码器:

使用avcodec_find_decoder函数查找合适的解码器,并使用avcodec_open2打开解码器。

c 复制代码
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec, NULL);

6.创建帧和包:

创建用于存储解码后的帧的AVFrame对象以及用于存储解码前的数据包的AVPacket对象。

c 复制代码
AVFrame *pFrame = av_frame_alloc();
AVPacket *pPacket = av_packet_alloc();

7.解码:

使用循环读取每个数据包,将数据包发送到解码器进行解码,得到解码后的帧。

c 复制代码
while (av_read_frame(pFormatCtx, pPacket) >= 0) {
    if (pPacket->stream_index == videoStreamIndex) {
        avcodec_receive_frame(pCodecCtx, pFrame);
        // 处理解码后的帧(显示、保存等)
    }
    av_packet_unref(pPacket);
}

8.释放资源

c 复制代码
avformat_close_input(&pFormatCtx);
avcodec_free_context(&pCodecCtx);
av_frame_free(&pFrame);
av_packet_free(&pPacket);

使用ffmpeg解码视频文件

本例子使用FFmpeg库解码视频文件,并将解码后的帧保存为PPM图像文件。

c 复制代码
#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;
}
相关推荐
却道天凉_好个秋4 小时前
音视频学习(三十二):VP8和VP9
音视频·vp8·vp9
爱趣五科技7 小时前
H5DS编辑器教程——企业级雪花特效开发指南
前端·安全·编辑器·音视频
1alisa10 小时前
OBS 录屏软件 for Mac 视频录制
macos·音视频
写代码的小王吧21 小时前
【安全】Java幂等性校验解决重复点击(6种实现方式)
java·linux·开发语言·安全·web安全·网络安全·音视频
yunteng5211 天前
音视频(四)android编译
android·ffmpeg·音视频·x264·x265
zhuxian20091 天前
ffmpeg音频分析
ffmpeg·音视频
AI服务老曹1 天前
机器学习算法能够自动学习并使用不同条件下的变化趋势,确保预测结果的准确性的智慧地产开源了
运维·学习·开源·音视频
花落已飘1 天前
ffmpeg基础知识入门
ffmpeg·音视频
sqmeeting2 天前
Linux NUC小主机化身视频会议服务器: 技术优势与部署实战
linux·服务器·windows·音视频·实时音视频