FFmpeg处理流程
ffmpeg -f avfoundation -i 1 -r 30 out.yuv
以上为一个录制屏幕的命令
Input #0, avfoundation, from '1':
Duration: N/A, start: 19326.762000, bitrate: N/A
Stream #0:0: Video: rawvideo (UYVY / 0x59565955), uyvy422, 1920x1080, 1000k tbr, 1000k tbn, 1000k tbc
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> rawvideo (native))
Press [q] to stop, [?] for help
Output #0, rawvideo, to 'out.yuv':
Metadata:
encoder : Lavf58.20.100
Stream #0:0: Video: rawvideo (UYVY / 0x59565955), uyvy422, 1920x1080, q=2-31, 995328 kb/s, 30 fps, 30 tbn, 30 tbc
Metadata:
encoder : Lavc58.35.100 rawvideo
从上面Stream行可以发现:视频的像素格式是 uyvy422,大小是1920x1080
使用:ffplay -s 1920x1080 -pix_fmt uyvy422 out.yuv 播放视频
ffmpeg -f avfoundation -list_devices true -i ""
[AVFoundation input device @ 0x7fbb18d03e80] AVFoundation video devices:
[AVFoundation input device @ 0x7fbb18d03e80] [0] FaceTime HD Camera (Built-in)
[AVFoundation input device @ 0x7fbb18d03e80] [1] Capture screen 0
[AVFoundation input device @ 0x7fbb18d03e80] AVFoundation audio devices:
[AVFoundation input device @ 0x7fbb18d03e80] [0] Built-in Microphone
ffmpeg -f avfoundation -i :0 out.wav
:
(冒号)之前指定,视频设备在 冒号 之后指定ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv
Metadata:
major_brand : mp42
minor_version : 1
compatible_brands: isommp423gp5
creation_time : 2018-11-02T08:07:34.000000Z
encoder : FormatFactory : www.pcfreetime.com
Duration: 00:06:24.08, start: 0.000000, bitrate: 356 kb/s
Stream #0:0(und): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 228 kb/s, 14.91 fps, 14.91 tbr, 14906 tbn, 7453 tbc (default)
ffpeg -i test.mov -an -vcodec copy out.h264
这是一个只要视频,不要音频的分割命令
ffmpeg -i input.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv
参数解释:
FFmpeg 提取PCM数据
ffmpeg -i out.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm
ffplay -ar 44100 -ac 2 -f s16le out.pcm
掌握这些获取原始数据的命令,可以方便进行对比
通过ffmpeg做二次开发,需要提取出其中的yuv数据,做每一帧的分析等,需要知道这些命令
例如:声道反转等,需要知道这些原始数据
ffmpeg -i in.mp4 -ss 00:00:00 -t 10 out.ts
ffmpeg -f concat -i inputs.txt out.flv
input.txt文件
file '1.ts'
file '2.ts'
file '3.ts'
视频转图片
ffmpeg -i in.flv -r 1 -f image2 image-%3d.jpeg
参数解释:
图片转视频
ffmpeg -i image-%3d.jpeg out.mp4
参数解释:
ffmpeg -re -i out.mp4 -c copy -f flv rtmp://server/live/streamName
参数解释:
拉流
ffmpeg -i rtmp://server/live/streamName -c copy dump.flv
ffmpeg -i rtmp://ivi.bupt.edu.cn/hls/cctv1hd.m3u8 -c copy out.flv
这样ffmpeg 就会从某个服务器中网本地存数据
ffmpeg -i test.mp4 -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4
#include
av_log_set_level(AV_LOG_DEBUG)
av_log(NULL, AV_LOG_INFO, "...%s\n", op);
这个的函数 av_log_set_level是设置输出日志的等级,凡是比这个参数等级高的都要输出
这个函数 av_log(NULL, AV_LOG_INFO, “…%s\n”, op)
常用的日志级别
#include "libavutil/log.h"
#include
int main(){
set_av_log_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_INFO, "HelloWorld\n");
return 0;
}
#include
#include "libavutil/log.h"
int main(){
set_av_log_level(AV_LOG_DEBUG);
int ret = avpriv_io_delete("./test.txt");
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "delete file error\n");
return -1;
}
ret = avpriv_io_move("11.txt", "22.txt");
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "move file error\n");
return -1;
}
return 0;
}
重要结构体
当调用 open_dir 的时候,ffmepg就会生成AVIODirContext结构体
read_dir的时候,就要传入这个 AVIODirContext结构体
close_dir的时候,传入的特使AVIODirContext
使用ffmpeg实现 ls 命令
#include
#include
int main(){
av_log_set_level(AV_LOG_INFO);
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
int ret = avio_open_dic(&ctx, "./", NULL);
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "Can't 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't read dir %s\n", av_err2str(ret));
return -1;
}
if(!entry){//读到目录最后一项
break;
}
av_log(NULL, AV_LOG_INFO, "%12"PRId64" %s \n",entry->size, entry->name );//打印大小和名字
}
return 0;
}
几个重要的结构体
执行顺序:解复用 -> 获取流 -> 读取数据包 -> 释放资源
#include
#include
int main(){
av_log_set_level(AV_LOG_INFO);
av_regester_all();
AVFormatContext *fmt_context = NULL;
int ret = avformat_open_input(&fmt_context, "./test.mp4", NULL);
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "con't open file %s\n", av_err2str(ret));//打印错误码
return -1;
}
av_dump_format(fmt_context, 0, "./test.mp4", 0);
avformat_close_input(&fmt_context);
return 0;
}
av_init_packet()
av_find_best_stream()
av_read_frame()
av_packet_unref()
#include
#include
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;
szAdtsHeader[1] = 0xf0;
szAdtsHeader[1] |= (0<<3);
szAdtsHeader[1] |= (0<<1);
szAdtsHeader[1] |= 1;
szAdtsHeader[2] = (audio_object_type - 1)<<6;
szAdtsHeader[2] |= (sampling_frequency_index & 0x0f)<<2;
szAdtsHeader[2] |= (0 << 1);
szAdtsHeader[2] |= (channel_config & 0x04)>>2;
szAdtsHeader[3] = (channel_ config & 0x03)<<6;
szAdtsHeader[3] |= (0 << 5) ;
szAdtsHeader[3] |= (0 << 4) ;
szAdtsHeader[3] |= (0 << 3) ;
szAdtsHeader[3] |= (0 << 2) ;
szAdtsHeader[3] |= ( (adtsLen & 0x1800) >> 11) ;
szAdtsHeader[4] = (uint8_ t)( (adtsLen & 0x7f8) >> 3) ;
szAdtsHeader[5] = (uint8_ t)( (adtsLen & 0x7) << 5);
szAdtsHeader[5] |= 0x1f ;
szAdtsHeader[6] = 0xfc;
}
int main(){
av_log_set_level(AV_LOG_INFO);
av_regester_all();
AVFormatContext *fmt_context = NULL;
int ret = avformat_open_input(&fmt_context, "./test.mp4", NULL);
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "con't open file %s\n", av_err2str(ret));//打印错误码
return -1;
}
av_dump_format(fmt_context, 0, "./test.mp4", 0);
avformat_close_input(&fmt_context);
// 获取 想要处理的流
int audio_index;
audio_index = av_find_best_stream(fmt_context, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);//如果audio_index >=0,则其代表找到是音频数据的索引值
if(audio_index < 0){
av_log(NULL, AV_LOG_ERROR, "con't file stream file %s\n", av_err2str(ret));//打印错误码
return -1;
}
// 打开一个可供写入的文件
FILE* dst_fd = fopen(“./test.aac”, "wb");
if(!dst_fd){
av_log(NULL, AV_LOG_ERROR, "Can't open out file\n");
avformat_close_input(&fmt_context);
return -1;
}
// 读取信息
AVPacket pkt;
av_init_packet(&pkt);
while(av_read_frame(fmt_context, &pkt) >= 0){// 要读取所有的数据帧,
if(audio_index == pkt.stream_index){// 如果相同则代表找到流了
// 添加一个头,表示如何播放分离出来的audio。如果不加上头,不知道如何播放
char adts_header_buf[7];
adts_header(adts_header_buf, pkt.size);
fwrite(dats_header_buf, 1, 7, dst_fd);
// 将获取的音频数据输出到一个新文件中
int len = fwrite(pkt.data, 1 , pkt.size, dst_fd);//写如文件
if(len != pkt.size){
av_log(NULL, AV_LOG_WARNING, "waning file write length data error");
}
}
av_packet_unref(pkt);//需要释放引用计数,防止内存泄漏
}
// 关闭文件
avformat_close_input(&fmt_context);
if(dst_fd){
fclose(dft_fd);
}
return 0;
}
Start Code
SPS/PPS
codec->extradata
int main(){
int err_code;
char errors[1024];
char *src_filename = NULL;
char *dst_fileanme = NULL;
FILE *dst_fd = NULL;
int video_stream_index = -1;
AVFormatContext *fmt_ctx = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
if(argc < 3){
av_log(NULL, AV_LOG_DEBUG, "the count of parameters shoudler be more than three~");
return -1;
}
src_filename = argv[1];
dsf_filename = argv[2];
if(src_filename == NULL || dsf_filename == NULL){
av_log(NULL, AV_LOG_ERROR, "src or dts file is null
\n");
return -1;
}
av_register_all();
dst_fd = fopen(dst_filename, "wb");
if(!dst_fd){
av_log(NULL, AV_LOG_ERROR, "Can't open destionation file\n");
return -1;
}
if((err_code = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)) < 0){
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Con't open source file:%s ,%d(%s)\n", src_filename, err_code, errors);
return -1;
}
av_dump_format(fmt_ctx, 0, src_filename, 0);//打印多媒体信息
av_init_packet(&pkt);
pkt_data = NULL;
pkt_size = 0;
video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(video_stream_index < 0){
av_log(NULL, AV_LOG_DEBUG, "Can't find %s stream in input file %s\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO), src_filename);
return AVERROR(EINVAL);
}
while(av_read_frame(fmt_ctx, &pkt) >= 0){
if(pkt.stream_index == video_stream_index){
h264_mp4toannexb(fmt_ctx, &pkt, dst_df);//自定义函数,设置 Start Code和SPS/PPSs,懒得写。。。。
}
av_packet_unref(&pkt);// 处理过的包就去掉引用
}
}
环境打死配不好,手敲要死了
感谢帮助
https://blog.csdn.net/lijjianqing/article/details/54707785
https://blog.csdn.net/zhouxj0818/article/details/52679741