ffmpeg解码推流研究

借助局域网的强大带宽,需要开发一款医学教学的app。大概的功能需求包括播放各种视频,播放各种摄像头,跨平台android,linux,mac,win等,支持输出左右眼双目,裸眼3d,红蓝3d,普通播放等功能,还需要对一些视频中的重要特征做标注。我立刻就瞄上了ffmpeg和opencv,一个用来解码,一个用来处理适时实时帧,堪称量身定做。

brew安装过程非常简单。只是在安装ffmpeg时出错了,ffplay一直装不上,后来下载源码编译成功。

ffmpeg安装成功后,可以使用命令测试,可在单机测试,我主要在局域网测试,没有去配置rstp服务器了,就直接使用udp测试。

推送视频流:
ffmpeg -re -i ~/work/VideoTest1.mp4 -vcodec copy -f mpegts udp://127.0.0.1:8000
使用ffplay播放视频流
ffplay -protocol_whitelist "file,udp,rtp" -i udp://127.0.0.1:8000

推送摄像头数据流:
ffmpeg -f avfoundation -framerate 30 -video_size 1280x720 -i "0" -vcodec libx264 -acodec libfaac -f mpegts udp://127.0.0.1:8000
使用ffplay播放视频流
ffplay -protocol_whitelist "file,udp,rtp" -i udp://127.0.0.1:8000

推送桌面流,这个可以用于企业分享PPT之类
ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -acodec libfaac -f mpegts udp://127.0.0.1:8000
使用ffplay播放视频流
ffplay -protocol_whitelist "file,udp,rtp" -i udp://127.0.0.1:8000

写一个简单的解码测试DEMO,解码视频帧。

#include <iostream>
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
};
#endif
 
 
 
int main(int argc, const char * argv[]) {
    // insert code here...
    
    avcodec_register_all();
    av_register_all();
    avformat_network_init();
    
    //输出支持解封装格式
    printf("======  av_input_format  =====\n");
    AVInputFormat *fmt = NULL;
    while ((fmt = av_iformat_next(fmt))) {
        printf("name : %s\n",fmt->name);
        printf("long_name : %s\n",fmt->long_name);
        printf("\n");
    }
    printf("==============================\n");
    
    AVFormatContext *pAVFormatCtx = NULL;
    pAVFormatCtx = avformat_alloc_context();
    //char filepath[] = "/Users/jiazhiguo/work/VideoTest1.mp4";
    if(argc<2)
    {
        //ShowUseage(argv[0]);
        return -1;
     }
    const char *filepath = argv[1];
    
    //打开文件
    char errorBuf[1024];
    int retOpenFile = avformat_open_input(&pAVFormatCtx, filepath, NULL, NULL);
    if (0 != retOpenFile){
        av_strerror(retOpenFile, errorBuf, sizeof(errorBuf));
        printf("Couldn't open file %s: %d(%s)\n", filepath, retOpenFile, errorBuf);
        return -1;
    }
    
    //输出文件信息
    printf("------------- File Information ------------------\n");
    av_dump_format(pAVFormatCtx,0,filepath,0);
    printf("-------------------------------------------------\n");
    
    //音视频分离
    int retFindStream = avformat_find_stream_info(pAVFormatCtx, NULL);
    if (0 != retFindStream){
        av_strerror(retFindStream, errorBuf, sizeof(errorBuf));
        printf("Couldn't find stream %s: %d(%s)\n", filepath, retFindStream, errorBuf);
        return -1;
    }
    
    int videoStreamIndex = -1;
    for (int i = 0; i < pAVFormatCtx->nb_streams;i++){
        AVStream *stream = pAVFormatCtx->streams[i];
        AVCodecParameters *codeParam = stream->codecpar;
        if (AVMEDIA_TYPE_VIDEO == codeParam->codec_type){
            videoStreamIndex = i;
            break;
        }
    }
    if (-1 == videoStreamIndex){
        printf("Didn't find a video stream.\n");
        return -1;
    }
    
    //视频流信息
    AVStream *videoStream = pAVFormatCtx->streams[videoStreamIndex];
    AVCodecParameters *codeParam = videoStream->codecpar;
    AVCodecContext *pAVCodeCtx = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(pAVCodeCtx, codeParam);
    if (0 == pAVCodeCtx){
        printf("Couldn't create AVCodecContext\n");
        return -1;
    }
    
    //查找视频解码器
    AVCodecID videoCodeId = codeParam->codec_id;
    AVCodec *videoDeCode = avcodec_find_decoder(videoCodeId);
    if(videoDeCode == NULL){
        printf("Codec not found.\n");
        return -1;
    }
    
    //打开视频解码器
    int retOpenVideoDecode = avcodec_open2(pAVCodeCtx, videoDeCode, NULL);
    if (retOpenVideoDecode != 0){
        av_strerror(retOpenVideoDecode, errorBuf, sizeof(errorBuf));
        printf("open decode Error. %s\n",errorBuf);
        return -1;
    }
    
    AVPacket *avPacket = av_packet_alloc();
    AVFrame *avVideoFrame = av_frame_alloc();
    
    bool bFirstFrame = false;
    while (1) {
        //从原始数据读取一帧
        av_read_frame(pAVFormatCtx, avPacket);
        if (avPacket->stream_index == videoStreamIndex){
            //送往解码器
            int retPackt = avcodec_send_packet(pAVCodeCtx, avPacket);
            if (retPackt < 0){
                av_strerror(retPackt, errorBuf, sizeof(errorBuf));
                printf("packet Error. %s\n",errorBuf);
                continue;
            }
            //从解码器获取一帧
            int retDcode = avcodec_receive_frame(pAVCodeCtx, avVideoFrame);
            if(retDcode < 0){
                av_strerror(retDcode, errorBuf, sizeof(errorBuf));
                printf("Decode Error. %s\n",errorBuf);
                continue;
            }else{
                bFirstFrame = true;
                break;
            }
 
        }
    }
    
    if (bFirstFrame){
        //todo 图像处理
    }
    
    //资源释放
    av_frame_free(&avVideoFrame);
    av_packet_free(&avPacket);
    avcodec_close(pAVCodeCtx);
    avformat_close_input(&pAVFormatCtx);
    avformat_network_deinit();
    
    return 0;
 
}

写一个ffmpeg的makefile文件

SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.c = .o)
CC = gcc
INCLUDES = -I/usr/local/Cellar/ffmpeg/4.2.2_2/include
LIBS = -L/usr/local/Cellar/ffmpeg/4.2.2_2/lib -lavformat -lavcodec -lswscale -lswresample -lavutil -lavfilter -lavdevice -lpostproc
LIBS +=  -lm -lz -lbz2 -llzma -pthread
CCFLAGS = -g -Wall -O0

my_app : $(OBJS)
	$(CC) $^ -o $@ $(INCLUDES) $(LIBS)

%.o : %.c
	$(CC) -c $< $(CCFLAGS)
clean:
	rm *.o
.PHONY:clean

执行make,生成app

my_app 输入视频文件解码

扫描二维码关注公众号,回复: 16862402 查看本文章

参考:

FFMPEG demo参考;

https://blog.csdn.net/zgq57609356/article/details/65443197
https://blog.csdn.net/m0_37684310/article/details/101776778

makefile参考:

http://blog.chinaunix.net/uid-25838286-id-3204219.html

https://blog.csdn.net/weixin_38391755/article/details/80380786

猜你喜欢

转载自blog.csdn.net/blogercn/article/details/105942491