ffmpeg-從mp4、flv、ts文件中提取264視頻流數據
main.c
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>void proc(int need_to_annexb, char* in_file, char* out_file)
{AVFormatContext* ifmat_ctx = NULL;int videoindex = -1;AVPacket* pkt = NULL;int ret = -1;int file_end = 0;//char* in_file = "believe.mp4";//char* out_file = "out_mp4_no_annexb.h264";FILE* out_fd = fopen(out_file, "wb");printf("in_file = %s , out_file = %s\n", in_file, out_file);//創建解復用器,最后使用avformat_close_input()釋放相關內存ifmat_ctx = avformat_alloc_context();if(!ifmat_ctx){printf("avformat_alloc_context faild!\n");return -1;}//根據url打開碼流,會選擇匹配的解復用器的ret = avformat_open_input(&ifmat_ctx, in_file, NULL, NULL);if(ret != 0){printf("avformat_open_input failed!\n");return -1;}//讀取媒體文件的部分數據包可以獲取碼流信息ret = avformat_find_stream_info(ifmat_ctx, NULL);if(ret < 0){printf("avformat_find_stream_info faile!\n");avformat_close_input(&ifmat_ctx);return -1;}//查找出哪個碼流是音頻還是視頻還是字幕videoindex = av_find_best_stream(ifmat_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if(videoindex == -1){printf("av_find_best_stream failed!\n");avformat_close_input(&ifmat_ctx);return -1;}//分配packetpkt = av_packet_alloc();av_init_packet(pkt);file_end = 0;while (0 == file_end){if((ret = av_read_frame(ifmat_ctx, pkt)) < 0){file_end = 1;printf("av_read_frame end!\n");}//讀出的幀判斷是否是視頻幀if(ret == 0 && pkt->stream_index == videoindex){//是否需要使用h264_mp4toannexb轉換if(need_to_annexb){//獲取比特流過濾器(h264_mp4toannexb)const AVBitStreamFilter* bsfilter = av_bsf_get_by_name("h264_mp4toannexb");AVBSFContext *bsf_ctx = NULL;//申請過濾器上下文av_bsf_alloc(bsfilter, &bsf_ctx);//從視頻流中拷貝編解決碼器參數avcodec_parameters_copy(bsf_ctx->par_in, ifmat_ctx->streams[videoindex]->codecpar);//初始化過濾器上下文av_bsf_init(bsf_ctx);int input_size = pkt->size;//記錄下是否send 一個packet,receive 一個packet。基本都是這個情況的//有比較少情況出現會send 一個packet,receive 幾個packet//(SPS、PPS、I幀在一個packet send,receive 多個packet)。int out_pkt_count = 0;if(av_bsf_send_packet(bsf_ctx, pkt) != 0){//不管是否成功,都要釋放packet,因為bitstreamfilter內部還有引用這個內存空間的av_packet_unref(pkt);continue;}//不管是否成功,都要釋放packet,因為bitstreamfilter內部還有引用這個內存空間的av_packet_unref(pkt);while (av_bsf_receive_packet(bsf_ctx , pkt) == 0){out_pkt_count++;size_t size = fwrite(pkt->data, 1, pkt->size, out_fd);if(size != pkt->size){printf("fwrite failed!\n");}av_packet_unref(pkt);}//send 一個packet ,receive pakcet 超過2個就輸出提示信息if(out_pkt_count >= 2){printf("one send packet size = %d, receive %d packet.\n", input_size,out_pkt_count);}if(bsf_ctx)av_bsf_free(&bsf_ctx);}else{size_t size = fwrite(pkt->data, 1, pkt->size, out_fd);if(size != pkt->size){printf("fwrite failed!\n");}av_packet_unref(pkt);}}else{if(ret == 0){av_packet_unref(pkt);}}}if(out_fd)fclose(out_fd);if(pkt)av_packet_free(&pkt);if(ifmat_ctx)avformat_close_input(&ifmat_ctx);}int main()
{proc(1, "believe.flv", "out_flv_need_toannexb.h264");//使用ffplay可以播放proc(0, "believe.flv", "out_flv_no_toannexb.h264");//使用ffplay不可以proc(1, "believe.mp4", "out_mp4_need_toannexb.h264");//使用ffplay可以播放proc(0, "believe.mp4", "out_mp4_no_toannexb.h264");//使用ffplay不可以proc(1, "believe.ts", "out_ts_need_toannexb.h264");//使用ffplay可以播放proc(0, "believe.ts", "out_ts_no_toannexb.h264");//使用ffplay可以播放//注意://flv/mp4/mkv一些結構中,h264需要h264_mp4toannexb處理,添加startcode/SPS/PPS等信息//ts不用h264_mp4toannexb處理。return 0;
}
flv兩個文件的對比:
mp4兩個文件的對比:
ts兩個文件的對比: