文章目錄
- FFmpeg代碼結構
- FFmpeg日志系統
- FFmpeg檔案與目錄操作
-
- 檔案的删除與重命名
- 操作目錄重要函數
- FFmpeg處理流資料的基本概念
-
- 幾個重要的結構體
- FFmpeg操作流資料的基本概念
- FFmpeg列印音視訊Meta資訊
- FFmpeg抽取音頻資料
- FFmpeg抽取視訊H264資料
- FFmpeg将mp4轉成flv
- FFmpeg音視訊裁剪
- FFmpeg日志的使用及目錄操作
- FFmpeg的基本概念及常用結構體
- 對複用 / 解複用及流操作的各種實戰
FFmpeg代碼結構
libavcodec | 提供了一系列編碼器的實作 |
libavformat | 實作在流協定,容器格式及其本IO通路 |
libavutil | 包括hash器,解碼器和各種工具函數 |
libavfilter | 提供了各種音視訊過濾器 |
libavdevice | 提供了通路捕獲裝置和回放裝置的接口 |
libswresample | 實作了混音和重采樣 |
libswscale | 實作了色彩轉換和縮放工具 |
FFmpeg日志系統
include <libavutil/log.h>
av_log_set_level(AV_LOG_DEBUG)
av_log(NULL,AV_LOG_INFO,"...%s\n",op)
常用日志級别
- AV_LOG_ERROR //級别最高
- AV_LOG_WARNING
- AV_LOG_INFO
- AV_LOG_DEBUG
示例:
vi ffmpeg_log.c
#include<stdio.h>
#include<libavutil/log.h>
int main(int argc,char* argv[])
{
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL,AV_LOG_INFO,"hello world!%s\n","abc");
return 0;
}
clang -g -o ffmpeg_log ffmpeg_log.c -lavutil
./ffmpeg_log
列印:
hello world!abc
FFmpeg檔案與目錄操作
檔案的删除與重命名
- avpriv_io_delete()
- avpriv_io_move()
vi ffmpeg_file.c
#include<libavformat/avformat.h>
int main(int argc,char* argv[])
{
int ret;
ret = avpriv_io_move("11.txt","22.txt");
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"failed to rename\n");
return -1;
}
av_log(NULL,AV_LOG_INFO,"success to rename\n");
ret = avpriv_io_delete("./mytestfile.txt");
if(ret < 0 ){
av_log(NULL,AV_LOG_ERROR,"delete failed\n");
return -1;
}
av_log(NULL,AV_LOG_INFO,"success to delete\n");
return 0;
}
clang -g -o ffmpeg_file ffmpeg_file.c `pkg-config --libs libavformat`
./ffmpeg_file
列印:
success to rename
success to delete
操作目錄重要函數
- avio_open_dir()
- avio_read_dir()
- avio_close_dir()
- AVIODirContext 操作目錄的上下文
- AVIODirEntry 目錄項。用于存放檔案名,檔案大小等資訊。
實作簡單的ls指令
ls -alt
#include<libavutil/log.h>
#include<libavformat/avformat.h>
int main(int argc,char* argv[])
{
int ret;
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
av_log_set_level(AV_LOG_INFO);
ret = avio_open_dir(&ctx,"./",NULL);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"can not open dir:%s\n",av_err2str(ret));
return -1;
}
while(1){
ret = avio_read_dir(ctx,&entry);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"can not read dir:%s\n",av_err2str(ret));
goto __fail;
}
if(!entry){
break;
}
av_log(NULL,AV_LOG_INFO,"%12"PRId64 " %s \n",entry->size,entry->name);
avio_free_directory_entry(&entry);
}
__fail:
avio_close_dir(&ctx);
return 0;
}
clang -g -o mylist ffmpeg_list.c `pkg-config --libs libavformat libavutil`
./mylist #輸出檔案清單
FFmpeg處理流資料的基本概念
- 多媒體檔案其實是個容器
- 在容器裡有很多流(Stream/Track)
- 每種流是由不同的編碼器編碼的
- 從流中讀出的資料稱為包
- 在一個包中包含着一個或多個幀
幾個重要的結構體
- AVFormatContext
- AVStream
- AVPacket
FFmpeg操作流資料的基本概念
解複用–>擷取流–>讀資料包–>釋放資源
FFmpeg列印音視訊Meta資訊
- av_register_all()
- avformat_open_input()/avformat_close_input
- av_dump_format()
vi mediainfo.c
#include<libavutil/log.h>
#include<libavformat/avformat.h>
int main(int argc,char* argv[])
{
int ret;
AVFormatContext *fmt_ctx = NULL;
av_log_set_level(AV_LOG_INFO);
av_register_all();
ret = avformat_open_input(&fmt_ctx,"./test.mp4",NULL,NULL);
if(ret<0){
av_log(NULL,AV_LOG_ERROR,"can not open file:%s\n",av_err2str(ret));
return -1;
}
av_dump_format(fmt_ctx,0,"./test.mp4",0);
avformat_close_input(&fmt_ctx);
return 0;
}
clang -g -o mediainfo mediainfo.c `pkg-config --libs libavutil libavformat`
./mediainfo

