#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {
#include <libavutil/time.h>
#include <libavutil/timestamp.h>
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <opencv2/opencv.hpp>
int main(int argc,char** argv){
//****初始化,分配内存,声明参数****//
av_register_all();
avcodec_register_all();
AVPacket decodePacket;
AVPacket encodePacket;
av_init_packet(&decodePacket);
AVCodec * pDecodec = NULL;
AVCodec * pEncodec = NULL;
AVFrame * pInFrame = av_frame_alloc();
AVFrame * pOutFrame = av_frame_alloc();
AVFormatContext * pInFormatContext = avformat_alloc_context();
AVFormatContext * pOutFormatContext = avformat_alloc_context();
AVInputFormat * pInFormat = NULL;
AVOutputFormat * pOutFormat = NULL;
AVStream * pInStream = NULL;
AVStream * pOutStream = NULL;
//****设置参数****//
const char * inFileName = "/home/wdh/videos/out_no_vioce_fps10_960x540.mp4"; //这个只有视频轨,没有音轨
const char * outFileName = "rtmp://127.0.0.1:1935/live/hahaha"; //nginx 流服务器地址
//****打开输入流****//
int ret = avformat_open_input(&pInFormatContext,inFileName,NULL,NULL); //打开上下文,上下文的内存在前面分配
if(ret < 0){
printf("Open input formatcontext failed,%d\n",ret);
return 1;
}
if ((ret = avformat_find_stream_info(pInFormatContext, 0)) < 0) {
printf("Failed to retrieve input stream information");
return 1;
}
av_dump_format(pInFormatContext,0,inFileName,0); //显示一下
pInStream = pInFormatContext->streams[0]; //赋值
pDecodec = avcodec_find_decoder(pInStream->codec->codec_id); //找到解码器
if(!pDecodec){
printf("Find decoder failed\n");
return 1;
}
ret = avcodec_open2(pInStream->codec,pDecodec,NULL); //打开输入流
if(ret < 0){
printf("Open instream failed,%d\n",ret);
return 1;
}
//****计算输入流信息****//
AVRational frame_rate;
double duration;
frame_rate = av_guess_frame_rate(pInFormatContext, pInStream, NULL); //获取码率
// frame_rate = {25,1};
duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); //计算延时
//****分配输出上下文****//
bool push_stream = false;
char * outFormatName = NULL;
if (strstr(outFileName, "rtmp://") != NULL) {
push_stream = true;
outFormatName = "flv";
}
else if (strstr(outFileName, "udp://") != NULL) {
push_stream = true;
outFormatName = "mpegts";
}
else {
push_stream = false;
outFormatName = NULL;
}
avformat_alloc_output_context2(&pOutFormatContext, NULL, outFormatName, outFileName);
//****创建输出流****//
pEncodec = avcodec_find_encoder(pInStream->codec->codec_id); //找到编码器
AVCodecContext * pEncodeContext = avcodec_alloc_context3(pEncodec); //分配编码上下文
// pEncodeContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER | AV_CODEC_FLAG_LOW_DELAY;
av_opt_set(pEncodeContext->priv_data,"tune","zerolatency",10);
//一组图片的数量
pEncodeContext->gop_size = 2;
//去掉B帧只留下 I帧和P帧
pEncodeContext->max_b_frames = 0;
pOutStream = avformat_new_stream(pOutFormatContext,NULL); //分配流空间
//设置输出流的参数
//编码器参数
pEncodeContext->width = pInStream->codec->width;
pEncodeContext->height = pInStream->codec->height;
pEncodeContext->sample_aspect_ratio = pInStream->codec->sample_aspect_ratio; //采样率
// if (pEncodec->pix_fmts){ // 编码器支持的像素格式列表
// pEncodeContext->pix_fmt = pEncodec->pix_fmts[0]; // 编码器采用所支持的第一种像素格式
// }
// else{
pEncodeContext->pix_fmt = pInStream->codec->pix_fmt; // 编码器采用解码器的像素格式
// }
pEncodeContext->time_base = av_inv_q(pInStream->codec->framerate); // 时基:解码器帧率取倒数
pEncodeContext->framerate = pInStream->codec->framerate;
// pEncodeContext->time_base.num *= 3.0;
// pEncodeContext->framerate.den *= 3.0;
if (pOutFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
{
pEncodeContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
ret = avcodec_open2(pEncodeContext, pEncodec, NULL);
if (ret < 0){
printf("打开编码器");
return 1;
}
ret = avcodec_parameters_from_context(pOutFormatContext->streams[0]->codecpar, pEncodeContext);
if (ret < 0){
av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", ret);
return ret;
}
// 拷贝输入流信息到输出流
// ret = avcodec_parameters_copy(pOutStream->codecpar, pInStream->codecpar);
// if (ret < 0) {
// printf("copy 编解码器上下文失败\n");
// }
// pOutStream->codecpar->codec_tag = 0;
// ret = avcodec_open2(pOutStream->codec,pEncodec,NULL); // 打开输出流
// if(ret < 0){
// printf("Open output stream faild,%d\n",ret);
// }
pOutStream = pOutFormatContext->streams[0];
//****分配推流io上下文****//
ret = avio_open(&pOutFormatContext->pb, outFileName, AVIO_FLAG_WRITE);
if (ret < 0) {
printf("Could not open output file '%s'\n", outFileName);
return -1;
}
ret = avformat_write_header(pOutFormatContext, NULL); //写入头
// pOutFormatContext->streams[0]->time_base.num *= 10;
if (ret < 0) {
printf("Error occurred when opening output file\n");
return -1;
}
//****处理主流程****//
int gotPicture=0;
int index = 0;
while(av_read_frame(pInFormatContext,&decodePacket)>=0){
// gotPicture = 0;
// ret = avcodec_decode_video2(pInStream->codec, pInFrame, &gotPicture, &decodePacket); //旧接口,解码
ret = avcodec_send_packet(pInStream->codec,&decodePacket);
ret = avcodec_receive_frame(pInStream->codec,pInFrame);
if(ret == -11){
continue;
}
if(ret < 0){
printf("decode error,%d\n",ret);
break;
}
// if(!pInFrame){
if(pInFrame){
// //自定义处理代码
// //**//
av_init_packet(&encodePacket);
encodePacket.data = NULL;
encodePacket.size = 0;
// ret = avcodec_encode_video2(pEncodeContext,&encodePacket,pInFrame,&gotPicture);
ret = avcodec_send_frame(pEncodeContext,pInFrame);
ret = avcodec_receive_packet(pEncodeContext,&encodePacket);
if(ret == -11){
// printf("encode wrong\n");
continue;
}
// 重新打时间戳
encodePacket.pts = decodePacket.pts;// + (int)(duration*AV_TIME_BASE);
encodePacket.dts = decodePacket.dts;// + (int)(duration*AV_TIME_BASE);
encodePacket.stream_index = 0;
printf("pts:%d , dts:%d , duration*AV_TIME_BASE:%d\n",encodePacket.pts,encodePacket.dts,(int)(duration*AV_TIME_BASE));
av_packet_rescale_ts(&encodePacket, pInStream->time_base, pOutStream->time_base);
encodePacket.pos = -1;
// 4. 将编码后的packet写入输出媒体文件
ret = av_interleaved_write_frame(pOutFormatContext, &encodePacket);
// av_packet_unref(&encodePacket);
}else{
// av_usleep((int64_t)(duration*AV_TIME_BASE)); //延时 模拟处理 以后似乎需要用处理流程取减
decodePacket.stream_index = 0;
//延时 更新packet中的pts和dts
av_packet_rescale_ts(&decodePacket,pInStream->time_base, pOutStream->time_base);
decodePacket.pos = -1;
printf("1-%d\n",decodePacket.size);
//将packet写入输出流 ,推流
ret = av_interleaved_write_frame(pOutFormatContext, &decodePacket);
if (ret < 0) {
printf("Error muxing packet\n");
break;
}
}
av_packet_unref(&decodePacket); //重置流空间
}
// 写输出文件尾
av_write_trailer(pOutFormatContext);
//****释放内存****//
av_frame_free(&pInFrame);
av_frame_free(&pOutFrame);
avformat_free_context(pInFormatContext);
avformat_free_context(pOutFormatContext);
return 0;
}