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;
}
相关推荐
顾道长生'2 小时前
(Arxiv-2025)通过动态 token 剔除实现无需训练的高效视频生成
计算机视觉·音视频·视频生成
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
爱分享的飘哥11 小时前
【V6.0 - 听觉篇】当AI学会“听”:用声音特征捕捉视频的“情绪爽点”
人工智能·音视频
菜包eo12 小时前
二维码驱动的独立站视频集成方案
网络·python·音视频
不太会编程的IT男14 小时前
在 Jetson Orin 开发套件上使用 Hardware Encoder / Decoder 构建 FFmpeg
ffmpeg·视频编解码·h.264
阿蒙Amon15 小时前
【Python小工具】使用 OpenCV 获取视频时长的详细指南
python·opencv·音视频
m0_6873998415 小时前
写一个Ununtu C++ 程序,调用ffmpeg API, 来判断一个数字电影的视频文件mxf 是不是Jpeg2000?
开发语言·c++·ffmpeg
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
正在走向自律19 小时前
第二章-AIGC入门-开启AIGC音频探索之旅:从入门到实践(6/36)
人工智能·aigc·音视频·语音识别·ai音乐·ai 音频·智能语音助手
Java患者·21 小时前
【小白】linux安装ffmpeg | java转码 【超详细】
ffmpeg