FFmpeg抽取音頻資料
- av_init_packet()
- av_find_best_stream()
- av_read_frame()/av_packet_unref()
vi exrta_audio.c
#include<stdio.h>
#include<libavutil/log.h>
#include<libavformat/avformat.h>
#define ADTS_HEADER_LEN 7;
void adts_header(char *szAdtsHeader, int dataLen){
int audio_object_type = 2;
int sampling_frequency_index = 7;
int channel_config = 2;
int adtsLen = dataLen + 7;
szAdtsHeader[0] = 0xff; //syncword:0xfff 高8bits
szAdtsHeader[1] = 0xf0; //syncword:0xfff 低4bits
szAdtsHeader[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
szAdtsHeader[1] |= (0 << 1); //Layer:0 2bits
szAdtsHeader[1] |= 1; //protection absent:1 1bit
szAdtsHeader[2] = (audio_object_type - 1)<<6; //profile:audio_object_type - 1 2bits
szAdtsHeader[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index 4bits
szAdtsHeader[2] |= (0 << 1); //private bit:0 1bit
szAdtsHeader[2] |= (channel_config & 0x04)>>2; //channel configuration:channel_config 高1bit
szAdtsHeader[3] = (channel_config & 0x03)<<6; //channel configuration:channel_config 低2bits
szAdtsHeader[3] |= (0 << 5); //original:0 1bit
szAdtsHeader[3] |= (0 << 4); //home:0 1bit
szAdtsHeader[3] |= (0 << 3); //copyright id bit:0 1bit
szAdtsHeader[3] |= (0 << 2); //copyright id start:0 1bit
szAdtsHeader[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
szAdtsHeader[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中間8bits
szAdtsHeader[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value 低3bits
szAdtsHeader[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
szAdtsHeader[6] = 0xfc;
}
int main(int argc,char* argv[])
{
int ret;
int len;
int audio_index;
char* src = NULL;
char* dst = NULL;
AVPacket pkt;
AVFormatContext *fmt_ctx = NULL;
av_log_set_level(AV_LOG_INFO);
av_register_all();
//1 read two params from console
if(argc < 3){
av_log(NULL,AV_LOG_ERROR,"this count of params should be more than 3 \n");
return -1;
}
src = argv[1];
dst = argv[2];
if(!src || !dst){
av_log(NULL,AV_LOG_ERROR,"src or dst is null! \n");
}
ret = avformat_open_input(&fmt_ctx,src,NULL,NULL);
if(ret<0){
av_log(NULL,AV_LOG_ERROR,"can not open file:%s\n",av_err2str(ret));
return -1;
}
FILE* dst_fd = fopen(dst,"wb");
if(!dst_fd){
av_log(NULL,AV_LOG_ERROR,"can not open out file! \n");
avformat_close_input(&fmt_ctx);
return -1;
}
av_dump_format(fmt_ctx,0,src,0);
//2 get stream
ret = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if(ret<0){
av_log(NULL,AV_LOG_ERROR,"can not find best stream! \n");
avformat_close_input(&fmt_ctx);
fclose(dst_fd);
return -1;
}
audio_index = ret;
av_init_packet(&pkt);
while(av_read_frame(fmt_ctx,&pkt) >= 0){
if(pkt.stream_index == audio_index){
char adts_header_buf[7];
adts_header(adts_header_buf,pkt.size);
fwrite(adts_header_buf,1,7,dst_fd);
//3 write audio data to aac file.
len = fwrite(pkt.data,1,pkt.size,dst_fd);
if(len != pkt.size){
av_log(NULL,AV_LOG_WARNING,"waning,length of data is not equal size of pkt! \n");
}
}
av_packet_unref(&pkt);
}
avformat_close_input(&fmt_ctx);
if(dst_fd){
fclose(dst_fd);
}
return 0;
}
clang -g -o extra_audio extra_audio.c `pkg-config --libs libavutil libavformat`
./extra_audio /Volumes/SoftCode/test/test02/test.mp4 ./test.aac
ffplay /Volumes/SoftCode/test/test02/test.aac
FFmpeg抽取視訊H264資料
- Start code
- SPS/PPS
- codec->extradata
FFmpeg将mp4轉成flv
- avformat_alloc_output_context2() / avformat_free_context()
- avformat_new_stream()
- avcodec_parameters_copy()
- avformat_write_header()
- avformat_write_frame()/av_interleaved_write_frame()
- av_write_trailer()
vi remuxing.c
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
tag,
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
pkt->stream_index);
}
int main(int argc, char **argv)
{
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, i;
int stream_index = 0;
int *stream_mapping = NULL;
int stream_mapping_size = 0;
if (argc < 3) {
printf("usage: %s input output\n"
"API example program to remux a media file with libavformat and libavcodec.\n"
"The output format is guessed according to the file extension.\n"
"\n", argv[0]);
return 1;
}
in_filename = argv[1];
out_filename = argv[2];
av_register_all();
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
stream_mapping_size = ifmt_ctx->nb_streams;
stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
ret = AVERROR(ENOMEM);
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index++;
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
av_dump_format(ofmt_ctx, 0, out_filename, 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
if (pkt.stream_index >= stream_mapping_size ||
stream_mapping[pkt.stream_index] < 0) {
av_packet_unref(&pkt);
continue;
}
pkt.stream_index = stream_mapping[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
log_packet(ifmt_ctx, &pkt, "in");
/* copy packet */
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
log_packet(ofmt_ctx, &pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
av_packet_unref(&pkt);
}
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}
clang -g -o remuxing remuxing.c `pkg-config --libs libavutil libavformat`
./remuxing /Volumes/SoftCode/yinship_test/test03/test.mp4 test.flv
ffplay test.flv
FFmpeg音視訊裁剪
- av_seek_frame()
vi cutvideo.c
#include <stdlib.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
tag,
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
pkt->stream_index);
}
int cut_video(double from_seconds, double end_seconds, const char* in_filename, const char* out_filename) {
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
int ret, i;
av_register_all();
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
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");
goto end;
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
av_dump_format(ofmt_ctx, 0, out_filename, 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
// int indexs[8] = {0};
// int64_t start_from = 8*AV_TIME_BASE;
ret = av_seek_frame(ifmt_ctx, -1, from_seconds*AV_TIME_BASE, AVSEEK_FLAG_ANY);
if (ret < 0) {
fprintf(stderr, "Error seek\n");
goto end;
}
int64_t *dts_start_from = malloc(sizeof(int64_t) * ifmt_ctx->nb_streams);
memset(dts_start_from, 0, sizeof(int64_t) * ifmt_ctx->nb_streams);
int64_t *pts_start_from = malloc(sizeof(int64_t) * ifmt_ctx->nb_streams);
memset(pts_start_from, 0, sizeof(int64_t) * ifmt_ctx->nb_streams);
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
log_packet(ifmt_ctx, &pkt, "in");
if (av_q2d(in_stream->time_base) * pkt.pts > end_seconds) {
av_free_packet(&pkt);
break;
}
if (dts_start_from[pkt.stream_index] == 0) {
dts_start_from[pkt.stream_index] = pkt.dts;
printf("dts_start_from: %s\n", av_ts2str(dts_start_from[pkt.stream_index]));
}
if (pts_start_from[pkt.stream_index] == 0) {
pts_start_from[pkt.stream_index] = pkt.pts;
printf("pts_start_from: %s\n", av_ts2str(pts_start_from[pkt.stream_index]));
}
/* copy packet */
pkt.pts = av_rescale_q_rnd(pkt.pts - pts_start_from[pkt.stream_index], in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts - dts_start_from[pkt.stream_index], in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
if (pkt.pts < 0) {
pkt.pts = 0;
}
if (pkt.dts < 0) {
pkt.dts = 0;
}
pkt.duration = (int)av_rescale_q((int64_t)pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
log_packet(ofmt_ctx, &pkt, "out");
printf("\n");
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
av_free_packet(&pkt);
}
free(dts_start_from);
free(pts_start_from);
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}
int main(int argc, char *argv[]){
if(argc < 5){
fprintf(stderr, "Usage: \
command startime, endtime, srcfile, outfile");
return -1;
}
double startime = atoi(argv[1]);
double endtime = atoi(argv[2]);
cut_video(startime, endtime, argv[3], argv[4]);
return 0;
}
clang -g -o cutvideo cutvideo.c `pkg-config --libs libavutil libavformat libavcodec`
./cutvideo 10 100 /Volumes/SoftCode/yinship_test/test03/test.mp4 testcut.mp4
ffplay testcut.mp4