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 輸入視頻文件解碼

 

參考:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章