指导1:制作屏幕录像
源代码:tutorial01.c
概要
电影文件有很多基本的组成部分。首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接着,你有一组流,例如,你经常有的是一个音频流和一个视频流。(一个流只是一种想像出来的词语,用来表示一连串的通过时间来串连的数据元素)。在流中的数据元素被称为帧Frame。每个流是由不同的编码器来编码生成的。编解码器描述了实际的数据是如何被编码Coded和解码DECoded的,因此它的名字叫做CODEC。Divx和MP3就是编解码器的例子。接着从流中被读出来的叫做包Packets。包是一段数据,它包含了一段可以被解码成方便我们最后在应用程序中操作的原始帧的数据。根据我们的目的,每个包包含了完整的帧或者对于音频来说是许多格式的完整帧。
基本上来说,处理视频和音频流是很容易的:
10 从video.avi文件中打开视频流video_stream
20 从视频流中读取包到帧中
30 如果这个帧还不完整,跳到20
40 对这个帧进行一些操作
50 跳回到20
|
在这个程序中使用ffmpeg来处理多种媒体是相当容易的,虽然很多程序可能在对帧进行操作的时候非常的复杂。因此在这篇指导中,我们将打开一个文件,读取里面的视频流,而且我们对帧的操作将是把这个帧写到一个PPM文件中。
打开文件
首先,来看一下我们如何打开一个文件。通过ffmpeg,你必需先初始化这个库。(注意在某些系统中必需用<ffmpeg/avcodec.h>和<ffmpeg/avformat.h>来替换)
#include <avcodec.h>
#include <avformat.h>
...
int main(int argc, charg *argv[]) {
av_register_all();
|
这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要这样做。
现在我们可以真正的打开文件:
AVFormatContext *pFormatCtx;
// Open video file
if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
return -1; // Couldn't open file
|
我们通过第一个参数来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。
这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息:
// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information
|
这个函数为pFormatCtx->streams填充上正确的信息。我们引进一个手工调试的函数来看一下里面有什么:
// Dump information about file onto standard error
dump_format(pFormatCtx, 0, argv[1], 0);
|
现在pFormatCtx->streams仅仅是一组大小为pFormatCtx->nb_streams的指针,所以让我们先跳过它直到我们找到一个视频流。
int i;
AVCodecContext *pCodecCtx;
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
|
流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文)的东西。这里面包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。但是我们必需要找到真正的编解码器并且打开它:
AVCodec *pCodec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
return -1; // Could not open codec
|
有些人可能会从旧的指导中记得有两个关于这些代码其它部分:添加CODEC_FLAG_TRUNCATED到pCodecCtx->flags和添加一个hack来粗糙的修正帧率。这两个修正已经不在存在于ffplay.c中。因此,我必需假设它们不再必要。我们移除了那些代码后还有一个需要指出的不同点:pCodecCtx->time_base现在已经保存了帧率的信息。time_base是一个结构体,它里面有一个分子和分母(AVRational)。我们使用分数的方式来表示帧率是因为很多编解码器使用非整数的帧率(例如NTSC使用29.97fps)。
保存数据
现在我们需要找到一个地方来保存帧:
AVFrame *pFrame;
// Allocate video frame
pFrame=avcodec_alloc_frame();
|
因为我们准备输出保存24位RGB色的PPM文件,我们必需把帧的格式从原来的转换为RGB。FFMPEG将为我们做这些转换。在大多数项目中(包括我们的这个)我们都想把原始的帧转换成一个特定的格式。让我们先为转换来申请一帧的内存。
// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
return -1;
|
即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始的数据。我们使用avpicture_get_size来获得我们需要的大小,然后手工申请内存空间:
uint8_t *buffer;
int numBytes;
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
|
av_malloc是ffmpeg的malloc,用来实现一个简单的malloc的包装,这样来保证内存地址是对齐的(4字节对齐或者2字节对齐)。它并不能保护你不被内存泄漏,重复释放或者其它malloc的问题所困扰。
现在我们使用avpicture_fill来把帧和我们新申请的内存来结合。关于AVPicture的结成:AVPicture结构体是AVFrame结构体的子集――AVFrame结构体的开始部分与AVPicture结构体是一样的。
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
|
最后,我们已经准备好来从流中读取数据了。
读取数据
我们将要做的是通过读取包来读取整个视频流,然后把它解码成帧,最好后转换格式并且保存。
int frameFinished;
AVPacket packet;
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
packet.data, packet.size);
// Did we get a video frame?
if(frameFinished) {
// Convert the image from its native format to RGB
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,
(AVPicture*)pFrame, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height);
// Save the frame to disk
if(++i<=5)
SaveFrame(pFrameRGB, pCodecCtx->width,
pCodecCtx->height, i);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}
|
这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到AVPacket结构体中。注意我们仅仅申请了一个包的结构体――ffmpeg为我们申请了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过av_free_packet()来释放。函数avcodec_decode_video()把包转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得到下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。最后,我们使用img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。要记住,你可以把一个AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。
关于包Packets的注释
从技术上讲一个包可以包含部分或者其它的数据,但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧。
|
现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
|
我们做了一些标准的文件打开动作,然后写入RGB数据。我们一次向文件写入一行数据。PPM格式文件的是一种包含一长串的RGB数据的文件。如果你了解HTML色彩表示的方式,那么它就类似于把每个像素的颜色头对头的展开,就像#ff0000#ff0000....就表示了了个红色的屏幕。(它被保存成二进制方式并且没有分隔符,但是你自己是知道如何分隔的)。文件的头部表示了图像的宽度和高度以及最大的RGB值的大小。
现在,回顾我们的main()函数。一旦我们开始读取完视频流,我们必需清理一切:
// Free the RGB image
av_free(buffer);
av_free(pFrameRGB);
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 0;
|
你会注意到我们使用av_free来释放我们使用avcode_alloc_fram和av_malloc来分配的内存。
上面的就是代码!下面,我们将使用Linux或者其它类似的平台,你将运行:
gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lavutil -lm
|
如果你使用的是老版本的ffmpeg,你可以去掉-lavutil参数:
gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lm
|
大多数的图像处理函数可以打开PPM文件。可以使用一些电影文件来进行测试。
分享到:
相关推荐
包含翻译后的API文档:ffmpeg-4.3.2-1.5.5-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.bytedeco:ffmpeg:4.3.2-1.5.5; 标签:bytedeco、ffmpeg、中文文档、jar包、java; 使用方法:解压翻译后的API文档,...
包含翻译后的API文档:ffmpeg-4.3.2-1.5.5-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.bytedeco:ffmpeg:4.3.2-1.5.5; 标签:bytedeco、ffmpeg、中英对照文档、jar包、java; 使用方法:解压翻译...
包含翻译后的API文档:ffmpeg-5.0-1.5.7-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.bytedeco:ffmpeg:5.0-1.5.7; 标签:bytedeco、ffmpeg、中英对照文档、jar包、java; 使用方法:解压翻译后的...
包含翻译后的API文档:ffmpeg-5.0-1.5.7-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.bytedeco:ffmpeg:5.0-1.5.7; 标签:bytedeco、ffmpeg、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用...
1. **FFmpeg 命令行工具**:FFmpeg 提供了一系列命令行工具,如 ffmpeg 用于转换媒体文件,ffplay 可以播放多媒体文件,ffprobe 用于分析媒体信息,而 ffserver 则支持多媒体流服务。每个工具都有其特定的选项和参数...
1. FFmpeg基本概念: - FFmpeg由多个组件构成,包括ffmpeg命令行工具、libavcodec编码库、libavformat解封装库、libavfilter过滤器库和libavutil通用工具库等。 - FFmpeg支持多种音视频编码格式,如H.264、HEVC、...
FFmpeg中文文档是一份详细的指南,涵盖了FFmpeg工具的各个方面,包括命令语法、编码解码、滤镜应用等关键知识点。FFmpeg是一个强大的开源工具集,用于处理音视频数据,支持各种格式的转换、捕获和流媒体操作。 1. *...
FFmpeg 是一个强大的开源工具,用于处理多媒体文件,包括音频和视频。在视频处理领域,FFmpeg 提供了丰富的功能...如果遇到问题,可以查阅 `使用说明-自己研究的方法` 文档或在线搜索 FFmpeg 的官方文档获取更多帮助。
1. **音频编码与解码**:FFmpeg支持众多音频编码格式,如AAC、MP3、Vorbis等。通过libavcodec库,FFmpeg可以对音频进行编码和解码,转换不同格式的音频文件。用户可以使用ffmpeg命令行工具进行音频格式转换,例如将...
英文文档(https://ffmpeg.org/ffmpeg-all.html): ffmpeg Documentation(ffmpeg.org ffmpeg-all.html).pdf 中文文档(https://ffmpeg.github.net.cn/ffmpeg-all.html): ffmpeg 文档_ FFmpeg中文网.pdf
1. 学习曲线:FFmpeg 的源码复杂且庞大,对于初学者来说,理解其工作原理可能有一定难度,但可以通过阅读官方文档、参与社区讨论和实践编码来逐步掌握。 2. 编译构建:要从源码编译 FFmpeg,你需要安装必要的依赖,...
1. FFMPEG功能概述 FFMPEG是一款功能强大的多媒体处理工具,主要用于音视频的编码、解码、播放和转码等。其主要功能可以划分为以下几个方面: * 编码解码:包括音频视频的编码解码,支持的格式庞大。 * 转码:...
1. **FFmpeg 命令行工具**:FFmpeg 包含一系列可执行文件,如 `ffmpeg`、`ffprobe` 和 `ffplay`。`ffmpeg` 主要用于转换多媒体文件,`ffprobe` 用于获取媒体文件的元数据,而 `ffplay` 是一个简单的媒体播放器。 2....
ffmpeg.js 该库提供了使用将FFmpeg构建移植到JavaScript的功能。 生成针对浏览器内部使用进行了优化:最小的大小可加快加载速度,asm.js,性能调整等。尽管它们也可在Node中使用。 建物 当前可用的版本(将来可能会...
这份"ffmpeg中文说明文档"将帮助我们深入理解FFmpeg的内部运作机制以及如何在实际项目中有效利用它。 《ffdoc.pdf》可能是FFmpeg的官方文档中文版,这份文档通常会涵盖FFmpeg的基本概念、架构、命令行工具的使用,...
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex "[0:v][1:v]overlay=W-w-10:H-h-10[out]" -map "[out]" output.mp4 ``` 这里使用 `overlay` 滤镜将两个视频流合并在一起。 #### 八、复制流 在某些情况下,...
1. **配置与安装**:FFmpeg 的安装过程可能会因操作系统而异,通常包括获取源代码、编译和安装。在 Linux 上,可能需要通过编译源代码来安装,而在 Windows 或 macOS 上,可以使用预编译的二进制版本。安装过程中,...
1. FFmpeg 命令行工具: FFmpeg 是核心工具,能够进行视频和音频的转换、编码、解码、合并、分割等多种操作。例如,你可以用它来将一个视频文件转换为不同的格式,调整分辨率,改变比特率,或者提取音频轨道。`...
2. FFmpeg文档:压缩包中包含的FFmpeg中文开发文档对于开发者来说极其宝贵,它详细阐述了FFmpeg的API用法、解码器的实现原理以及如何使用FFmpeg进行多媒体处理。这些文档可以帮助开发者理解FFmpeg的内部工作流程,...
1. **下载ffmpeg源码**:首先,你需要从官方或者可靠的第三方源下载ffmpeg的源代码。确保下载的版本与OpenCV 4.7.0兼容。例如,你可以访问ffmpeg的官方网站或者通过Git仓库获取。 2. **配置ffmpeg**:解压下载的...