转: FFMpeg 封装MP4 文件
2017-05-24 15:36
2351 查看
FFmpeg 封装MP4文件的一个例子
项目中用到FFmpeg 将IP摄像头的视频流的保存MP4文件的。之前的大哥将它先存成了H264文件,然后又调用FFMpeg的命令行去实现转码为MP4。感觉比较麻烦和拙劣 。网上看了很多,本身自己也是菜鸟。发一篇别人的 原文:最简单的FFmpeg封装MP4文件
给那个代码添加了一些注释,本人刚接触,不对的地方还请大家纠正。
const char* SRC_FILE = "1.mkv"; const char* OUT_FILE = "outfile.h264"; const char* OUT_FMT_FILE = "outfmtfile.mp4"; int main() { av_register_all(); AVFormatContext* pFormat = NULL; // 输入文件的Format if (avformat_open_input(&pFormat, SRC_FILE, NULL, NULL) < 0) { return 0; } AVCodecContext* video_dec_ctx = NULL; // 解码器上下文 AVCodec* video_dec = NULL; // 视频解码器 if (avformat_find_stream_info(pFormat, NULL) < 0) { return 0; } av_dump_format(pFormat, 0, SRC_FILE, 0); video_dec_ctx = pFormat->streams[0]->codec; video_dec = avcodec_find_decoder(video_dec_ctx->codec_id); if (avcodec_open2(video_dec_ctx, video_dec, NULL) < 0) { return 0; } AVFormatContext* pOFormat = NULL; AVOutputFormat* ofmt = NULL; if (avformat_alloc_output_context2(&pOFormat, NULL, NULL, OUT_FILE) < 0) // 下面有解析 { return 0; } ofmt = pOFormat->oformat; if (avio_open(&(pOFormat->pb), OUT_FILE, AVIO_FLAG_READ_WRITE) < 0) { return 0; } AVCodecContext *video_enc_ctx = NULL; // 视频编码器上下文 AVCodec *video_enc = NULL; // 视频编码器 video_enc = avcodec_find_encoder(AV_CODEC_ID_H264); // 指定编码器 AVStream *video_st = avformat_new_stream(pOFormat, video_enc); // 创建流 if (!video_st) return 0; // 设置了编码器的相关信息 video_enc_ctx = video_st->codec; video_enc_ctx->width = video_dec_ctx->width; video_enc_ctx->height = video_dec_ctx->height; video_enc_ctx->pix_fmt = PIX_FMT_YUV420P; video_enc_ctx->time_base.num = 1; video_enc_ctx->time_base.den = 25; video_enc_ctx->bit_rate = video_dec_ctx->bit_rate; video_enc_ctx->gop_size = 250; video_enc_ctx->max_b_frames = 10; //H264 //pCodecCtx->me_range = 16; //pCodecCtx->max_qdiff = 4; video_enc_ctx->qmin = 10; video_enc_ctx->qmax = 51; // 打开编码器 if (avcodec_open2(video_enc_ctx, video_enc, NULL) < 0) { printf("编码器打开失败!\n"); return 0; } printf("Output264video Information====================\n"); av_dump_format(pOFormat, 0, OUT_FILE, 1); printf("Output264video Information====================\n"); //mp4 file AVFormatContext* pMp4Format = NULL; // FormatContext - 格式上下文 AVOutputFormat* pMp4OFormat = NULL; // 输出格式 //可以初始化一个用于输出的AVFormatContext结构体,第二个参数如设成NULL格式有后面两个猜出 // 第三个参数为format_name 第四个参数为fileName if (avformat_alloc_output_context2(&pMp4Format, NULL, NULL, OUT_FMT_FILE) < 0) { return 0; } // 从输出格式上下文中知道确切的输出格式 pMp4OFormat = pMp4Format->oformat; // 创建并初始化一个AVIOConetxt为接收给定路径文件资源的接收 if (avio_open(&(pMp4Format->pb), OUT_FMT_FILE, AVIO_FLAG_READ_WRITE) < 0) { return 0; } // 循环看输入format中的流们,根据这个流创建输出流 for (int i = 0; i < pFormat->nb_streams; i++) { // 根据输入流创建输出流 AVStream *in_stream = pFormat->streams[i]; AVStream *out_stream = avformat_new_stream(pMp4Format, in_stream->codec->codec); if (!out_stream) { return 0; } // 拷贝输入的avcodecConText 给输出的AVCodecContext int ret = 0; ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { fprintf(stderr, "Failed to copy context from input to output stream codec context\n"); return 0; } // 这是干啥? out_stream->codec->codec_tag = 0; if (pMp4Format->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } //Print detailed information about the input or output format, such as //uration, bitrate, streams, container, programs, metadata, side data, //codec and time base. av_dump_format(pMp4Format, 0, OUT_FMT_FILE, 1); // 写视频文件头 if (avformat_write_header(pMp4Format, NULL) < 0) { return 0; } //// // 设置AVOption(选项设置-根据字符串设置结构体的属性值) av_opt_set(video_enc_ctx->priv_data, "preset", "superfast", 0); av_opt_set(video_enc_ctx->priv_data, "tune", "zerolatency", 0); avformat_write_header(pOFormat, NULL); // 再次写头? // AVPacket是存储压缩编码数据相关信息的结构体。 保存的是解码前的数据,也就是压缩后的数据。 // 该结构体不直接包含数据,其中有一个执行数据域的指针,ffmpeg中的很多数据结构都是用这种方法来管理 // 它保存了解复用之后,解码之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加信息,如 // 显示时间戳(pts)、解码时间戳(dts)、数据时长,所在媒体流的索引等 AVPacket *pkt = new AVPacket(); av_init_packet(pkt); AVFrame *pFrame = avcodec_alloc_frame(); //帧 int ts = 0; while (1) { // 作用是读取码流中的音频若干帧或者视频一帧 - 返回流的下一帧 if (av_read_frame(pFormat, pkt) < 0) { avio_close(pOFormat->pb); // 关闭流 - 输入的流哦 av_write_trailer(pMp4Format); // 给输出MP4文件 写文件件尾 avio_close(pMp4Format->pb); // 关闭流 - 输出的MP4文件的流 delete pkt; // 销毁pkt - 包 return 0; } // 根据pkt包中的流的索引来搞 0-对应的是视频流, 1 - 对应的是音频流 // stream_index 标识当前AVPacket所从属的码流 // 在前面的某个地方搞过 了 AVMEDIA_TYPE_VIDEO = 0,AVMEDIA_TYPE_AUDIO = 1; if (pkt->stream_index == 0) { int got_picture = 0, ret = 0; // 作用是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame // video_dec_ctx:编解码上下文环境,定义了编解码操作的一些细节; // got_picture_ptr:该值为0表明没有图像可以解码,否则表明有图像可以解码; ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture, pkt); // - 解码,将那个H264的文件解码了 if (ret < 0) { delete pkt; return 0; } //DTS(decoding time stamp) 和PTS(Presentation Time stamp) 前者是解码的时间,后者是显示的时间。 两种时间戳 pFrame->pts = pFrame->pkt_pts;//ts++; // 若为真,则证明有图像可以解码。 if (got_picture) { AVPacket *tmppkt = new AVPacket; av_init_packet(tmppkt); int size = video_enc_ctx->width*video_enc_ctx->height * 3 / 2; char* buf = new char[size]; memset(buf, 0, size); tmppkt->data = (uint8_t*)buf; // 数据域指针-搞它这个存放帧数据的地方的管理方式。 tmppkt->size = size; // 编码一帧视频数据 // video_enc_ctx:编码器上下文 // tmppkt: output AVPacket. (输出pkt) // pFrame: AVFrame containing the raw video data to be encoded. // got_picture: 如果输出packt不为空 它被设为1,否则为0 ,若函数返回值为err,这个指针就不要用了。 ret = av 4000 codec_encode_video2(video_enc_ctx, tmppkt, pFrame, &got_picture); if (ret < 0) { avio_close(pOFormat->pb); // 关了这个流吧 delete buf; return 0; } // 如果tmppkt不为空 if (got_picture) { //ret = av_interleaved_write_frame(pOFormat, tmppkt); AVStream *in_stream = pFormat->streams[pkt->stream_index]; // 输入流 AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; // 输出流 // 对新的pkt的时间戳们进行 // 它的作用是计算 "a * b / c" 的值并分五种方式来取整. - "时钟基c" 表示的 数值a 转换成以 "时钟基b" 来表示。 tmppkt->pts = av_rescale_q_rnd(tmppkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); tmppkt->dts = av_rescale_q_rnd(tmppkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); tmppkt->duration = av_rescale_q(tmppkt->duration, in_stream->time_base, out_stream->time_base); tmppkt->pos = -1; // Write a packet to an output media file ensuring correct interleaving. ret = av_interleaved_write_frame(pMp4Format, tmppkt); if (ret < 0) return 0; delete tmppkt; // 删除临时的pkt delete buf; // 删除buf } } //avcodec_free_frame(&pFrame); } else if (pkt->stream_index == 1) // 音频的,我不太理解 { AVStream *in_stream = pFormat->streams[pkt->stream_index]; AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base); pkt->pos = -1; if (av_interleaved_write_frame(pMp4Format, pkt) < 0) return 0; } } avcodec_free_frame(&pFrame); return 0; }
相关文章推荐
- ffmpeg开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)
- ffmpeg封装h264裸流为mp4文件,视频播放速度特别慢-解决
- 使用FFMPEG从MP4封装中提取视频流到H264文件
- 使用FFMPEG从MP4封装中提取视频流到.264文件 (转载)
- 使用FFMPEG从MP4封装中提取视频流到.264文件
- 使用FFMPEG从MP4封装中提取视频流到H264文件
- ffmpeg开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)
- 运用ffmpeg生成MP4文件
- ffmpeg 将1张图片和1个MP3音频文件转为MP4
- 使用ffmpeg拆分大的mp4文件为小段的mp4文件
- 使用ffmpeg把flv转换成Android可播放的mp4文件
- 使用ffmpeg转码的MP4文件需要加载完了才能播放的解决办法
- FFMpeg写MP4文件例子分析
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- FFMpeg写MP4文件例子分析
- ffmpeg代码笔记5:MP4文件读取packet,计算DTS
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- FFMpeg写MP4文件例子分析
- 运用ffmpeg生成MP4文件
- FFMpeg写MP4文件例子分析 .