基于ffmpeg的推流示例
? 流媒體(streaming media)是指將一連串的媒體數(shù)據(jù)壓縮后,經(jīng)過(guò)網(wǎng)上分段發(fā)送數(shù)據(jù),在網(wǎng)上即時(shí)傳輸影音以供觀賞的一種技術(shù)與過(guò)程,此技術(shù)使得數(shù)據(jù)包得以像流水一樣發(fā)送;如果不使用此技術(shù),就必須在使用前下載整個(gè)媒體文件。流式傳輸可傳送現(xiàn)場(chǎng)影音或預(yù)存于服務(wù)器上的影片,當(dāng)觀看者在收看這些影音文件時(shí),影音數(shù)據(jù)在送達(dá)觀看者的計(jì)算機(jī)后立即由特定播放軟件播放。
RTMP是Real Time Messaging Protocol(實(shí)時(shí)消息傳輸協(xié)議)的首字母縮寫(xiě)。該協(xié)議基于TCP,是一個(gè)協(xié)議族,包括RTMP基本協(xié)議及RTMPT/RTMPS/RTMPE等多種變種。RTMP是一種設(shè)計(jì)用來(lái)進(jìn)行實(shí)時(shí)數(shù)據(jù)通信的網(wǎng)絡(luò)協(xié)議,主要用來(lái)在Flash/AIR平臺(tái)和支持RTMP協(xié)議的流媒體/交互服務(wù)器之間進(jìn)行音視頻和數(shù)據(jù)通信。支持該協(xié)議的軟件包括Adobe Media Server/Ultrant Media Server/red5等。RTMP與HTTP一樣,都屬于TCP/IP四層模型的應(yīng)用層。
RTMP 是一種基于 TCP 的、用于數(shù)據(jù)、音頻和視頻傳輸?shù)碾p向通信協(xié)議。大部分具備行業(yè)標(biāo)準(zhǔn)的編碼器(如 encoding.com、Bitmovin、Harmonic 和 AWS Elemental 等)都能夠生產(chǎn) RTMP 數(shù)據(jù)源。同樣,Twitch、YouTube、Facebook Live 等流媒體服務(wù)和 Dacast、Ant Media、Wowza 等直播平臺(tái)都能接收 RTMP 推流。
利用FFMPEG進(jìn)行視頻編碼,通過(guò)RTMP將視頻流推送到nginx服務(wù)端。FFMPEG推流過(guò)程和視頻編碼過(guò)程是一樣的,視頻流本身也是屬于文件的一種,所以只需要正常實(shí)現(xiàn)視頻編碼即可。
FFMPEG視頻編碼示例:https://blog.csdn.net/weixin_44453694/article/details/123885112
nginx服務(wù)器搭建示例:https://blog.csdn.net/weixin_44453694/article/details/126511614
FFMPEG視頻解碼示例:https://blog.csdn.net/weixin_44453694/article/details/127098011
- 推流示例
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VIDEO_DEV "/dev/video0"
char file_name[100]="rtmp://124.21.108.66:8088/live/ashui";//視頻編碼文件名
#define STREAM_FRAME_RATE 25 /*每秒25幀*/
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /*圖像格式y(tǒng)uv420p*/
#define STREAM_DURATION 10.0 /*錄制時(shí)間10s*/
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;//互斥鎖
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//條件變量
int width;
int height;
int size;
int mp4_decode_stat=0;
unsigned char *rgb_buff=NULL;
unsigned char video_flag=1;
void *Video_CollectImage(void *arg);
void *Video_savemp4(void*arg);
typedef enum
{
false=0,
true,
}bool;
typedef struct OutputStream
{
AVStream *st;
AVCodecContext *enc;
int64_t next_pts;/*將生成的下一幀的pts*/
AVFrame *frame;/*保存編解碼數(shù)據(jù)*/
AVFrame *tmp_frame;
struct SwsContext *sws_ctx;
struct SwrContext *swr_ctx;
}OutputStream;
typedef struct IntputDev
{
AVCodecContext *pcodecCtx;
AVCodec *pCodec;
AVFormatContext *v_ifmtCtx;
int videoindex;//視頻幀ID
struct SwsContext *img_convert_ctx;
AVPacket *in_packet;
AVFrame *pFrame,*pFrameYUV;
}IntputDev;
IntputDev video_input={0};//視頻輸入流
/*添加一個(gè)輸出流*/
static void add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
AVCodecContext *c;
int i;
/*查找編碼器*/
*codec=avcodec_find_encoder(codec_id);
if(*codec==NULL)
{
printf("Could not find encoder for ' %s' n",avcodec_get_name(codec_id));
exit(1);
}
/*向媒體文件添加新流。*/
ost->st=avformat_new_stream(oc,NULL);
if(ost->st==NULL)
{
printf("Could not allocate stream n");
exit(1);
}
ost->st->id=oc->nb_streams-1;
/*分配AvcodeContext并將其字段設(shè)置為默認(rèn)值*/
c=avcodec_alloc_context3(*codec);
if(c==NULL)
{
printf("avcodec_alloc_context3 failed n");
}
ost->enc=c;
switch((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
break;
case AVMEDIA_TYPE_VIDEO:/*視頻流*/
c->codec_id=codec_id;
c->bit_rate=2500000;//比特率
/*分辨率必須是2的倍數(shù)。*/
c->width=width;
c->height=height;
/*時(shí)基:這是時(shí)間的基本單位(秒)
其中幀時(shí)間戳被表示。對(duì)于固定fps內(nèi)容,時(shí)基應(yīng)為1/幀率,時(shí)間戳增量應(yīng)與1相同*/
ost->st->time_base=(AVRational){1,STREAM_FRAME_RATE};
c->time_base=ost->st->time_base;
c->gop_size=12;/*最多每12幀發(fā)射一幀*/
c->pix_fmt=STREAM_PIX_FMT;/*圖像格式*/
if(c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
/*
為了測(cè)試,我們還添加了B幀
*/
c->max_b_frames=2;
}
if(c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
{
/*
需要避免使用某些系數(shù)溢出的宏塊。
這種情況不會(huì)發(fā)生在普通視頻中,只會(huì)發(fā)生在這里
色度平面的運(yùn)動(dòng)與亮度平面不匹配。*/
c->mb_decision=2;
}
break;
default:
break;
}
/*有些格式希望流頭是分開(kāi)的。*/
if(oc->oformat->flags & AVFMT_GLOBALHEADER)
{
c->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
/*視頻輸出*/
static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
AVFrame *picture;
int ret;
/*
分配AVFrame并將其字段設(shè)置為默認(rèn)值。結(jié)果
必須使用av_frame_free()釋放結(jié)構(gòu)。
*/
picture=av_frame_alloc();
if(picture==NULL)
{
return NULL;
}
picture->format=pix_fmt;
picture->width=width;
picture->height=height;
/*為幀數(shù)據(jù)分配緩沖區(qū)*/
ret=av_frame_get_buffer(picture,32);/*緩沖區(qū)以32位對(duì)齊*/
if(ret<0)
{
printf("Could not allocate frame datan");/*無(wú)法分配幀數(shù)據(jù)*/
exit(1);
}
return picture;
}
static void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
int ret;
AVCodecContext *c=ost->enc;
AVDictionary *opt=NULL;
av_dict_copy(&opt,opt_arg, 0);
/*初始化AvcodeContext以使用給定的AVCodec*/
ret=avcodec_open2(c, codec,&opt);
/*釋放為AVDictionary結(jié)構(gòu)分配的所有內(nèi)存*以及所有鍵和值。*/
av_dict_free(&opt);
if(ret<0)
{
printf("could not open video codec :%sn",av_err2str(ret));//無(wú)法打開(kāi)視頻編解碼器
exit(1);
}
/*視頻輸出*/
ost->frame=alloc_picture(AV_PIX_FMT_YUV420P,c->width,c->height);
if(ost->frame==NULL)
{
printf("could not allocate video framen");/*無(wú)法分配視頻幀*/
exit(1);
}
printf("ost->frame alloc success fmt=%d w=%d h=%dn",c->pix_fmt,c->width,c->height);
ost->tmp_frame=NULL;
if(c->pix_fmt!=AV_PIX_FMT_YUV420P)
{
ost->tmp_frame=alloc_picture(AV_PIX_FMT_YUV420P,c->width, c->height);/*視頻幀格式*/
if(ost->tmp_frame==NULL)
{
printf("conld not allocate temporary picturen");/*無(wú)法分配臨時(shí)幀*/
exit(1);
}
}
/*根據(jù)提供的編解碼器上下文中的值填充參數(shù)結(jié)構(gòu)。*/
ret=avcodec_parameters_from_context(ost->st->codecpar,c);
if(ret)
{
printf("Could not copy the stream parametersn");/*無(wú)法復(fù)制流參數(shù)*/
exit(1);
}
}
static void log_packet(const AVFormatContext * fmt_ctx, const AVPacket * pkt)
{
AVRational *time_base=&fmt_ctx->streams[pkt->stream_index]->time_base;
printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%dn",
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
pkt->stream_index);
}
/*寫(xiě)入數(shù)據(jù)*/
static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
/*數(shù)據(jù)包中的有效時(shí)間字段(時(shí)間戳/持續(xù)時(shí)間)從一個(gè)時(shí)基轉(zhuǎn)換為另一個(gè)時(shí)基。*/
av_packet_rescale_ts(pkt,*time_base,st->time_base);
pkt->stream_index=st->index;
/*打印信息*/
//log_packet(fmt_ctx, pkt);
return av_interleaved_write_frame(fmt_ctx, pkt);/*將數(shù)據(jù)包寫(xiě)入輸出媒體文件,確保正確的交織。*/
}
static int write_video_frame(AVFormatContext * oc, OutputStream * ost,AVFrame *frame)
{
int ret;
AVCodecContext *c;
int got_packet=0;
AVPacket pkt={0};
if(frame==(void *)-1)return 1;
c=ost->enc;
/*使用默認(rèn)值初始化數(shù)據(jù)包*/
av_init_packet(&pkt);
/*對(duì)一幀視頻進(jìn)行編碼。*/
ret=avcodec_encode_video2(c,&pkt,frame,&got_packet);
if(ret<0)
{
printf("Error encoding video frame:%sn",av_err2str(ret));//編碼視頻流錯(cuò)誤
exit(1);
}
/*
printf("--------vidoe pkt.pts=%sn",av_ts2str(pkt.pts));
printf("------st.num=%d st.den=%d codec.num=%d codec.den=%d-------n",ost->st->time_base.num,
ost->st->time_base.den,
c->time_base.num,
c->time_base.den);
*/
if(got_packet)
{
ret=write_frame(oc,&c->time_base,ost->st,&pkt);/*寫(xiě)入流數(shù)據(jù)*/
}
else
{
ret=0;
}
if(ret<0)
{
printf("Error while writing video frame:%sn",av_err2str(ret));/*寫(xiě)入流出錯(cuò)*/
exit(1);
}
return (frame||got_packet)?0:1;
}
static AVFrame *get_video_frame(OutputStream *ost,IntputDev* input, int *got_pic)
{
int ret,got_picture;
AVCodecContext *c=ost->enc;
AVFrame *ret_frame=NULL;
/*在各自的時(shí)基中比較兩個(gè)時(shí)間戳。*/
if(av_compare_ts(ost->next_pts,c->time_base,STREAM_DURATION, (AVRational){1,1})>=0)
{
//return (void*)-1;
}
/*確保幀數(shù)據(jù)可寫(xiě),盡可能避免數(shù)據(jù)復(fù)制。*/
if(av_frame_make_writable(ost->frame)<0)
{
exit(1);
}
/*此函數(shù)返回文件中存儲(chǔ)的內(nèi)容,并且不驗(yàn)證是否存在解碼器的有效幀。*/
if(av_read_frame(input->v_ifmtCtx,input->in_packet)>=0)
{
if(input->in_packet->stream_index == input->videoindex)
{
/*解碼一幀視頻數(shù)據(jù)。輸入一個(gè)壓縮編碼的結(jié)構(gòu)體AVPacket,輸出一個(gè)解碼后的結(jié)構(gòu)體AVFrame*/
ret=avcodec_decode_video2(input->pcodecCtx, input->pFrame,&got_picture,input->in_packet);
*got_pic=got_picture;
if(ret<0)
{
printf("Decode Error.n");
av_packet_unref(input->in_packet);
return NULL;
}
if(got_picture)
{
sws_scale(input->img_convert_ctx, (const unsigned char * const *)input->pFrame->data,input->pFrame->linesize,0,input->pcodecCtx->height,input->pFrameYUV->data,input->pFrameYUV->linesize);
sws_scale(input->img_convert_ctx, (const unsigned char * const *)input->pFrame->data,input->pFrame->linesize,0,input->pcodecCtx->height,ost->frame->data,ost->frame->linesize);
pthread_mutex_lock(&fastmutex);//互斥鎖上鎖
memcpy(rgb_buff,input->pFrameYUV->data[0],size);
pthread_cond_broadcast(&cond);//廣播喚醒所有線程
pthread_mutex_unlock(&fastmutex);//互斥鎖解鎖
ost->frame->pts=ost->next_pts++;
//水印添加處理
//frame->frame->format=AV_PIX_FMT_YUV420P;
//AVFrame *frame_out=av_frame_alloc();
//unsigned char *frame_buffer_out;
//frame_buffer_out=(unsigned char *)av_malloc(size);
//av_image_fill_arrays(frame_out->data,frame_out->linesize,frame_buffer_out,AV_PIX_FMT_YUV420P,width,height,32);
//添加水印,調(diào)用libavfilter庫(kù)實(shí)現(xiàn)
//time_t sec;
//sec=time(NULL);
//struct tm* today = localtime(&sec);
//char sys_time[64];
//strftime(sys_time, sizeof(sys_time), "%Y/%m/%d %H:%M:%S", today);
//waterMark(ost->frame,frame_out,width,height,sys_time);
//yuv420p,y表示亮度,uv表示像素顏色
//ost->frame=frame_out;
//ost->frame->pts=ost->next_pts++;
ret_frame=ost->frame;
}
}
av_packet_unref(input->in_packet);
}
return ret_frame;
}
static void close_stream(AVFormatContext * oc, OutputStream * ost)
{
avcodec_free_context(&ost->enc);
av_frame_free(&ost->frame);
av_frame_free(&ost->tmp_frame);
sws_freeContext(ost->sws_ctx);
swr_free(&ost->swr_ctx);
}
int main()
{
/*創(chuàng)建攝像頭采集線程*/
pthread_t pthid[2];
pthread_create(&pthid[0],NULL,Video_CollectImage, NULL);
pthread_detach(pthid[0]);/*設(shè)置分離屬性*/
sleep(1);
while(1)
{
if(width!=0 && height!=0 && size!=0)break;
if(video_flag==0)return 0;
}
printf("image:%d * %d,%dn",width,height,size);
unsigned char *rgb_data=malloc(size);
/*創(chuàng)建mp4視頻編碼線程*/
pthread_create(&pthid[1],NULL,Video_savemp4, NULL);
pthread_detach(pthid[1]);/*設(shè)置分離屬性*/
int count=0;
mp4_decode_stat=1;
pause();
pthread_mutex_destroy(&fastmutex);/*銷毀互斥鎖*/
pthread_cond_destroy(&cond);/*銷毀條件變量*/
free(rgb_buff);
free(rgb_data);
return 0;
}
void *Video_CollectImage(void *arg)
{
int res=0;
AVFrame *Input_pFrame=NULL;
AVFrame *Output_pFrame=NULL;
printf("pth:%sn",avcodec_configuration());
/*注冊(cè)設(shè)備*/
avdevice_register_all();
/*查找輸入格式*/
AVInputFormat *ifmt=av_find_input_format("video4linux2");
if(ifmt==NULL)
{
printf("av_find_input_format failedn");
video_flag=0;
return 0;
}
/*打開(kāi)輸入流并讀取頭部信息*/
AVFormatContext *ps=NULL;
//分配一個(gè)AVFormatContext。
ps=avformat_alloc_context();
res=avformat_open_input(&ps,VIDEO_DEV,ifmt,NULL);
if(res)
{
printf("open input failedn");
video_flag=0;
return 0;
}
/*查找流信息*/
res=avformat_find_stream_info(ps,NULL);
if(res)
{
printf("find stream failedn");
video_flag=0;
return 0;
}
/*打印有關(guān)輸入或輸出格式信息*/
av_dump_format(ps, 0, "video4linux2", 0);
/*尋找視頻流*/
int videostream=-1;
videostream=av_find_best_stream(ps,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
printf("videostram=%dn",videostream);
/*尋找編解碼器*/
AVCodec *video_avcodec=NULL;/*保存解碼器信息*/
AVStream *stream = ps->streams[videostream];
AVCodecContext *context=stream->codec;
video_avcodec=avcodec_find_decoder(context->codec_id);
if(video_avcodec==NULL)
{
printf("find video decodec failedn");
video_flag=0;
return 0;
}
/*初始化音視頻解碼器*/
res=avcodec_open2(context,video_avcodec,NULL);
if(res)
{
printf("avcodec_open2 failedn");
video_flag=0;
return 0;
}
AVPacket *packet=av_malloc(sizeof(AVPacket));/*分配包*/
AVFrame *frame=av_frame_alloc();/*分配視頻幀*/
AVFrame *frameyuv=av_frame_alloc();/*申請(qǐng)YUV空間*/
/*分配空間,進(jìn)行圖像轉(zhuǎn)換*/
width=context->width;
height=context->height;
int fmt=context->pix_fmt;/*流格式*/
size=av_image_get_buffer_size(AV_PIX_FMT_YUV420P,width,height,16);
unsigned char *buff=NULL;
printf("w=%d,h=%d,size=%dn",width,height,size);
buff=av_malloc(size);
rgb_buff=malloc(size);//保存RGB顏色數(shù)據(jù)
/*存儲(chǔ)一幀圖像數(shù)據(jù)*/
av_image_fill_arrays(frameyuv->data,frameyuv->linesize,buff,AV_PIX_FMT_YUV420P,width,height, 16);
/*轉(zhuǎn)換上下文,使用sws_scale()執(zhí)行縮放/轉(zhuǎn)換操作。*/
struct SwsContext *swsctx=sws_getContext(width,height, fmt,width,height, AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
/*視頻輸入流信息*/
video_input.img_convert_ctx=swsctx;//格式轉(zhuǎn)換上下文
video_input.in_packet=packet;//數(shù)據(jù)包
video_input.pcodecCtx=context;
video_input.pCodec=video_avcodec;/*保存解碼器信息*/
video_input.v_ifmtCtx=ps;//輸入流并讀取頭部信息
video_input.videoindex=videostream;/*視頻流*/
video_input.pFrame=frame;/*視頻幀*/
video_input.pFrameYUV=frameyuv;/*申請(qǐng)YUV空間*/
//水印添加處理
frameyuv->width=width;
frameyuv->height=height;
frameyuv->format=AV_PIX_FMT_YUV420P;
AVFrame *frame_out=av_frame_alloc();
unsigned char *frame_buffer_out;
frame_buffer_out=(unsigned char *)av_malloc(size);
av_image_fill_arrays(frame_out->data,frame_out->linesize,frame_buffer_out,AV_PIX_FMT_YUV420P,width,height,16);
/*讀幀*/
char *p=NULL;
int go=0;
int Framecount=0;
time_t sec,sec2;
char sys_time[64];
while(video_flag)
{
if(!mp4_decode_stat)
{
res=av_read_frame(ps,packet);//讀取數(shù)據(jù)
if(res>=0)
{
if(packet->stream_index == AVMEDIA_TYPE_VIDEO)//視頻流
{
/*解碼一幀視頻數(shù)據(jù)。輸入一個(gè)壓縮編碼的結(jié)構(gòu)體AVPacket,輸出一個(gè)解碼后的結(jié)構(gòu)體AVFrame*/
res=avcodec_decode_video2(ps->streams[videostream]->codec,frame,&go,packet);
if(res<0)
{
printf("avcodec_decode_video2 failedn");
break;
}
if(go)
{
/*轉(zhuǎn)換像素的函數(shù)*/
sws_scale(swsctx,(const uint8_t * const*)frame->data,frame->linesize,0,context->height,frameyuv->data,frameyuv->linesize);
//添加水印,調(diào)用libavfilter庫(kù)實(shí)現(xiàn)
sec=time(NULL);
if(sec!=sec2)
{
struct tm* today = localtime(&sec);
strftime(sys_time, sizeof(sys_time), "%Y/%m/%d %H:%M:%S", today);
}
waterMark(frameyuv,frame_out,width,height,sys_time);
//yuv420p,y表示亮度,uv表示像素顏色
p=frame_buffer_out;
memcpy(p,frame_out->data[0],frame_out->height*frame_out->width);//y,占用空間w*h
p+=frame_out->width*frame_out->height;
memcpy(p,frame_out->data[1],frame_out->height/2*frame_out->width/2);//u,占用空間(w/2)*(h/2)
p+=frame_out->height/2*frame_out->width/2;
memcpy(p,frame_out->data[2],frame_out->height/2*frame_out->width/2);//v,占用空間(w/2)*(h/2)
p+=frame_out->height/2*frame_out->width/2;
pthread_mutex_lock(&fastmutex);//互斥鎖上鎖
memcpy(rgb_buff,frame_buffer_out,size);
pthread_cond_broadcast(&cond);//廣播喚醒所有線程
pthread_mutex_unlock(&fastmutex);//互斥鎖解鎖
}
}
}
}
}
sws_freeContext(swsctx);/*釋放上下文*/
av_frame_free(&frameyuv);/*釋放YUV空間*/
av_packet_unref(packet);/*釋放包*/
av_frame_free(&frame);/*釋放視頻幀*/
avformat_close_input(&ps);/*關(guān)閉流*/
sws_freeContext(video_input.img_convert_ctx);
avcodec_close(video_input.pcodecCtx);
av_free(video_input.pFrameYUV);
av_free(video_input.pFrame);
avformat_close_input(&video_input.v_ifmtCtx);
video_flag=0;
pthread_exit(NULL);
}
/*MP4格式數(shù)據(jù)保存*/
void *Video_savemp4(void*arg)
{
while(1)
{
if(mp4_decode_stat)
{
int res;
AVFormatContext *oc=NULL;
AVDictionary *opt=NULL;
/* 創(chuàng)建的AVFormatContext結(jié)構(gòu)體。*/
avformat_alloc_output_context2(&oc,NULL,"flv",NULL);//通過(guò)文件名創(chuàng)建
if(oc==NULL)
{
printf("為輸出格式分配AVFormatContext失敗n");
avformat_alloc_output_context2(&oc,NULL,"flv",NULL);//通過(guò)文件名創(chuàng)建
return 0;
}
if(oc==NULL)return (void*)1;
/*輸出流信息*/
AVOutputFormat *ofmt=oc->oformat;
printf("ofmt->video_codec=%dn",ofmt->video_codec);
int have_video=1;
int encode_video=0;
OutputStream video_st={0};
if(ofmt->video_codec !=AV_CODEC_ID_NONE)
{
/*添加一個(gè)輸出流*/
add_stream(&video_st,oc,&video_input.pCodec,ofmt->video_codec);
have_video=1;
encode_video=1;
}
printf("w=%d,h=%d,size=%dn",width,height,size);
/*視頻幀處理*/
if(have_video)open_video(oc,video_input.pCodec,&video_st,opt);
printf("打開(kāi)輸出文件成功rn");
/*打印有關(guān)輸入或輸出格式信息*/
printf("file_name=%sn",file_name);
av_dump_format(oc, 0,file_name,1);
if(!(ofmt->flags & AVFMT_NOFILE))
{
/*打開(kāi)輸出文件,成功之后創(chuàng)建的AVFormatContext結(jié)構(gòu)體*/
res=avio_open(&oc->pb,file_name,AVIO_FLAG_WRITE);
if(res<0)
{
printf("%s open failed :%sn",file_name,av_err2str(res));
return (void*)1;
}
}
/*寫(xiě)入流數(shù)據(jù)頭*/
res=avformat_write_header(oc,&opt);
if(res<0)
{
printf("open output faile:%sn",av_err2str(res));
return (void*)1;
}
if(res<0)
{
printf("open output faile:%sn",av_err2str(res));
return (void*)1;
}
int got_pic;
while(encode_video)
{
/*獲取流數(shù)據(jù)*/
AVFrame *frame=get_video_frame(&video_st,&video_input,&got_pic);
if(!got_pic || frame==NULL)
{
usleep(100);
continue;
}
encode_video=!write_video_frame(oc,&video_st,frame);
}
/*將流數(shù)據(jù)寫(xiě)入輸出媒體文件并釋放文件私有數(shù)據(jù)。*/
av_write_trailer(oc);
/*關(guān)閉AVIOContext*s訪問(wèn)的資源,釋放它,并將指向它的指針設(shè)置為NULL*/
if(!(ofmt->flags & AVFMT_NOFILE))avio_closep(&oc->pb);
/*釋放AVFormatContext及其所有流。*/
avformat_free_context(oc);
/*關(guān)閉流*/
if(have_video)close_stream(oc, &video_st);
mp4_decode_stat=0;
}
}
}
-
服務(wù)器
+關(guān)注
關(guān)注
12文章
9109瀏覽量
85310 -
ffmpeg
+關(guān)注
關(guān)注
0文章
46瀏覽量
7393 -
rtmp
+關(guān)注
關(guān)注
0文章
7瀏覽量
1586
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論