基于vs2017 ffmpeg4.3, sdl2.0 实现简单播放器

背景

最近重新阅读敬爱的雷神的遗作 <<最简单的基于FFMPEG+SDL的音频播放器>>

发现当时的ffmpeg版本已经不能在vs2017编译通过了.

因此我下载了最新的ffmpeg4.3(2020-6-15), 重新实现一次.

下载相关库文件

在以下页面下载ffmpeg, 注意选择4.3 windows-32bit, dev.

注意: 考虑到减少搭建中可能遇到的坑, 因此统一选择x86.

https://ffmpeg.zeranoe.com/builds/

http://www.libsdl.org/release/SDL2-2.0.12-win32-x86.zip

使用vs2017建立一个工程

命名为FFmpegSDLPlayer,  注意将工程的配置修改为x86.

配置头文件和库

将以上文件解压后, 放置在工程目录下.

首先配置工程属性C/C++-> 常规> 附加包含目录

.\ffmpeg\include;.\SDL2\include;

然后配置工程属性 链接器 > 常规 > 附加库目录

.\ffmpeg\lib;.\SDL2\lib\x86;

最后配置 工程属性 链接器 > 输入 > 附加依赖项

avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;postproc.lib;swresample.lib;swscale.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies)

代码

代码方面, 参考着"雷神"的代码写发现有些函数被废弃了.

不得不采用新的函数, 编写的过程中遇到一些阻力, 都一一克服, 

比如以前的写法每次直接读取一帧, 现在的写法是每次读取一定的字节数, 放进avcodec_send_packet,

然后从avcodec_receive_frame取出, 这里如果不仔细阅读返回值的意义, 就很容易被绕晕.



#include <stdio.h>
#include <intsafe.h>

#include <stdlib.h>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL.h"
}

#define INBUF_SIZE 4096
using namespace std;

const char filePath[] = "a.h265"; //  "640.mp4"; // 
bool GetVideoIndex(AVFormatContext * pFormatCtx, int &i) {
    int videoIndex = -1;
    for (; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoIndex = i;
            break;
        }
    }
    return videoIndex;
}

static int DecodePacket(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame) {
    int ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "sending a packet for decoding failed! \n");
        return -1;
    }

    ret = avcodec_receive_frame(dec_ctx, frame);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        return 0;
    }
    else if (ret < 0) {
        fprintf(stderr, "Error during decoding\n");
        return -1;
    }
    return 1;
}

void InitSDLObject(AVFrame * pFrame, 
    SDL_Window * &screen, SDL_Renderer* &sdlRenderer, 
    SDL_Texture* &sdlTexture, SDL_Rect &sdlRect) {
    screen = SDL_CreateWindow("fd-player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pFrame->width, pFrame->height, SDL_WINDOW_OPENGL);
    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    sdlTexture = SDL_CreateTexture(sdlRenderer,
        SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,
        pFrame->width, pFrame->height);
    sdlRect.x = 0;
    sdlRect.y = 0;
    sdlRect.w = pFrame->width;
    sdlRect.h = pFrame->height;
}

void ShowFrameInSDL(AVFrame *& pFrame,
    SDL_Window * &screen, SDL_Texture*& sdlTexture,
    SDL_Renderer*& sdlRenderer, SDL_Rect& sdlRect) {
    if (sdlRenderer == nullptr) {
        InitSDLObject(pFrame, screen, sdlRenderer, sdlTexture, sdlRect);
    }
    SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
        pFrame->data[0], pFrame->linesize[0],
        pFrame->data[1], pFrame->linesize[1],
        pFrame->data[2], pFrame->linesize[2]);
    SDL_RenderClear(sdlRenderer);
    SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
    SDL_RenderPresent(sdlRenderer);
    SDL_Delay(40);
}

