ffmpeg实现视频解码

news/2024/7/10 18:37:52 标签: ffmpeg, 音视频

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

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

平台环境: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 };
};

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

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

1.初始化FFmpeg库:

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

av_register_all();
avcodec_register_all();
avformat_network_init();

2.打开输入文件:

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

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

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

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

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

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

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

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

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打开解码器。

AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec, NULL);

6.创建帧和包:

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

AVFrame *pFrame = av_frame_alloc();
AVPacket *pPacket = av_packet_alloc();

7.解码:

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

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_119">使用ffmpeg解码视频文件

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

#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;
}


http://www.niftyadmin.cn/n/5342508.html

相关文章

java截取视频最后一帧照片作为封面

引言 我们在日常工作中经常会遇到上传视频&#xff0c;而产品还会要求截取视频某一帧作为封面展示&#xff0c;对于这种情况新手还是比较头疼的&#xff0c;那我们直接世界上最简单的实现方案。 怎么搞 1.提前引入包 <!--视频多媒体工具包 包含 FFmpeg、OpenCV--><…

08-微服务Seata分布式事务使用

一、分布式事务简介 1.1 概念 事务ACID&#xff1a; A&#xff08;Atomic&#xff09;&#xff1a;原子性&#xff0c;构成事务的所有操作&#xff0c;要么都执行完成&#xff0c;要么全部不执行&#xff0c;不可能出现部分成功部分失 败的情况。 C&#xff08;Consistency&…

机器视觉系统选型-参数-镜头各个参数之间相互关系

焦距越小&#xff0c;景深越大&#xff1b;焦距越小&#xff0c;畸变越大&#xff1b; 光圈越大&#xff0c;图像亮度越高&#xff1b;光圈越大&#xff0c;景深越小&#xff1b; 光圈越大&#xff0c;分辨率越高&#xff1b; 一般像场中心较边缘分辨率高&#xff0c;像场中心较…

【分布式技术专题】「分布式技术架构」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)

探索Tomcat技术架构设计模式的奥秘 Tomcat系统架构分析Tomcat 整体结构Tomcat总体结构图以 Service 作为“婚姻”1) Service 接口方法列表 2) StandardService 的类结构图方法列表 3) StandardService. SetContainer4) StandardService. addConnector 以 Server 为“居”1) Ser…

WPF引用halcon的HSmartWindowControlWPF控件一加上Name属性就,无缘无故运行后报错

报错内容&#xff1a; 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 MC1000 未知的生成错误“Could not find assembly System.Drawing.Common, Version0.0.0.0, Cultureneutral, PublicKeyTokencc7b13ffcd2ddd51. Either explicitly load this assembly using a method …

如何测试python 版本与 torch 、 torchvision 版本是否对应?

python 版本与 torch 、 torchvision 版本的对应关系如下图所示&#xff1a; 打开 anaconda powershell prompt&#xff0c;输入如下命令&#xff1a; >python>>>import torch>>>c torch.ones((3,1)) //创建矩阵>>>c c.cuda(0) …

你知道Mysql的架构吗?

msyql分为server曾和存储引擎层 server层包括了连接器(管理连接&#xff0c;权限验证)、查询缓存&#xff08;命中直接返回结果&#xff09;、分析器&#xff08;词法分析&#xff0c;语法分析&#xff09;、优化器&#xff08;执行计划生成&#xff0c;索引选择&#xff09;、…

机器学习实验2——线性回归求解加州房价问题

文章目录 &#x1f9e1;&#x1f9e1;实验内容&#x1f9e1;&#x1f9e1;&#x1f9e1;&#x1f9e1;数据预处理&#x1f9e1;&#x1f9e1;代码缺失值处理特征探索相关性分析文本数据标签编码数值型数据标准化划分数据集 &#x1f9e1;&#x1f9e1;线性回归&#x1f9e1;&am…