#include <stdio.h>
#include <libavformat/avformat.h>int main(int argc, char **argv)
{//打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
// avformat_network_init();const char *default_filename = "believe.mp4";char *in_filename = NULL;if(argv[1] == NULL){in_filename = default_filename;}else{in_filename = argv[1];}printf("in_filename = %sn", in_filename);//AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体AVFormatContext *ifmt_ctx = NULL; // 输入文件的demuxint videoindex = -1; // 视频索引int audioindex = -1; // 音频索引// 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);if (ret < 0) //如果打开媒体文件失败,打印失败原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("open %s failed:%sn", in_filename, buf);goto failed;}ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0) //如果打开媒体文件失败,打印失败原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("avformat_find_stream_info %s failed:%sn", in_filename, buf);goto failed;}//打开媒体文件成功printf_s("n==== av_dump_format in_filename:%s ===n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("n==== av_dump_format finish =======nn");// url: 调用avformat_open_input读取到的媒体文件的路径/名字printf("media name:%sn", ifmt_ctx->url);// nb_streams: nb_streams媒体流数量printf("stream number:%dn", ifmt_ctx->nb_streams);// bit_rate: 媒体文件的码率,单位为bpsprintf("media average ratio:%lldkbpsn",(int64_t)(ifmt_ctx->bit_rate/1024));// 时间int total_seconds, hour, minute, second;// duration: 媒体文件时长,单位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);//通过上述运算,可以得到媒体文件的总时长printf("total duration: %02d:%02d:%02dn", hour, minute, second);printf("n");/** 老版本通过遍历的方式读取媒体文件视频和音频的信息* 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果*/for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++){AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流//如果是音频流,则打印音频的信息if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type){printf("----- Audio info:n");// index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识printf("index:%dn", in_stream->index);// sample_rate: 音频编解码器的采样率,单位为Hzprintf("samplerate:%dHzn", in_stream->codecpar->sample_rate);// codecpar->format: 音频采样格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_FLTPn");}else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_S16Pn");}// channels: 音频信道数目printf("channel number:%dn", in_stream->codecpar->channels);// codec_id: 音频压缩编码格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id){printf("audio codec:AACn");}else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id){printf("audio codec:MP3n");}else{printf("audio codec_id:%dn", in_stream->codecpar->codec_id);}// 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);//将音频总时长转换为时分秒的格式打印到控制台上printf("audio duration: %02d:%02d:%02dn",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));}else{printf("audio duration unknown");}printf("n");audioindex = i; // 获取音频的索引}else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //如果是视频流,则打印视频的信息{printf("----- Video info:n");printf("index:%dn", in_stream->index);// avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧printf("fps:%lffpsn", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:MPEG4n");}else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:H264n");}else{printf("video codec_id:%dn", in_stream->codecpar->codec_id);}// 视频帧宽度和帧高度printf("width:%d height:%dn", in_stream->codecpar->width,in_stream->codecpar->height);//视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02dn",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上}else{printf("video duration unknown");}printf("n");videoindex = i;}}AVPacket *pkt = av_packet_alloc();int pkt_count = 0;int print_max_count = 10;printf("n-----av_read_frame startn");while (1){ret = av_read_frame(ifmt_ctx, pkt);if (ret < 0){printf("av_read_frame endn");break;}if(pkt_count++ < print_max_count){if (pkt->stream_index == audioindex){printf("audio pts: %lldn", pkt->pts);printf("audio dts: %lldn", pkt->dts);printf("audio size: %dn", pkt->size);printf("audio pos: %lldn", pkt->pos);printf("audio duration: %lfnn",pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));}else if (pkt->stream_index == videoindex){printf("video pts: %lldn", pkt->pts);printf("video dts: %lldn", pkt->dts);printf("video size: %dn", pkt->size);printf("video pos: %lldn", pkt->pos);printf("video duration: %lfnn",pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));}else{printf("unknown stream_index:n", pkt->stream_index);}}av_packet_unref(pkt);}if(pkt)av_packet_free(&pkt);
failed:if(ifmt_ctx)avformat_close_input(&ifmt_ctx);getchar(); //加上这一句,防止程序打印完信息马上退出return 0;
}
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>#define ADTS_HEADER_LEN 7;const int sampling_frequencies[] = {96000, // 0x088200, // 0x164000, // 0x248000, // 0x344100, // 0x432000, // 0x524000, // 0x622050, // 0x716000, // 0x812000, // 0x911025, // 0xa8000 // 0xb// 0xc d e f是保留的
};int adts_header(char * const p_adts_header, const int data_length,const int profile, const int samplerate,const int channels)
{int sampling_frequency_index = 3; // 默认使用48000hzint adtsLen = data_length + 7;int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);int i = 0;for(i = 0; i < frequencies_size; i++){if(sampling_frequencies[i] == samplerate){sampling_frequency_index = i;break;}}if(i >= frequencies_size){printf("unsupport samplerate:%dn", samplerate);return -1;}p_adts_header[0] = 0xff; //syncword:0xfff 高8bitsp_adts_header[1] = 0xf0; //syncword:0xfff 低4bitsp_adts_header[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bitp_adts_header[1] |= (0 << 1); //Layer:0 2bitsp_adts_header[1] |= 1; //protection absent:1 1bitp_adts_header[2] = (profile)<<6; //profile:profile 2bitsp_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index 4bitsp_adts_header[2] |= (0 << 1); //private bit:0 1bitp_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels 高1bitp_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bitsp_adts_header[3] |= (0 << 5); //original:0 1bitp_adts_header[3] |= (0 << 4); //home:0 1bitp_adts_header[3] |= (0 << 3); //copyright id bit:0 1bitp_adts_header[3] |= (0 << 2); //copyright id start:0 1bitp_adts_header[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bitsp_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bitsp_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value 低3bitsp_adts_header[5] |= 0x1f; //buffer fullness:0x7ff 高5bitsp_adts_header[6] = 0xfc; //11111100 //buffer fullness:0x7ff 低6bits// number_of_raw_data_blocks_in_frame:// 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。return 0;
}int main(int argc, char *argv[])
{int ret = -1;char errors[1024];char *in_filename = NULL;char *aac_filename = NULL;FILE *aac_fd = NULL;int audio_index = -1;int len = 0;AVFormatContext *ifmt_ctx = NULL;AVPacket pkt;// 设置打印级别av_log_set_level(AV_LOG_DEBUG);if(argc < 3){av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!n");return -1;}in_filename = argv[1]; // 输入文件aac_filename = argv[2]; // 输出文件if(in_filename == NULL || aac_filename == NULL){av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!n");return -1;}aac_fd = fopen(aac_filename, "wb");if (!aac_fd){av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %sn", aac_filename);return -1;}// 打开输入文件if((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)n",in_filename,ret,errors);return -1;}// 获取解码器信息if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)n",in_filename,ret,errors);return -1;}// dump媒体信息av_dump_format(ifmt_ctx, 0, in_filename, 0);// 初始化packetav_init_packet(&pkt);// 查找audio对应的steam indexaudio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);if(audio_index < 0){av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %sn",av_get_media_type_string(AVMEDIA_TYPE_AUDIO),in_filename);return AVERROR(EINVAL);}// 打印AAC级别printf("audio profile:%d, FF_PROFILE_AAC_LOW:%dn",ifmt_ctx->streams[audio_index]->codecpar->profile,FF_PROFILE_AAC_LOW);if(ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC){printf("the media file no contain AAC stream, it's codec_id is %dn",ifmt_ctx->streams[audio_index]->codecpar->codec_id);goto failed;}// 读取媒体文件,并把aac数据帧写入到本地文件while(av_read_frame(ifmt_ctx, &pkt) >=0 ){if(pkt.stream_index == audio_index){char adts_header_buf[7] = {0};adts_header(adts_header_buf, pkt.size,ifmt_ctx->streams[audio_index]->codecpar->profile,ifmt_ctx->streams[audio_index]->codecpar->sample_rate,ifmt_ctx->streams[audio_index]->codecpar->channels);fwrite(adts_header_buf, 1, 7, aac_fd); // 写adts header , ts流不适用,ts流分离出来的packet带了adts headerlen = fwrite( pkt.data, 1, pkt.size, aac_fd); // 写adts dataif(len != pkt.size){av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)n",len,pkt.size);}}av_packet_unref(&pkt);}failed:// 关闭输入文件if(ifmt_ctx){avformat_close_input(&ifmt_ctx);}if(aac_fd){fclose(aac_fd);}return 0;
}
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{av_strerror(errnum, err_buf, 128);return err_buf;
}/*
AvCodecContext->extradata[]中为nalu长度
* codec_extradata:
* 1, 64, 0, 1f, ff, e1, [0, 18], 67, 64, 0, 1f, ac, c8, 60, 78, 1b, 7e,
* 78, 40, 0, 0, fa, 40, 0, 3a, 98, 3, c6, c, 66, 80,
* 1, [0, 5],68, e9, 78, bc, b0, 0,
*///ffmpeg -i 2018.mp4 -codec copy -bsf:h264_mp4toannexb -f h264 tmp.h264
//ffmpeg 从mp4上提取H264的nalu h
int main(int argc, char **argv)
{AVFormatContext *ifmt_ctx = NULL;int videoindex = -1;AVPacket *pkt = NULL;int ret = -1;int file_end = 0; // 文件是否读取结束if(argc < 3){printf("usage inputfile outfilen");return -1;}FILE *outfp=fopen(argv[2],"wb");printf("in:%s out:%sn", argv[1], argv[2]);// 分配解复用器的内存,使用avformat_close_input释放ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx){printf("[error] Could not allocate context.n");return -1;}// 根据url打开码流,并选择匹配的解复用器ret = avformat_open_input(&ifmt_ctx,argv[1], NULL, NULL);if(ret != 0){printf("[error]avformat_open_input: %sn", av_get_err(ret));return -1;}// 读取媒体文件的部分数据包以获取码流信息ret = avformat_find_stream_info(ifmt_ctx, NULL);if(ret < 0){printf("[error]avformat_find_stream_info: %sn", av_get_err(ret));avformat_close_input(&ifmt_ctx);return -1;}// 查找出哪个码流是video/audio/subtitlesvideoindex = -1;// 推荐的方式videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if(videoindex == -1){printf("Didn't find a video stream.n");avformat_close_input(&ifmt_ctx);return -1;}// 分配数据包pkt = av_packet_alloc();av_init_packet(pkt);// 1 获取相应的比特流过滤器//FLV/MP4/MKV等结构中,h264需要h264_mp4toannexb处理。添加SPS/PPS等信息。// FLV封装时,可以把多个NALU放在一个VIDEO TAG中,结构为4B NALU长度+NALU1+4B NALU长度+NALU2+...,// 需要做的处理把4B长度换成00000001或者000001const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");AVBSFContext *bsf_ctx = NULL;// 2 初始化过滤器上下文av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;// 3 添加解码器属性avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);av_bsf_init(bsf_ctx);file_end = 0;while (0 == file_end){if((ret = av_read_frame(ifmt_ctx, pkt)) < 0){// 没有更多包可读file_end = 1;printf("read file end: ret:%dn", ret);}if(ret == 0 && pkt->stream_index == videoindex){
#if 0int input_size = pkt->size;int out_pkt_count = 0;if (av_bsf_send_packet(bsf_ctx, pkt) != 0) // bitstreamfilter内部去维护内存空间{av_packet_unref(pkt); // 你不用了就把资源释放掉continue; // 继续送}av_packet_unref(pkt); // 释放资源while(av_bsf_receive_packet(bsf_ctx, pkt) == 0){out_pkt_count++;// printf("fwrite size:%dn", pkt->size);size_t size = fwrite(pkt->data, 1, pkt->size, outfp);if(size != pkt->size){printf("fwrite failed-> write:%u, pkt_size:%un", size, pkt->size);}av_packet_unref(pkt);}if(out_pkt_count >= 2){printf("cur pkt(size:%d) only get 1 out pkt, it get %d pktsn",input_size, out_pkt_count);}
#else // TS流可以直接写入size_t size = fwrite(pkt->data, 1, pkt->size, outfp);if(size != pkt->size){printf("fwrite failed-> write:%u, pkt_size:%un", size, pkt->size);}av_packet_unref(pkt);
#endif}else{if(ret == 0)av_packet_unref(pkt); // 释放内存}}if(outfp)fclose(outfp);if(bsf_ctx)av_bsf_free(&bsf_ctx);if(pkt)av_packet_free(&pkt);if(ifmt_ctx)avformat_close_input(&ifmt_ctx);printf("finishn");return 0;
}
#include "FlvMetaData.h"FlvMetaData::FlvMetaData(uint8_t *meta, unsigned int length) {m_meta = meta;m_length = length;m_duration = 0;m_width = 0;m_height = 0;m_framerate = 0;m_videodatarate = 0;m_audiodatarate = 0;m_videocodecid = 0;m_audiosamplerate = 0;m_audiosamplesize = 0;m_stereo = false;parseMeta();
}FlvMetaData::FlvMetaData(const FlvMetaData& r) {m_length = r.m_length;m_meta = new uint8_t[m_length];memcpy(m_meta, r.m_meta, m_length);m_duration = r.m_duration;m_width = r.m_width;m_height = r.m_height;m_framerate = r.m_framerate;m_videodatarate = r.m_videodatarate;m_audiodatarate = r.m_audiodatarate;m_videocodecid = r.m_videocodecid;m_audiosamplerate = r.m_audiosamplerate;m_audiosamplesize = r.m_audiosamplesize;m_stereo = r.m_stereo;
}FlvMetaData& FlvMetaData::operator=(const FlvMetaData& r) {if(this == &r) {return *this;}if(m_meta != NULL) {delete m_meta;}m_length = r.m_length;m_meta = new uint8_t[m_length];memcpy(m_meta, r.m_meta, m_length);m_duration = r.m_duration;m_width = r.m_width;m_height = r.m_height;m_framerate = r.m_framerate;m_videodatarate = r.m_videodatarate;m_audiodatarate = r.m_audiodatarate;m_videocodecid = r.m_videocodecid;m_audiosamplerate = r.m_audiosamplerate;m_audiosamplesize = r.m_audiosamplesize;m_stereo = r.m_stereo;return *this;
}FlvMetaData::~FlvMetaData() {if(m_meta != NULL) {delete m_meta;m_meta = NULL;}
}void FlvMetaData::parseMeta() {unsigned int arrayLen = 0;unsigned int offset = TAG_HEAD_LEN + 13;unsigned int nameLen = 0;double numValue = 0;bool boolValue = false;if(m_meta[offset++] == 0x08) {arrayLen |= m_meta[offset++];arrayLen = arrayLen << 8;arrayLen |= m_meta[offset++];arrayLen = arrayLen << 8;arrayLen |= m_meta[offset++];arrayLen = arrayLen << 8;arrayLen |= m_meta[offset++];//cerr << "ArrayLen = " << arrayLen << endl;} else {//TODO:cerr << "metadata format error!!!" << endl;return ;}for(unsigned int i = 0; i < arrayLen; i++) {numValue = 0;boolValue = false;nameLen = 0;nameLen |= m_meta[offset++];nameLen = nameLen << 8;nameLen |= m_meta[offset++];//cerr << "name length=" << nameLen << " ";char name[nameLen + 1];
#if DEBUGprintf("norign n");for(unsigned int i = 0; i < nameLen + 3; i++) {printf("%x ", m_meta[offset + i]);}printf("n");
#endifmemset(name, 0, sizeof(name));memcpy(name, &m_meta[offset], nameLen);name[nameLen + 1] = '