int main(int argc, char* argv[]) {
    AVFormatContext	*pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        fprintf(stderr, "open file %s failed! \n", filePath);
        return -1;
    }
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        fprintf(stderr, "find stream info failed! \n");
        return -1;
    }

    int i = 0;
    int videoIndex = GetVideoIndex(pFormatCtx, i);
    if (videoIndex < 0) {
        fprintf(stderr, "get video stream %d failed! \n", i);
        return -1;
    }

    AVCodecParameters *pCodecParam = pFormatCtx->streams[videoIndex]->codecpar;
    AVCodec *pCodec = avcodec_find_decoder(pCodecParam->codec_id);
    if (pCodec == NULL) {
        fprintf(stderr, "[Err] find decoder of videoIndex %d failed! \n", i);
        return -1;
    }
    AVCodecParserContext *parser = av_parser_init(pCodec->id);
    if (!parser) {
        fprintf(stderr, "parser init failed! \n");
        return -1;
    }
    AVCodecContext *codecContext = avcodec_alloc_context3(pCodec);
    if (!codecContext) {
        fprintf(stderr, "alloc context failed! \n");
        return -1;
    }
    
    if (avcodec_parameters_to_context(codecContext, pCodecParam) < 0) {
        fprintf(stderr, "parameters to context failed! \n");
        return -1;
    }

    if (avcodec_open2(codecContext, pCodec, NULL) < 0) {
        fprintf(stderr, "open decoder failed! \n");
        return -1;
    }

    AVFrame *pFrame = av_frame_alloc();
    AVFrame * pFrameYUV = av_frame_alloc();
    AVPacket* pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "alloc packet failed! \n");
    }

    av_dump_format(pFormatCtx, 0, filePath, 0);

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        fprintf(stderr, "init sdl failed! \n");
        return -1;
    }

    SDL_Window *screen = nullptr;
    SDL_Renderer* sdlRenderer = nullptr;
    SDL_Texture* sdlTexture = nullptr;
    SDL_Rect sdlRect = { 0 };

    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
    uint8_t *data;
    FILE* f = fopen(filePath, "rb");
    if (!f) {
        fprintf(stderr, "open file failed failed! \n");
        return -1;
    }

    while (!feof(f)) {
        int data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size){
            break;
        }
        data = inbuf;
        while (data_size > 0) {
            int ret = av_parser_parse2(parser, codecContext, &pkt->data, &pkt->size,
                data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "av parse failed! \n");
                return -1;
            }
            data += ret;
            data_size -= ret;
            if (pkt->size) {
                if (pkt->stream_index == videoIndex) {
                    int retd = DecodePacket(codecContext, pkt, pFrame);
                    if (retd == 0) continue;
                    if (retd == -1) return -1;
                    ShowFrameInSDL(pFrame, screen, sdlTexture, sdlRenderer, sdlRect);
                }
            }
        }       
    }
    SDL_Quit();
    av_parser_close(parser);
    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_free_context(&codecContext);
    av_packet_free(&pkt);
    avformat_close_input(&pFormatCtx);
    return 0;
}

FFmpeg库

  1. libavutil
    核心库, 公共的音视频处理操作
  2. libavformat
    处理文件格式和协议
  3. libavcodec
    编解码库, codec层
  4. libavfilter
    滤镜, 音频特效, 视频特效, 
  5. libavdevice
    输出设备
  6. libswresample
    音频重采样
  7. libswscale
    格式转换, 宽高缩放
  8. libpostproc
    后期处理

FFmpeg常用函数

  1. av_register_all
    注册所有组件
  2. avformat_open_input
    打开输入视频文件
  3. avformat_find_stream_info
    获取视频文件信息
  4. avcodec_find_decoder
    查找解码器
  5. avcodec_open1
    打开解码器
  6. av_read_frame
    从输入文件读取一帧压缩数据
  7. avcodec_decode_video2
    解码一桢压缩数据
  8. avcodec_close
    关闭解码器
  9. avformat_close_input
    关闭输入视频文件

FFmpeg常用结构体

   AVFormatContext   // 上下文信息, 封装格式, 含AVStream []数组
   AVInputFormat     // 封装格式的名称, id, 扩展名
   AVStream          // 音频或视频的编码信息, 含AVCodecContext
   AVCodecContext    // 音视频的编解码详细信息, 如音频(采样率, 采样格式, 声道), 视频(图像格式, 宽高)
   AVCodec           // 音视频的编解码器, 名称, id, 
   AVPacket          // 一帧编码后的数据
   AVFrame           // 一帧解码后的数据

FFmpeg命令

  ffmpeg -i output.mp4 -i 1.ts -acodec copy  -ar 48000 out2.ts
  ffmpeg -i audio.mp3 -i video-with-audio.avi -map 0:0 -map 1:0 -acodec copy -vcodec copy -shortest output.avi
  ffmpeg -i musicshort.wav -i movie.avi final_video.avi
  ffmpeg -i music.mp3 music.wav
  ffmpeg -i video.flv -vn audio.mp3
  ffmpeg -i 0.ts -vn -acodec copy -absf aac_adtstoasc output.mp4
  ffmpeg -i output.mp4 -i 1.txt -map_metadata 1 -c:a copy -id3v2_version 3 -write_id3v1 1 output2.mp4
  ffmpeg -i out2.ts -metadata title="guo" -metadata artist="guo" -metadata track="11" -metadata date="2012" -metadata album="guo" -c:a copy -id3v2_version 3 -write_id3v1 1 -y out4.ts

我的代码库

https://github.com/gzx-miller/ffmpeg_sdl_player_vs2017

参考

http://ffmpeg.org/documentation.html

http://wiki.libsdl.org/FrontPage

https://blog.csdn.net/leixiaohua1020/article/details/10528443?utm_source=blogxgwz1

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值