mp4文件解封装:
视频文件(mp4 )–解封装—音频流(aac ), 视频流 (h264)
命令行参数: input.mp4 out.h264 out.aac
#include
#include "libavutil/log.h"
#include "libavformat/avformat.h"
#define ERROR_STRING_SIZE 1024
#define ADTS_HEADER_LEN 7;
int adts_header(char * const p_adts_header, const int data_length,
const int profile, const int samplerate,
const int channels)
{}
int main(int argc, char **argv)
{
char *in_filename = argv[1];
char *h264_filename = argv[2];
char *aac_filename = argv[3];
FILE *aac_fd = NULL;
FILE *h264_fd = NULL;
h264_fd = fopen(h264_filename, "wb");
if(!h264_fd) {
printf("fopen %s failed\n", h264_filename);
return -1;
}
aac_fd = fopen(aac_filename, "wb");
if(!aac_fd) {
printf("fopen %s failed\n", aac_filename);
return -1;
}
AVFormatContext *ifmt_ctx = NULL;
int video_index = -1;
int audio_index = -1;
AVPacket *pkt = NULL;
int ret = 0;
char errors[ERROR_STRING_SIZE+1]; // 主要是用来缓存解析FFmpeg api返回值的错误string
ifmt_ctx = avformat_alloc_context();
if(!ifmt_ctx) {
printf("avformat_alloc_context failed\n");
// fclose(aac_fd);
return -1;
}
ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
if(ret < 0) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
avformat_close_input(&ifmt_ctx);
return -1;
}
video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(video_index == -1) {
avformat_close_input(&ifmt_ctx);
return -1;
}
audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if(audio_index == -1) {
avformat_close_input(&ifmt_ctx);
return -1;
}
// h264_mp4toannexb
const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb"); // 对应面向对象的方法
if(!bsfilter) {
avformat_close_input(&ifmt_ctx);
return -1;
}
AVBSFContext *bsf_ctx = NULL; // 对应面向对象的变量
ret = av_bsf_alloc(bsfilter, &bsf_ctx);
if(ret < 0) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
avformat_close_input(&ifmt_ctx);
return -1;
}
ret = avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[video_index]->codecpar);
if(ret < 0) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
avformat_close_input(&ifmt_ctx);
av_bsf_free(&bsf_ctx);
return -1;
}
ret = av_bsf_init(bsf_ctx);
if(ret < 0) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
avformat_close_input(&ifmt_ctx);
av_bsf_free(&bsf_ctx);
return -1;
}
pkt = av_packet_alloc();
av_init_packet(pkt);
while (1) {
ret = av_read_frame(ifmt_ctx, pkt); // 不会去释放pkt的buf,如果我们外部不去释放,就会出现内存泄露
if(ret < 0 ) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
printf("av_read_frame failed:%s\n", errors);
break;
}
// av_read_frame 成功读取到packet,则外部需要进行buf释放
// 处理视频 mp4的视频流不带startcode h264格式带,需加上
if(pkt->stream_index == video_index) {
// 处理视频
ret = av_bsf_send_packet(bsf_ctx, pkt);
if(ret < 0) {
av_strerror(ret, errors, ERROR_STRING_SIZE);
av_packet_unref(pkt);
continue;
}
while (1) {
ret = av_bsf_receive_packet(bsf_ctx, pkt);
if(ret != 0) {
break;
}
size_t size = fwrite(pkt->data, 1, pkt->size, h264_fd);
if(size != pkt->size)
{
av_log(NULL, AV_LOG_DEBUG, "h264 warning, length of writed data isn't equal pkt->size(%d, %d)\n",
size,
pkt->size);
}
av_packet_unref(pkt);
}
} else 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);
size_t size fwrite(pkt->data, 1, pkt->size, aac_fd);= ; // 写adts data
if(size != pkt->size)
{
av_log(NULL, AV_LOG_DEBUG, "aac warning, length of writed data isn't equal pkt->size(%d, %d)\n",
size,
pkt->size);
}
av_packet_unref(pkt);
} else {
av_packet_unref(pkt); // 释放buffer
}
}
printf("while finish\n");
failed:
if(h264_fd) {
fclose(h264_fd);
}
if(aac_fd) {
fclose(aac_fd);
}
if(pkt)
av_packet_free(&pkt);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
printf("Hello World!\n");
return 0;
}
debug:
测试音频流是否加上startcode
1 av_bsf_send_packet(bfs_ctx,pkt);
pkt: data
查看 Memory: 00 00 02 b0
2 fwrite(pkt->data,pkt->size,h264_fd);
pkt->data:
Memory: 00 00 00 01 (startcode)