藉助局域網的強大帶寬,需要開發一款醫學教學的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