一、mp4的几种封装方案
1、ffmpeg
2、mp4v2
3、gpac
https://blog.csdn.net/LLL347/article/details/85886975
https://blog.csdn.net/weixin_43549602/article/details/84571906
二、mp4v2
通过EasyAACEncoder + mp4v2, 根据两位老哥的代码,整合了个demo.
main.cpp
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include "EasyAACEncoderAPI.h" #include "mp4v2.h" #include "util.h" /*test file*/ static off_t file_size; static char* get_h264_frame(char* raw_file) { int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { printf("open h264 raw file %s failed. ", raw_file); return nullptr; } file_size = lseek(raw_fd, 0, SEEK_END); if (file_size <= 0) { printf("h264 raw file %s empty. ", raw_file); return nullptr; } char*h264_raw = (char*)malloc(file_size); if (!h264_raw) { printf("alloc raw buffer failed for file %s. ", raw_file); return nullptr; } lseek(raw_fd, 0, SEEK_SET); ssize_t nb_read = 0; if ((nb_read = read(raw_fd, h264_raw, file_size)) != file_size) { printf("buffer %s failed, expect=%dKB, actual=%dKB. ", raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); return nullptr; } close(raw_fd); return h264_raw; } /*example*/ int main(int argc, char** argv) { int fps = 25; /*g711 data source*/ int n = 0; char g711a[320]; FILE *infile=fopen("./doc/stream-file/8k_1_16.g711a","rb"); /*g711_to_aac*/ InitParam initParam; initParam.u32AudioSamplerate=8000; initParam.ucAudioChannel=1; initParam.u32PCMBitSize=16; initParam.ucAudioCodec = Law_ALaw; Easy_Handle handle = Easy_AACEncoder_Init(initParam); int bAACBufferSize = 4*1024; unsigned char *pbAACBuffer = (unsigned char*)malloc(bAACBufferSize * sizeof(unsigned char)); unsigned int aac_len = 0; /*Create */ //MP4FileHandle mp4 = MP4CreateEx("test.mp4", MP4_DETAILS_ALL, 0, 1, 1, 0, 0, 0, 0); MP4FileHandle mp4 = MP4Create("test.mp4", 0); MP4SetTimeScale(mp4, 90000); /*Set video*/ MP4TrackId video = MP4AddH264VideoTrack(mp4, 90000, 90000 / fps, 640, 480, 0x64, //sps[1] AVCProfileIndication 0x00, //sps[2] profile_compat 0x1f, //sps[3] AVCLevelIndication 3); MP4SetVideoProfileLevel(mp4, 0x7F); /*Set audio*/ MP4TrackId audio = MP4AddAudioTrack(mp4, 8000, 1024, MP4_MPEG4_AUDIO_TYPE); MP4SetAudioProfileLevel(mp4, 0x2); while(1){ if(fread(g711a,1,320,infile) > 0) { if(Easy_AACEncoder_Encode(handle, (unsigned char*)g711a, 320, pbAACBuffer, &aac_len) > 0) { dump_bin("ttt.aac",(char*)pbAACBuffer, aac_len); MP4WriteSample(mp4, audio, pbAACBuffer, aac_len, MP4_INVALID_DURATION, 0, 1); } }else{ fseek(infile, 0, SEEK_SET); continue; } char str[32]; sprintf(str,"./doc/stream-file/h264_3/%d",n); char* h264_raw = get_h264_frame(str); if(h264_raw != nullptr){ MP4WriteSample(mp4,video , (uint8_t*)h264_raw, file_size, MP4_INVALID_DURATION, 0, 1); } free(h264_raw); if(++n > 311) break; } Easy_AACEncoder_Release(handle); free(pbAACBuffer); MP4Close(mp4); return 0; }
Makefile
APP = main INCLUDE = -I ./lib/EasyAACEncoder/include -I ./lib/mp4v2/include -I ./lib/mp4v2/include/mp4v2 -I ./lib/util LIB = ./lib/EasyAACEncoder/lib/libEasyAACEncoder.a ./lib/mp4v2/lib/libmp4v2.a SRC = main.cpp ./lib/util/util.c CFLAGS = -g -O0 -std=c++11 -lstdc++ LDFLAGS = -lpthread -lz out: g++ $(SRC) -o $(APP) $(LIB) $(INCLUDE) $(CFLAGS) $(LDFLAGS) clean: rm -rf *o *.out $(APP)
封装一下
while( ((i=fread(buffer,1,160,infile))>0) && (runcond) )
{
rtp_session_send_with_ts(session,buffer,i,user_ts);
user_ts+=160;
}
while(!feof(ts_file)){
int read_len = fread(buf+count, 1, TS_PACKET_SIZE, ts_file);
count += read_len;
}
参考
1. 先是随手一搜,看起来应该很简单
https://blog.csdn.net/lipku/article/details/78138645
https://github.com/EasyDarwin/EasyAACEncoder
2. 以上很快搞定了,发现vlc , ffmpeg播放正常,Windows Media Player播不了,然后找到据说需要在视频nalu头部添加长度信息
https://blog.csdn.net/firehood_/article/details/8813587#
https://blog.csdn.net/machh/article/details/40623387
3. 视频nalu头部添加长度信息也不管用,aac单独导出来也能播,写到mp4就不行,已经怀疑mp4v2到底是不是靠谱!
https://www.zhihu.com/question/25396952
4. 2015年就有人遇到同样的问题,留到今天还没解决,咱用ffmpeg也行, 只是mp4v2看起来小巧,为了更轻量折腾了2天半.
三、ffmpeg
ffmpeg 音视频同步需要注意,调了一圈,发现一样Windows Media Player播不了
ffmpeg muxer (h264 +g711a)
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include <unistd.h> #include <fcntl.h> #include <libavutil/opt.h> #include <libavformat/avformat.h> #define STREAM_FRAME_RATE 25 /* 25 images/s */ /*test file*/ static off_t file_size; static char* get_file_frame(char* raw_file) { int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { printf("open h264 raw file %s failed. ", raw_file); return NULL; } file_size = lseek(raw_fd, 0, SEEK_END); if (file_size <= 0) { printf("h264 raw file %s empty. ", raw_file); return NULL; } char* raw = (char*)malloc(file_size); if (!raw) { printf("alloc raw buffer failed for file %s. ", raw_file); return NULL; } lseek(raw_fd, 0, SEEK_SET); ssize_t nb_read = 0; if ((nb_read = read(raw_fd, raw, file_size)) != file_size) { printf("buffer %s failed, expect=%dKB, actual=%dKB. ", raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); return NULL; } close(raw_fd); return raw; } /* Add an output stream. */ static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id) { AVCodecContext *c; AVStream *st; /* find the encoder */ *codec = avcodec_find_encoder(codec_id); if (!(*codec)) { fprintf(stderr, "Could not find encoder for '%s' ", avcodec_get_name(codec_id)); exit(1); } st = avformat_new_stream(oc, *codec); if (!st) { fprintf(stderr, "Could not allocate stream "); exit(1); } st->id = oc->nb_streams-1; c = st->codec; switch ((*codec)->type) { case AVMEDIA_TYPE_AUDIO: c->sample_fmt = AV_SAMPLE_FMT_FLTP; c->bit_rate = 64000; c->sample_rate = 44100; c->channels = 2; break; case AVMEDIA_TYPE_VIDEO: c->codec_id = codec_id; c->bit_rate = 400000; /* Resolution must be a multiple of two. */ c->width = 1920; c->height = 1080; /* timebase: This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be * identical to 1. */ c->time_base.den = STREAM_FRAME_RATE; c->time_base.num = 1; c->gop_size = STREAM_FRAME_RATE; /* emit one intra frame every twelve frames at most */ c->pix_fmt = AV_PIX_FMT_YUV420P; if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { /* just for testing, we also add B frames */ c->max_b_frames = 2; } if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { /* Needed to avoid using macroblocks in which some coeffs overflow. * This does not happen with normal video, it just happens here as * the motion of the chroma plane does not match the luma plane. */ c->mb_decision = 2; } break; default: break; } /* Some formats want stream headers to be separate. */ if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; return st; } /**************************************************************/ /* media file output */ int main(int argc, char **argv) { AVOutputFormat *fmt; AVFormatContext *oc; AVStream *audio_st,*video_st; AVCodec *audio_codec,*video_codec; int ret; char filename[32]; sprintf(filename,"rm.mp4"); /* Initialize libavcodec, and register all codecs and formats. */ av_register_all(); /* allocate the output media context */ avformat_alloc_output_context2(&oc, NULL, NULL, filename); if (!oc) { printf(" Could not deduce output format from file extension. "); return 0; } if (!oc) { return 1; } fmt = oc->oformat; /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ video_st = NULL; audio_st = NULL; if (fmt->video_codec != AV_CODEC_ID_NONE) { fmt->video_codec = AV_CODEC_ID_H264; video_st = add_stream(oc, &video_codec, fmt->video_codec); } if (fmt->audio_codec != AV_CODEC_ID_NONE) { fmt->audio_codec = AV_CODEC_ID_AAC; audio_st = add_stream(oc, &audio_codec, fmt->audio_codec); } /* open the output file, if needed */ if (!(fmt->flags & AVFMT_NOFILE)) { ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, "Could not open '%s': %s ", filename, av_err2str(ret)); return 0; } } /* Write the stream header, if any. */ ret = avformat_write_header(oc, NULL); if (ret < 0) { fprintf(stderr, "Error occurred when opening output file: %s ", av_err2str(ret)); return 0; } video_st->time_base.den = 90000; video_st->time_base.num = 1; long pts_vtime = 0; long pts_atime = 0; printf("ffmpeg mux start : "); /*g711 data source*/ int m = 0; int n = 0; FILE *infile=fopen("./doc/stream-file/8k_1_16.g711a","rb"); int video_tag; while(1){ AVPacket pkt = { 0 }; av_init_packet(&pkt); pkt.size = 0; if(video_tag == 1) { pts_vtime += 90000/25; pkt.dts = pts_vtime; pkt.pts = pts_vtime; pkt.stream_index = video_st->index; /*h264 mux*/ char str[32]; sprintf(str,"./avc_raw/avc_raw_%03d.h264",m); char* h264_raw = get_file_frame(str); if(h264_raw != NULL){ pkt.size = file_size; pkt.data = (unsigned char*)h264_raw; ret = av_interleaved_write_frame(oc, &pkt); } free(h264_raw); if(++m > 248) m=0; video_tag = 0; }else{ pts_atime += 90000/25/2; pkt.dts = pts_atime; pkt.pts = pts_atime; pkt.stream_index = audio_st->index; /*aac mux*/ char str[32]; sprintf(str,"./aac_raw/aac_raw_%03d.aac",n); char* aac_raw = get_file_frame(str); if(aac_raw != NULL){ pkt.size = file_size; pkt.data = (unsigned char*)aac_raw; ret = av_interleaved_write_frame(oc, &pkt); } free(aac_raw); if(++n > 431) break; video_tag = 1; } } av_write_trailer(oc); avio_close(oc->pb); avformat_free_context(oc); printf(" ---FILE:%s-line %d --- ",__FILE__,__LINE__); printf("ffmpeg save file done !!! "); return 0; }
ffmpeg muxer (h264 + aac/g711a)
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include <unistd.h> #include <fcntl.h> #include "EasyAACEncoderAPI.h" #include <libavutil/opt.h> #include <libavformat/avformat.h> #define STREAM_FRAME_RATE 25 /* 25 images/s */ /*test file*/ static off_t file_size; static char* get_file_frame(char* raw_file) { int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { printf("open h264 raw file %s failed. ", raw_file); return NULL; } file_size = lseek(raw_fd, 0, SEEK_END); if (file_size <= 0) { printf("h264 raw file %s empty. ", raw_file); return NULL; } char* raw = (char*)malloc(file_size); if (!raw) { printf("alloc raw buffer failed for file %s. ", raw_file); return NULL; } lseek(raw_fd, 0, SEEK_SET); ssize_t nb_read = 0; if ((nb_read = read(raw_fd, raw, file_size)) != file_size) { printf("buffer %s failed, expect=%dKB, actual=%dKB. ", raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); return NULL; } close(raw_fd); return raw; } int dump(const char * file, char *buf, int len) { FILE *fp = fopen(file, "a+b"); if(fp) { fwrite(buf, len, 1, fp); fclose(fp); fp = NULL; return -1; } return 0; } void make_dsi( unsigned int sampling_frequency_index, unsigned int channel_configuration, unsigned char* dsi ) { unsigned int object_type = 2; // AAC LC by default dsi[0] = (object_type<<3) | (sampling_frequency_index>>1); dsi[1] = ((sampling_frequency_index&1)<<7) | (channel_configuration<<3); } int get_sr_index(unsigned int sampling_frequency) { switch (sampling_frequency) { case 96000: return 0; case 88200: return 1; case 64000: return 2; case 48000: return 3; case 44100: return 4; case 32000: return 5; case 24000: return 6; case 22050: return 7; case 16000: return 8; case 12000: return 9; case 11025: return 10; case 8000: return 11; case 7350: return 12; default: return 0; } } /** * Add ADTS header at the beginning of each and every AAC packet. * This is needed as MediaCodec encoder generates a packet of raw * AAC data. * * Note the packetLen must count in the ADTS header itself !!! . *注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header **/ void addADTStoPacket(char* packet, int packetLen) { int profile = 2; //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC; int freqIdx = 4; //44.1K, 见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码 int chanCfg = 2; //见后面注释channel_configuration,Stero双声道立体声 /*int avpriv_mpeg4audio_sample_rates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; channel_configuration: 表示声道数chanCfg 0: Defined in AOT Specifc Config 1: 1 channel: front-center 2: 2 channels: front-left, front-right 3: 3 channels: front-center, front-left, front-right 4: 4 channels: front-center, front-left, front-right, back-center 5: 5 channels: front-center, front-left, front-right, back-left, back-right 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel 8-15: Reserved */ // fill in ADTS data packet[0] = (char)0xFF; packet[1] = (char)0xF9; packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2)); packet[3] = (char)(((chanCfg&3)<<6) + (packetLen>>11)); packet[4] = (char)((packetLen&0x7FF) >> 3); packet[5] = (char)(((packetLen&7)<<5) + 0x1F); packet[6] = (char)0xFC; } /* Add an output stream. */ static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id) { AVCodecContext *c; AVStream *st; /* find the encoder */ *codec = avcodec_find_encoder(codec_id); if (!(*codec)) { fprintf(stderr, "Could not find encoder for '%s' ", avcodec_get_name(codec_id)); exit(1); } st = avformat_new_stream(oc, *codec); if (!st) { fprintf(stderr, "Could not allocate stream "); exit(1); } st->id = oc->nb_streams-1; c = st->codec; switch ((*codec)->type) { case AVMEDIA_TYPE_AUDIO: c->sample_fmt = AV_SAMPLE_FMT_FLTP; c->bit_rate = 64000; c->sample_rate = 44100; c->channels = 2; char dsi[2]; make_dsi( get_sr_index(44100), 2, dsi ); c->extradata = (uint8_t*)av_malloc(sizeof(uint8_t)*2); memcpy(c->extradata, &dsi[0], 2); c->extradata_size = 2; break; case AVMEDIA_TYPE_VIDEO: c->codec_id = codec_id; c->bit_rate = 400000; /* Resolution must be a multiple of two. */ c->width = 1920; c->height = 1080; /* timebase: This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be * identical to 1. */ c->time_base.den = STREAM_FRAME_RATE; c->time_base.num = 1; c->gop_size = STREAM_FRAME_RATE; /* emit one intra frame every twelve frames at most */ c->pix_fmt = AV_PIX_FMT_YUV420P; if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { /* just for testing, we also add B frames */ c->max_b_frames = 2; } if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { /* Needed to avoid using macroblocks in which some coeffs overflow. * This does not happen with normal video, it just happens here as * the motion of the chroma plane does not match the luma plane. */ c->mb_decision = 2; } break; default: break; } /* Some formats want stream headers to be separate. */ if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; return st; } /**************************************************************/ /* media file output */ int main(int argc, char **argv) { AVOutputFormat *fmt; AVFormatContext *oc; AVStream *audio_st,*video_st; AVCodec *audio_codec,*video_codec; int ret; char filename[32]; sprintf(filename,"rm.mp4"); /* Initialize libavcodec, and register all codecs and formats. */ av_register_all(); /* allocate the output media context */ avformat_alloc_output_context2(&oc, NULL, NULL, filename); if (!oc) { printf(" Could not deduce output format from file extension. "); return 0; } if (!oc) { return 1; } fmt = oc->oformat; /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ video_st = NULL; audio_st = NULL; if (fmt->video_codec != AV_CODEC_ID_NONE) { fmt->video_codec = AV_CODEC_ID_H264; video_st = add_stream(oc, &video_codec, fmt->video_codec); } if (fmt->audio_codec != AV_CODEC_ID_NONE) { fmt->audio_codec = AV_CODEC_ID_AAC; audio_st = add_stream(oc, &audio_codec, fmt->audio_codec); } /* open the output file, if needed */ if (!(fmt->flags & AVFMT_NOFILE)) { ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, "Could not open '%s': %s ", filename, av_err2str(ret)); return 0; } } /* Write the stream header, if any. */ ret = avformat_write_header(oc, NULL); if (ret < 0) { fprintf(stderr, "Error occurred when opening output file: %s ", av_err2str(ret)); return 0; } video_st->time_base.den = 90000; video_st->time_base.num = 1; long pts_time = 0; long pts_atime = 0; long pts_vtime = 0; printf("ffmpeg mux start : "); /*g711 data source*/ int m = 0; int n = 0; char g711a[320]; FILE *infile=fopen("./doc/stream-file/8k_1_16.g711a","rb"); /*g711_to_aac*/ InitParam initParam; initParam.u32AudioSamplerate=8000; initParam.ucAudioChannel=1; initParam.u32PCMBitSize=16; initParam.ucAudioCodec = Law_ALaw; Easy_Handle handle = Easy_AACEncoder_Init(initParam); int bAACBufferSize = 4*1024; unsigned char *pbAACBuffer = (unsigned char*)malloc(bAACBufferSize * sizeof(unsigned char)); unsigned int aac_len = 0; int video_tag=0; while(1){ AVPacket pkt = { 0 }; av_init_packet(&pkt); pkt.dts = pts_time; pkt.pts = pts_time; pkt.size = 0; if(video_tag == 1) { pts_vtime += 90000/25; //(video_st->time_base.num*1000/video_st->time_base.den)// pkt.dts = pts_vtime; pkt.pts = pts_vtime; pkt.stream_index = video_st->index; /*h264 mux*/ char str[32]; sprintf(str,"./avc_raw/avc_raw_%03d.h264",m); char* h264_raw = get_file_frame(str); if(h264_raw != NULL){ pkt.size = file_size; pkt.data = (unsigned char*)h264_raw; ret = av_interleaved_write_frame(oc, &pkt); } free(h264_raw); if(++m > 248) break; video_tag = 0; } else { pts_atime += 90000/25/2; pkt.dts = pts_atime; pkt.pts = pts_atime; pkt.stream_index = audio_st->index; #if 1 /*aac mux*/ char str[32]; sprintf(str,"./aac_raw/aac_raw_%03d.aac",n); char* aac_raw = get_file_frame(str); if(aac_raw != NULL){ pkt.size = file_size; pkt.data = (unsigned char*)aac_raw; ret = av_interleaved_write_frame(oc, &pkt); } free(aac_raw); if(++n > 431) break; #else /*g711a mux*/ if(fread(g711a,1,320,infile) > 0) { if(Easy_AACEncoder_Encode(handle, (unsigned char*)g711a, 320, pbAACBuffer, &aac_len) > 0) { pkt.size = aac_len-7; pkt.data = (unsigned char*)(&pbAACBuffer[7]); ret = av_interleaved_write_frame(oc, &pkt); } }else{ fseek(infile, 0, SEEK_SET); continue; } #endif video_tag = 1; } } av_write_trailer(oc); avio_close(oc->pb); avformat_free_context(oc); printf(" ---FILE:%s-line %d --- ",__FILE__,__LINE__); printf("ffmpeg save file done !!! "); return 0; }
ffmpeg muxer pts/dts:
https://blog.csdn.net/leixiaohua1020/article/details/39802913#t2
end