搜档网
当前位置:搜档网 › 如何用FFmpeg编写一个简单播放器详细步骤介绍

如何用FFmpeg编写一个简单播放器详细步骤介绍

如何用FFmpeg编写一个简单播放器详细步骤介绍
如何用FFmpeg编写一个简单播放器详细步骤介绍

如何用FFmpeg编写一个简单播放器详细步骤介绍(转载)

FFmpeg, 播放器, 编写

FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具。FFMPEG 几乎为你把所有的繁重工作都做了,比如解码、编码、复用和解复用。这使得多媒体应用程序变得容易编写。它是一个简单的,用C编写的,快速的并且能够解码几乎所有你能用到的格式,当然也包括编码多种格式。

唯一的问题是它的文档基本上是没有的。有一个单独的指导讲了它的基本原理另外还有一个使用doxygen生成的文档。这就是为什么当我决定研究 FFMPEG来弄清楚音视频应用程序是如何工作的过程中,我决定把这个过程用文档的形式记录并且发布出来作为初学指导的原因。

在FFMPEG工程中有一个示例的程序叫作ffplay。它是一个用C编写的利用ffmpeg来实现完整视频播放的简单播放器。这个指导将从原来Martin Bohme写的一个更新版本的指导开始(我借鉴了一些),基于Fabrice Bellard的ffplay,我将从那里开发一个可以使用的视频播放器。在每一个指导中,我将介绍一个或者两个新的思想并且讲解我们如何来实现它。每一个指导都会有一个C源文件,你可以下载,编译并沿着这条思路来自己做。源文件将向你展示一个真正的程序是如何运行,我们如何来调用所有的部件,也将告诉你在这个指导中技术实现的细节并不重要。当我们结束这个指导的时候,我们将有一个少于1000行代码的可以工作的视频播放器。

在写播放器的过程中,我们将使用SDL来输出音频和视频。SDL是一个优秀的跨平台的多媒体库,被用在MPEG播放、模拟器和很多视频游戏中。你将需要下载并安装SDL开发库到你的系统中,以便于编译这个指导中的程序。

这篇指导适用于具有相当编程背景的人。至少至少应该懂得C并且有队列和互斥量等概念。你应当了解基本的多媒体中的像波形一类的概念,但是你不必知道的太多,因为我将在这篇指导中介绍很多这样的概念。

更新:我修正了在指导7和8中的一些代码错误,也添加-lavutil参数。欢迎给我发邮件到dranger@https://www.sodocs.net/doc/1c9937687.html,,讨论关于程序问题、疑问、注释、思路、特性等任何的问题

指导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,你必需先初始化这个库。(注意在某些系统中必需用来替换)

#include

#include

...

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; inb_streams; i++)

if(pFormatCtx->streams->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

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文件。可以使用一些电影文件来进行测试

如何用FFmpeg编写一个简单播放器详细步骤介绍(3)

FFmpeg, 播放器, 编写

上来得到这个库的源代码或者如果有可能的话你可以直接下载开发包到你的操作系统中。按照这个指导,你将需要编译这个库。(剩下的几个指导中也是一样)SDL库中有许多种方式来在屏幕上绘制图形,而且它有一个特殊的方式来在屏幕上显示图像――这种方式叫做YUV覆盖。YUV(从技术上来讲并不叫YUV而是叫做YCbCr)是一种类似于RGB方式的存储原始图像的格式。粗略的讲,Y是亮度分量,U和V是色度分量。(这种格式比RGB复杂的多,因为很多的颜色信息被丢弃了,而且你可以每 2个Y有1个U和1个V)。SDL的YUV覆盖使用一组原始的YUV数据并且在屏幕上显示出他们。它可以允许4种不同的 YUV格式,但是其中的YV12是最快的一种。还有一个叫做YUV420P的YUV格式,它和YV12

是一样的,除了U和V分量的位置被调换了以外。 420意味着它以4:2:0的比例进行了二次抽样,基本上就意味着1个颜色分量对应着4个亮度分量。所以它的色度信息只有原来的1/4。这是一种节省带宽的好方式,因为人眼感觉不到这种变化。在名称中的P表示这种格式是平面的――简单的说就是Y,U和V 分量分别在不同的数组中。FFMPEG可以把图像格式转换为YUV420P,但是现在很多视频流的格式已经是YUV420P的了或者可以被很容易的转换成YUV420P格式。

于是,我们现在计划把指导1中的SaveFrame()函数替换掉,让它直接输出我们的帧到屏幕上去。但一开始我们必需要先看一下如何使用SDL库。首先我们必需先包含SDL库的头文件并且初始化它。

#include #include if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); exit(1);}

SDL_Init()函数告诉了SDL库,哪些特性我们将要用到。当然SDL_GetError()是一个用来手工除错的函数。

创建一个显示

现在我们需要在屏幕上的一个地方放上一些东西。在SDL中显示图像的基本区域叫做面surface。

SDL_Surface *screen; screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);if(!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); exit(1);}

这就创建了一个给定高度和宽度的屏幕。下一个选项是屏幕的颜色深度――0表示使用和当前一样的深度。(这个在OS X系统上不能正常工作,原因请看源代码)

现在我们在屏幕上来创建一个YUV覆盖以便于我们输入视频上去:

SDL_Overlay *bmp; bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);

正如前面我们所说的,我们使用YV12来显示图像。

显示图像

前面那些都是很简单的。现在我们需要来显示图像。让我们看一下是如何来处理完成后的帧的。我们将原来对RGB处理的方式,并且替换SaveFrame() 为显示到屏幕上的代码。为了显示到屏幕上,我们将先建立一个AVPicture结构体并且设置其数据指针和行尺寸来为我们的YUV覆盖服务:

if(frameFinished) { SDL_LockYUVOverlay(bmp); AVPicture

pict; pict.data[0] = bmp->pixels[0]; pict.data[1] =

bmp->pixels[2]; pict.data[2] =

bmp->pixels[1]; pict.linesize[0] =

bmp->pitches[0]; pict.linesize[1] =

bmp->pitches[2]; pict.linesize[2] = bmp->pitches[1]; // Convert the image into YUV format that SDL uses img_convert(&pict,

PIX_FMT_YUV420P, (AVPicture *)pFrame,

pCodecCtx->pix_fmt, pCodecCtx->width,

pCodecCtx->height); SDL_UnlockYUVOverlay(bmp); }

首先,我们锁定这个覆盖,因为我们将要去改写它。这是一个避免以后发生问题的好习惯。正如前面所示的,这个AVPicture结构体有一个数据指针指向一个有4个元素的指针数据。由于我们处理的是YUV420P,所以我们只需要3个通道即只要三组数据。其它的格式可能需要第四个指针来表示alpha通道或者其它参数。行尺寸正如它的名字表示的意义一样。在YUV覆盖中相同功能的结构体是像素pixel和程度pitch。(程度pitch是在SDL里用来表示指定行数据宽度的值)。所以我们现在做的是让我们的覆盖中的pict.data中的三个指针有一个指向必要的空间的地址。类似的,我们可以直接从覆盖中得到行尺寸信息。像前面一样我们使用img_convert来把格式转换成PIX_FMT_YUV420P。

绘制图像

但我们仍然需要告诉SDL如何来实际显示我们给的数据。我们也会传递一个表明电影位置、宽度、高度和缩放大小的矩形参数给SDL的函数。这样,SDL为我们做缩放并且它可以通过显卡的帮忙来进行快速缩放。

SDL_Rect rect; if(frameFinished) { // Convert the image into YUV

format that SDL uses img_convert(&pict,

PIX_FMT_YUV420P, (AVPicture *)pFrame,

pCodecCtx->pix_fmt, pCodecCtx->width,

pCodecCtx->height); SDL_UnlockYUVOverlay(bmp); rect.x =

0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp, &rect); }

现在我们的视频显示出来了!

让我们再花一点时间来看一下SDL的特性:它的事件驱动系统。SDL被设置成当你在SDL中点击或者移动鼠标或者向它发送一个信号它都将产生一个事件的驱动方式。如果你的程序想要处理用户输入的话,它就会检测这些事件。你的程序也可以产生事件并且传递给SDL事件系统。当使用SDL进行多线程编程的时候,这相当有用,这方面代码我们可以在指导4中看到。在这个程序中,我们将在处理完包以后就立即轮询事件。现在而言,我们将处理SDL_QUIT事件以便于我们退出:

SDL_Event event; av_free_packet(&packet); SDL_PollEvent (&event); switch(event.type) { case

SDL_QUIT: SDL_Quit(); exit(0); break; default: break; }

让我们去掉旧的冗余代码,开始编译。如果你使用的是Linux或者其变体,使用SDL库进行编译的最好方式为:

gcc -o tutorial02 tutorial02.c -lavutil -lavformat -lavcodec -lz -lm \`sdl-config --cflags --libs`

这里的sdl-config命令会打印出用于gcc编译的包含正确SDL库的适当参数。为了进行编译,在你自己的平台你可能需要做的有点不同:请查阅一下 SDL文档中关于你的系统的那部分。一旦可以编译,就马上运行它。

当运行这个程序的时候会发生什么呢?电影简直跑疯了!实际上,我们只是以我们能从文件中解码帧的最快速度显示了所有的电影的帧。现在我们没有任何代码来计算出我们什么时候需要显示电影的帧。最后(在指导5),我们将花足够的时间来探讨同步问题。但一开始我们会先忽略这个,因为我们有更加重要的事情要处理:音频!

指导3:播放声音

现在我们要来播放声音。SDL也为我们准备了输出声音的方法。函数

SDL_OpenAudio()本身就是用来打开声音设备的。它使用一个叫做

SDL_AudioSpec结构体作为参数,这个结构体中包含了我们将要输出的音频的所有信息。

在我们展示如何建立之前,让我们先解释一下电脑是如何处理音频的。数字音频是由一长串的样本流组成的。每个样本表示声音波形中的一个值。声音按照一个特定的采样率来进行录制,采样率表示以多快的速度来播放这段样本流,它的表示方式为每秒多少次采样。例如22050和44100的采样率就是电台和CD常用的采样率。此外,大多音频有不只一个通道来表示立体声或者环绕。例如,如果采样是立体声,那么每次的采样数就为2个。当我们从一个电影文件中等到数据的时候,我们不知道我们将得到多少个样本,但是ffmpeg将不会给我们部分的样本――这意味着它将不会把立体声分割开来。

SDL播放声音的方式是这样的:你先设置声音的选项:采样率(在SDL的结构体中被叫做freq的表示频率frequency),声音通道数和其它的参数,然后我们设置一个回调函数和一些用户数据userdata。当开始播放音频的时候,SDL将不断地调用这个回调函数并且要求它来向声音缓冲填入一个特定的数量的字节。当我们把这些信息放到SDL_AudioSpec结构体中后,我们调用函数

SDL_OpenAudio()就会打开声音设备并且给我们送回另外一个AudioSpec结构体。这个结构体是我们实际上用到的--因为我们不能保证得到我们所要求的。

设置音频

目前先把讲的记住,因为我们实际上还没有任何关于声音流的信息。让我们回过头来看一下我们的代码,看我们是如何找到视频流的,同样我们也可以找到声音流。

// Find the first video stream

videoStream=-1;

audioStream=-1;

for(i=0; i < pFormatCtx->nb_streams; i++) {

if(pFormatCtx->streams->codec->codec_type==CODEC_TYPE_VIDEO

&&

videoStream < 0) {

videoStream=i;

}

if(pFormatCtx->streams->codec->codec_type==CODEC_TYPE_AUDIO &&

audioStream < 0) {

audioStream=i;

}

}

if(videoStream==-1)

return -1; // Didn't find a video stream

if(audioStream==-1)

return -1;

从这里我们可以从描述流的AVCodecContext中得到我们想要的信息,就像我们得到视频流的信息一样。

AVCodecContext *aCodecCtx;

aCodecCtx=pFormatCtx->streams[audioStream]->codec;

包含在编解码上下文中的所有信息正是我们所需要的用来建立音频的信息:

wanted_spec.freq = aCodecCtx->sample_rate;

wanted_spec.format = AUDIO_S16SYS;

wanted_spec.channels = aCodecCtx->channels;

wanted_spec.silence = 0;

wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;

wanted_spec.callback = audio_callback;

wanted_https://www.sodocs.net/doc/1c9937687.html,erdata = aCodecCtx;

if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {

fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());

return -1;

}

让我们浏览一下这些:

·freq 前面所讲的采样率

·format 告诉SDL我们将要给的格式。在“S16SYS”中的S表示有符号的signed,16表示每个样本是16位长的,SYS表示大小头的顺序是与使用的系统相同的。这些格式是由 avcodec_decode_audio2为我们给出来的输入音频的格式。·channels 声音的通道数

·silence 这是用来表示静音的值。因为声音采样是有符号的,所以0当然就是这个值。

·samples 这是当我们想要更多声音的时候,我们想让SDL给出来的声音缓冲区的尺寸。一个比较合适的值在512到8192之间;ffplay使用1024。·callback 这个是我们的回调函数。我们后面将会详细讨论。

·userdata 这个是SDL供给回调函数运行的参数。我们将让回调函数得到整个编解码的上下文;你将在后面知道原因。

最后,我们使用SDL_OpenAudio函数来打开声音。

如果你还记得前面的指导,我们仍然需要打开声音编解码器本身。这是很显然的。

AVCodec *aCodec;

aCodec = avcodec_find_decoder(aCodecCtx->codec_id);

if(!aCodec) {

fprintf(stderr, "Unsupported codec!\n");

return -1;

}

avcodec_open(aCodecCtx, aCodec);

队列

嗯!现在我们已经准备好从流中取出声音信息。但是我们如何来处理这些信息呢?我们将会不断地从文件中得到这些包,但同时SDL也将调用回调函数。解决方法为创建一个全局的结构体变量以便于我们从文件中得到的声音包有地方存放同时也保证SDL中的声音回调函数audio_callback能从这个地方得到声音数据。所以我们要做的是创建一个包的队列queue。在ffmpeg中有一个叫AVPacketList的结构体可以帮助我们,这个结构体实际是一串包的链表。下面就是我们的队列结构体:

typedef struct PacketQueue {

AVPacketList *first_pkt, *last_pkt;

int nb_packets;

int size;

SDL_mutex *mutex;

SDL_cond *cond;

} PacketQueue;

首先,我们应当指出nb_packets是与size不一样的--size表示我们从packet->size中得到的字节数。你会注意到我们有一个互斥量mutex和一个条件变量cond在结构体里面。这是因为SDL是在一个独立的线程中来进行音频处理的。如果我们没有正确的锁定这个队列,我们有可能把数据搞乱。我们将来看一个这个队列是如何来运行的。每一个程序员应当知道如何来生成的一个队列,但是我们将把这部分也来讨论从而可以学习到SDL的函数。

一开始我们先创建一个函数来初始化队列:

void packet_queue_init(PacketQueue *q) {

memset(q, 0, sizeof(PacketQueue));

q->mutex = SDL_CreateMutex();

q->cond = SDL_CreateCond();

}

接着我们再做一个函数来给队列中填入东西:

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

AVPacketList *pkt1;

if(av_dup_packet(pkt) < 0) {

return -1;

}

pkt1 = av_malloc(sizeof(AVPacketList));

if (!pkt1)

return -1;

pkt1->pkt = *pkt;

pkt1->next = NULL;

SDL_LockMutex(q->mutex);

if (!q->last_pkt)

q->first_pkt = pkt1;

else

q->last_pkt->next = pkt1;

q->last_pkt = pkt1;

q->nb_packets++;

q->size += pkt1->pkt.size;

SDL_CondSignal(q->cond);

SDL_UnlockMutex(q->mutex);

return 0;

}

函数SDL_LockMutex()锁定队列的互斥量以便于我们向队列中添加东西,然后函数SDL_CondSignal()通过我们的条件变量为一个接收函数(如果它在等待)发出一个信号来告诉它现在已经有数据了,接着就会解锁互斥量并让队列可以自由访问。

下面是相应的接收函数。注意函数SDL_CondWait()是如何按照我们的要求让函数阻塞block的(例如一直等到队列中有数据)。

int quit = 0;

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1;

int ret;

SDL_LockMutex(q->mutex);

for(;;) {

if(quit) {

ret = -1;

break;

}

pkt1 = q->first_pkt;

if (pkt1) {

q->first_pkt = pkt1->next;

if (!q->first_pkt)

q->last_pkt = NULL;

q->nb_packets--;

q->size -= pkt1->pkt.size;

*pkt = pkt1->pkt;

av_free(pkt1);

ret = 1;

break;

} else if (!block) {

ret = 0;

break;

} else {

SDL_CondWait(q->cond, q->mutex);

}

}

SDL_UnlockMutex(q->mutex);

return ret;

}

正如你所看到的,我们已经用一个无限循环包装了这个函数以便于我们想用阻塞的方式来得到数据。我们通过使用SDL中的函数SDL_CondWait()来避免无限循环。基本上,所有的CondWait只等待从SDL_CondSignal()函数(或者

SDL_CondBroadcast()函数)中发出的信号,然后再继续执行。然而,虽然看起来我们陷入了我们的互斥体中--如果我们一直保持着这个锁,我们的函数将永远无法把数据放入到队列中去!但是,SDL_CondWait()函数也为我们做了解锁互斥量的动作然后才尝试着在得到信号后去重新锁定它。

意外情况

你们将会注意到我们有一个全局变量quit,我们用它来保证还没有设置程序退出的信号(SDL会自动处理TERM类似的信号)。否则,这个线程将不停地运行直到我们使用kill -9来结束程序。FFMPEG同样也提供了一个函数来进行回调并检查我们是否需要退出一些被阻塞的函数:这个函数就是

url_set_interrupt_cb。

int decode_interrupt_cb(void) {

return quit;

}

...

main() {

...

url_set_interrupt_cb(decode_interrupt_cb);

...

SDL_PollEvent(&event);

switch(event.type) {

case SDL_QUIT:

quit = 1;

...

当然,这仅仅是用来给ffmpeg中的阻塞情况使用的,而不是SDL中的。我们还必需要设置quit标志为1。

为队列提供包

剩下的我们唯一需要为队列所做的事就是提供包了:

PacketQueue audioq;

main() {

...

avcodec_open(aCodecCtx, aCodec);

packet_queue_init(&audioq);

SDL_PauseAudio(0);

函数SDL_PauseAudio()让音频设备最终开始工作。如果没有立即供给足够的数据,它会播放静音。

我们已经建立好我们的队列,现在我们准备为它提供包。先看一下我们的读取包的循环:

while(av_read_frame(pFormatCtx, &packet)>=0) {

// Is this a packet from the video stream?

if(packet.stream_index==videoStream) {

// Decode video frame

....

}

} else if(packet.stream_index==audioStream) {

packet_queue_put(&audioq, &packet);

} else {

av_free_packet(&packet);

}

注意:我们没有在把包放到队列里的时候释放它,我们将在解码后来释放它。取出包

现在,让我们最后让声音回调函数audio_callback来从队列中取出包。回调函数的格式必需为void callback(void *userdata, Uint8 *stream, int len),这里的userdata就是我们给到SDL的指针,stream是我们要把声音数据写入的缓冲区指针,len是缓冲区的大小。下面就是代码:

void audio_callback(void *userdata, Uint8 *stream, int len) {

AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;

int len1, audio_size;

static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; static unsigned int audio_buf_size = 0;

static unsigned int audio_buf_index = 0;

while(len > 0) {

if(audio_buf_index >= audio_buf_size) {

audio_size = audio_decode_frame(aCodecCtx, audio_buf,

sizeof(audio_buf));

if(audio_size < 0) {

audio_buf_size = 1024;

memset(audio_buf, 0, audio_buf_size);

} else {

audio_buf_size = audio_size;

}

audio_buf_index = 0;

}

len1 = audio_buf_size - audio_buf_index;

if(len1 > len)

len1 = len;

memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);

len -= len1;

stream += len1;

audio_buf_index += len1;

}

}

这基本上是一个简单的从另外一个我们将要写的audio_decode_frame()函数中

获取数据的循环,这个循环把结果写入到中间缓冲区,尝试着向流中写入len 字节并且在我们没有足够的数据的时候会获取更多的数据或者当我们有多余数据的时候保存下来为后面使用。这个audio_buf的大小为 1.5倍的声音帧的大小以便于有一个比较好的缓冲,这个声音帧的大小是ffmpeg给出的。

最后解码音频

让我们看一下解码器的真正部分:audio_decode_frame

int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {

static AVPacket pkt;

static uint8_t *audio_pkt_data = NULL;

static int audio_pkt_size = 0;

int len1, data_size;

for(;;) {

while(audio_pkt_size > 0) {

data_size = buf_size;

len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size,

audio_pkt_data, audio_pkt_size);

if(len1 < 0) {

audio_pkt_size = 0;

break;

}

audio_pkt_data += len1;

audio_pkt_size -= len1;

if(data_size <= 0) {

continue;

}

return data_size;

}

if(pkt.data)

av_free_packet(&pkt);

if(quit) {

return -1;

}

if(packet_queue_get(&audioq, &pkt, 1) < 0) {

return -1;

}

audio_pkt_data = pkt.data;

audio_pkt_size = pkt.size;

}

}

整个过程实际上从函数的尾部开始,在这里我们调用了packet_queue_get()函数。我们从队列中取出包,并且保存它的信息。然后,一旦我们有了可以使用的包,我们就调用函数avcodec_decode_audio2(),它的功能就像它的姐妹函数avcodec_decode_video()一样,唯一的区别是它的一个包里可能有不止一个声音帧,所以你可能要调用很多次来解码出包中所有的数据。同时也要记住进行指针audio_buf的强制转换,因为SDL给出的是8位整型缓冲指针而ffmpeg给出的数据是16位的整型指针。你应该也会注意到 len1和data_size的不同,len1表示解码使用的数据的在包中的大小,data_size表示实际返回的原始声音数据的大小。

当我们得到一些数据的时候,我们立刻返回来看一下是否仍然需要从队列中得到更加多的数据或者我们已经完成了。如果我们仍然有更加多的数据要处理,我们把它保存到下一次。如果我们完成了一个包的处理,我们最后要释放它。

就是这样。我们利用主的读取队列循环从文件得到音频并送到队列中,然后被audio_callback函数从队列中读取并处理,最后把数据送给SDL,于是SDL就相当于我们的声卡。让我们继续并且编译:

gcc -o tutorial03 tutorial03.c -lavutil -lavformat -lavcodec -lz -lm \ `sdl-config --cflags --libs`

啊哈!视频虽然还是像原来那样快,但是声音可以正常播放了。这是为什么呢?因为声音信息中的采样率--虽然我们把声音数据尽可能快的填充到声卡缓冲中,但是声音设备却会按照原来指定的采样率来进行播放。

我们几乎已经准备好来开始同步音频和视频了,但是首先我们需要的是一点程序的组织。用队列的方式来组织和播放音频在一个独立的线程中工作的很好:它使得程序更加更加易于控制和模块化。在我们开始同步音视频之前,我们需要让我们的代码更加容易处理。所以下次要讲的是:创建一个线程。

如何用FFmpeg编写一个简单播放器详细步骤介绍(5)

FFmpeg, 播放器, 编写

} else {

break;

}

}

// Is this a packet from the video stream?

if(packet->stream_index == is->videoStream) {

packet_queue_put(&is->videoq, packet);

} else if(packet->stream_index == is->audioStream) {

packet_queue_put(&is->audioq, packet);

} else {

av_free_packet(packet);

}

}

这里没有什么新东西,除了我们给音频和视频队列限定了一个最大值并且我们添加一个检测读错误的函数。格式上下文里面有一个叫做pb的 ByteIOContext 类型结构体。这个结构体是用来保存一些低级的文件信息。函数url_ferror用来检测结构体并发现是否有些读取文件错误。

在循环以后,我们的代码是用等待其余的程序结束和提示我们已经结束的。这些代码是有益的,因为它指示出了如何驱动事件--后面我们将显示影像。

while(!is->quit) {

SDL_Delay(100);

}

fail:

if(1){

SDL_Event event;

event.type = FF_QUIT_EVENT;

https://www.sodocs.net/doc/1c9937687.html,er.data1 = is;

SDL_PushEvent(&event);

}

return 0;

我们使用SDL常量SDL_USEREVENT来从用户事件中得到值。第一个用户事件的值应当是SDL_USEREVENT,下一个是 SDL_USEREVENT+1并且依此类推。在我们的程序中FF_QUIT_EVENT被定义成SDL_USEREVENT+2。如果喜欢,我们也可以传递用户数据,在这里我们传递的是大结构体的指针。最后我们调用

SDL_PushEvent()函数。在我们的事件分支中,我们只是像以前放入

SDL_QUIT_EVENT部分一样。我们将在自己的事件队列中详细讨论,现在只是确保我们正确放入了FF_QUIT_EVENT事件,我们将在后面捕捉到它并且设置我们的退出标志quit。

得到帧:video_thread

ffmpeg开发指南(使用 libavformat 和 libavcodec)

ffmpeg开发指南(使用libavformat 和libavcodec) ffmpeg开发指南(使用libavformat 和libavcodec) ffmpeg开发指南(使用libavformat 和libavcodec)Ffmpeg 中的Libavformat 和libavcodec库是访问大多数视频文件格式的一个很好的方法。不幸的是,在开发您自己的程序时,这套库基本上没有提供什么实际的文档可以用来作为参考(至少我没有找到任何文档),并且它的例程也并没有太多的帮助。这种情况意味着,当我在最近某个项目中需要用到libavformat/libavcodec 库时,需要作很多试验来搞清楚怎样使用它们。这里是我所学习的--希望我做的这些能够帮助一些人,以免他们重蹈我的覆辙,作同样的试验,遇到同样的错误。你还可以从这里下载一个demo程序。我将要公开的这部分代码需要0.4.8 版本的ffmpeg库中的 libavformat/libavcodec 的支持(我正在写最新版本)。如果您发现以后的版本与我写的程序不能兼容,请告知我。在这个文档里,我仅仅涉及到如何从文件中读入视频流;音频流使用几乎同样的方法可以工作的很好,不过,我并没有实际使用过它们,所以,我没于办法提供任何示例代码。或许您会觉得奇怪,为什么需要两个库文件libavformat 和libavcodec :许多视频文件格式(AVI就是一个最好的例子)

实际上并没有明确指出应该使用哪种编码来解析音频和视 频数据;它们只是定义了音频流和视频流(或者,有可能是多个音频视频流)如何被绑定在一个文件里面。这就是为什么有时候,当你打开了一个AVI文件时,你只能听到声音,却不能看到图象--因为你的系统没有安装合适的视频解 码器。所以,libavformat 用来处理解析视频文件并将包含在其中的流分离出来,而libavcodec 则处理原始音频和视频流的解码。打开视频文件: 首先第一件事情--让我们来看看怎样打开一个视频文件 并从中得到流。我们要做的第一件事情就是初始化libavformat/libavcodec:av_register_all(); 这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么做却没有什么原因。下一步,打开文件: AVFormatContext *pFormatCtx;

ffmpeg编译、使用与常见问题

FFMPEG编译、使用与常见问题 一. Linux下FFMPEG的安装与测试 (1) 二. FFMPEG编译中出现的一些问题与解决方法 (4) 三. FFMpeg简介及命令选项参数 (8) 四. FFMPEG与x264的编译 (13) 一.Linux下FFMPEG的安装与测试 a. 先装mp3在linux下的包:lame-3.97.tar.gz; tar -xvzf lame-3.97.tar.gz; cd lame-3.97; ./configure --enable-shared --prefix=/usr/; make; make install; b. 支持Ogg Vorbis: as4自带相应的rpm包,你可以安装一下如下rpm包: libvorbis, libvorbis-devel,libogg, libogg-devel 一般情况下as4都会安装 c. 支持xvid x264,现在最流行的两种高质量的压缩格式 xvid的编译安装 wget https://www.sodocs.net/doc/1c9937687.html,/downloads/xvidcore-1.1.0.tar.gz tar zvxf xvidcore-1.1.0.tar.gz cd xvidcore-1.1.2/build/generic ./configure --prefix=/usr --enable-shared make make install x264的获取用git: git clone git://https://www.sodocs.net/doc/1c9937687.html,/x264.git cd x264 ./configure --prefix=/usr --enable-shared make make install3 d. AC3和dts编码的支持

如何用FFmpeg编写一个简单播放器详细步骤介绍

如何用FFmpeg编写一个简单播放器详细步骤介绍(转载) FFmpeg, 播放器, 编写 FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具。FFMPEG 几乎为你把所有的繁重工作都做了,比如解码、编码、复用和解复用。这使得多媒体应用程序变得容易编写。它是一个简单的,用C编写的,快速的并且能够解码几乎所有你能用到的格式,当然也包括编码多种格式。 唯一的问题是它的文档基本上是没有的。有一个单独的指导讲了它的基本原理另外还有一个使用doxygen生成的文档。这就是为什么当我决定研究 FFMPEG来弄清楚音视频应用程序是如何工作的过程中,我决定把这个过程用文档的形式记录并且发布出来作为初学指导的原因。 在FFMPEG工程中有一个示例的程序叫作ffplay。它是一个用C编写的利用ffmpeg来实现完整视频播放的简单播放器。这个指导将从原来Martin Bohme写的一个更新版本的指导开始(我借鉴了一些),基于Fabrice Bellard的ffplay,我将从那里开发一个可以使用的视频播放器。在每一个指导中,我将介绍一个或者两个新的思想并且讲解我们如何来实现它。每一个指导都会有一个C源文件,你可以下载,编译并沿着这条思路来自己做。源文件将向你展示一个真正的程序是如何运行,我们如何来调用所有的部件,也将告诉你在这个指导中技术实现的细节并不重要。当我们结束这个指导的时候,我们将有一个少于1000行代码的可以工作的视频播放器。 在写播放器的过程中,我们将使用SDL来输出音频和视频。SDL是一个优秀的跨平台的多媒体库,被用在MPEG播放、模拟器和很多视频游戏中。你将需要下载并安装SDL开发库到你的系统中,以便于编译这个指导中的程序。 这篇指导适用于具有相当编程背景的人。至少至少应该懂得C并且有队列和互斥量等概念。你应当了解基本的多媒体中的像波形一类的概念,但是你不必知道的太多,因为我将在这篇指导中介绍很多这样的概念。 更新:我修正了在指导7和8中的一些代码错误,也添加-lavutil参数。欢迎给我发邮件到dranger@https://www.sodocs.net/doc/1c9937687.html,,讨论关于程序问题、疑问、注释、思路、特性等任何的问题 指导1:制作屏幕录像 源代码:tutorial01.c

ffmpeg之参数详解29

本文讲述参数详解 1. ffmpeg.exe -i F:\闪客之家\闪客之 歌.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\11.flv ffmpe g -i F:\01.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\tes t.flv 使用-ss参数作用(time_off set the start time offset),可以从指定时间点开始转换任务。如: 转换文件格式的同时抓缩微图: ffmpeg -i "test.avi " -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg ' 对已有flv抓图: ffmpeg -i "test.flv " -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg ' -ss后跟的时间单位为秒Ffmpeg转换命令 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 17 6x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或者 ffmpeg -y -i test.mpeg -ac 1 -acodec amr_nb -ar 8000 -s 176x144 -b 128 -r 15 test.3gp ffmpeg参数设定解说 -bitexact 使用标准比特率 -vcodec xvid 使用xvid压缩 -s 320x240 指定分辨率 -r 29.97 桢速率(可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97) 画面部分,选其一 -b <比特率> 指定压缩比特率,似乎ffmpeg是自动VBR的,指定了就大概是平均比特率,比如768,1500这样的就是原来默认项目中有的 -qscale <数值> 以<数值> 质量为基础的VBR,取值0.01-255,约小质量越好 -qmin <数值> 设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31 -sameq 使用和源同样的质量声音部分 -acodec aac 设定声音编码 -ac <数值> 设定声道数,1就是单声道,2就是立体声,转换单声道的TVrip可以用1(节省一半容量),高品质的DVDrip就可以用2 -ar <采样率> 设定声音采样率,PSP只认24000 -ab <比特率> 设定声音比特率,前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换君默认比特率都较小,要听到较高品质声音的话建议设到160kbps(80)以上 -vol <百分比> 设定音量,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍这样,要得到一个高画质音质低容量的MP4的话,首先画面最好不要用固定比特率,而用VBR参数让程序自己去判断,而音质参数可以在原来的基础上提升一点,听起来要舒服很多,也不会太大(看情况调整例子:ffmpeg -y -i "1.avi " -title "Test

视频解码中用到的ffmpeg交叉编译技巧

视频解码中用到的ffmpeg交叉编译技巧 一、简介 FFmpeg是一个集录制,转换,音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。 由于ffmpeg是基于linux开发的,源代码和windows下最常见的visual studio提供的c/c++编译器不兼容,因此不能直接使用msvc++编译,想要使用ffmpeg,需要在windows下配置一个类似Linux 的编译环境,并将ffmpeg编译为二进制库以后,再利用其进行进一步开发。 目前,windows下可直接使用的最新版本是由ffmpeg工程组,于2008年4月份发布的ffmpeg full sdk v3.2。该sdk尽可能的集成了更多的编解码器。 在应用中,使用该开发包存在2个缺陷,第一,该sdk编译基于ffmpeg r12790,编解码效率上,当前已经有了较大的提升;第二,该开发包包含了很多不需要的编解码器,需要尽可能减少开发包的体积及关联的库的个数。基于以上需求,需要自己编译出windows下可用动态库及静态库。 使用到的库文件有4个,libavcodec,libavformat,libavutil,libswscale,其中库 libavcodec,libavformat用于对媒体文件进行处理,如编解码;libavutil是一个通用的小型函数库,该库中实现了CRC校验码的产生,128位整数数学,最大公约数,整数开方,整数取对数,内存分配,大端小端格式的转换等功能,libswscale主要用于图像格式转换及缩放。因效率的需要,我们选择了 ffmpeg-mt版本,同时通过配置编译尽量减少相关文件的体积。 网上类似编译过程说明较多,但实际编译过程中碰到一些问题,这些问题的解决花费了不少时间,本文档对这一过程进行记录,以便后续维护及学习。 二、编译环境搭建 windows下ffmpeg编译环境有多种可选择方案。这里我们使用MinGW+MSYS。 1.下载mingw+msys并安装 https://www.sodocs.net/doc/1c9937687.html,/projects/mingw/ mingw,默认安装,在choose components时选择g++ compiler及MinGW Make Msys,默认安装, Do you wish to continue with the post install【yn】,y! Do you have mingw installed?【yn】,y Where is your mingw installation? C:/mingw

用ffmpeg转换视频格式

用?mpeg转换视频格式deanraccoon@https://www.sodocs.net/doc/1c9937687.html, 2011年5月9日 目录

1实用?fmpeg命令 转自 https://www.sodocs.net/doc/1c9937687.html,/blog/19-ffmpeg-commands-for-all-needs 转自 https://www.sodocs.net/doc/1c9937687.html,/spip.php?article36 1.1查看视频文件信息 ffmpeg-i video.avi 1.2N个图片转化视频 ffmpeg-f image2-i image%d.jpg video.mpg 这个命令把当前文件夹下所有图片转成视频文件 1.3视频转化图片 ffmpeg video.mpg image%d.jpg 图片格式包括:PGM,PPM,PAM,PGMYUV,JPEG,GIF,PNG,TIFF,SGI 1.4视频转为iphone可播放的格式 ffmpeg source_video.avi input-acodec aac-ab128kb-vcodec mpeg4-b1200kb-mbd2-flags +4mv+trell-aic2-cmp2-subcmp2-s320x180-title X final_video.mp4 参数含义: ?源文件:source video.avi ?音频编码:aac ?音频的比特率:128kb/s ?视频编码:mpeg4 ?视频比特率:1200kb/s ?视频大小:320px par180px ?结果文件:?nal video.mp4 1.5视频转换成psp格式 ffmpeg-i source_video.avi-b300-s320x240-vcodec xvid-ab32-ar24000-acodec aac final_video.mp4参数含义: ?源文件:source video.avi

ffmpeg用法

ffmpeg.exe -i F:\闪客之家\闪客之歌.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\11.flv ffmpeg -i F:\01.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv 使用-ss参数作用(time_off set the start time offset),可以从指定时间点开始转换任务。如: 转换文件格式的同时抓缩微图: ffmpeg -i "test.avi " -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg ' 对已有flv抓图: ffmpeg -i "test.flv " -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg ' -ss后跟的时间单位为秒Ffmpeg转换命令 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x1 44 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或者 ffmpeg -y -i test.mpeg -ac 1 -acodec amr_nb -ar 8000 -s 176x144 -b 128 -r 15 test.3gp ffmpeg参数设定解说 -bitexact 使用标准比特率 -vcodec xvid 使用xvid压缩 -s 320x240 指定分辨率 -r 29.97 桢速率(可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97) 画面部分,选其一 -b <比特率> 指定压缩比特率,似乎ffmpeg是自动VBR的,指定了就大概是平均比特率,比如768,1500这样的就是原来默认项目中有的 -qscale <数值> 以<数值> 质量为基础的VBR,取值0.01-255,约小质量越好 -qmin <数值> 设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31 -sameq 使用和源同样的质量声音部分 -acodec aac 设定声音编码 -ac <数值> 设定声道数,1就是单声道,2就是立体声,转换单声道的TV rip可以用1(节省一半容量),高品质的DVDrip就可以用2 -ar <采样率> 设定声音采样率,PSP只认24000 -ab <比特率> 设定声音比特率,前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换君默认比特率都较小,要听到较高品质声音的话建议设到160kbps (80)以上 -vol <百分比> 设定音量,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍这样,要得到一个高画质音质低容量的MP4的话,首先画面最好不要用固定比特率,而用VBR参数让程序自己去判断,而音质参数可以在原来的基础上提升一点,听起来要舒服很多,也不会太大(看情况调整例子:ffmpeg -y -i "1.avi " -title "Test " -vcodec xvid -s 368x208 -r 29.97 -b 1500 -acodec aac -ac 2 -ar 24000 -ab 128 -vol 200 -f psp -muxvb 768 "1.*** "

ffmpeg与sdl

ffmpeg与sdl 电影文件有很多基本的组成部分。首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接着,你有一组流,例如,你经常有的是一个音频流和一个视频流。(一个流只是一种想像出来的词语,用来表示一连串的通过时间来串连的数据元素)。在流中的数据元素被称为帧Frame。每个流是由不同的编码器来编码生成的。编解码器描述了实际的数据是如何被编码Coded和解码DECoded的,因此它的名字叫做CODEC。Divx和MP3就是编解码器的例子。接着从流中被读出来的叫做包Packets。包是一段数据,它包含了一段可以被解码成方便我们最后在应用程序中操作的原始帧的数据。根据我们的目的,每个包包含了完整的帧或者对于音频来说是许多格式 的完整帧。 基本上来说,处理视频和音频流是很容易的: 在这个程序中使用ffmpeg来处理多种媒体是相当容易的,虽然很多程序可能在对帧进行操作的时候非常的复杂。因此在这篇指导中,我们将打开一个文件,读取里面的视频流,而且我们对帧的操作将是把这个帧 写到一个PPM文件中。 打开文件 首先,来看一下我们如何打开一个文件。通过ffmpeg,你必需先初始化这个库。(注意在某些系统中必需用来替换)

这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要这样做。 现在我们可以真正的打开文件: 我们通过第一个参数来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatCont ext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NU LL或者0,libavformat将自动检测这些参数。 这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息: 这个函数为pFormatCtx->streams填充上正确的信息。我们引进一个手工调试的函数来看一下里面有什么:

基于Qt+ffmpeg的跨平台安卓实时投屏软件的开发与设计

一种高分辨率实时屏幕共享硬件加速系统 摘要:建立一个支持超高分辨率(如4k)的交互式屏幕共享系统具有挑战性,因为延迟和帧速率在用户体验中扮演着重要角色。屏幕帧需要有效地压缩,而不需要消耗大量的计算资源。提出了一种实时屏幕共享的硬件加速系统,该系统通过利用连续屏幕帧间的内容冗余来减少编码工作量。提出了一种采用不同输入大小的H.264高级视频编码(H.264/AVC)的多个编码器,通过为更新的屏幕内容选择合适的编码方式,节省了编码时间。提出了一种优化的元数据处理方法。帧内的小而远的更新可以分割成独立的帧,以便进行更有效的压缩,这也有利于交互延迟。在评估中,在一般的屏幕共享场景中,该系统比一般的单个编解码器的编码时间更短。对延迟的测量表明,4K分辨率屏幕共享的端到端延迟仅为17-25ms,这使得该系统适合于本地有线和无线连接中的各种应用。 关键词:4K分辨率,硬件-加速编码,低延迟屏幕共享

目录 第1章绪论 (1) 1.1研究背景和意义 (1) 1.1.1 系统的开发背景分析 (1) 1.1.2 本课题的研究意义 (1) 1.2 国内外的研究 (2) 1.2.1 国内研究 (2) 1.2.2 国外研究 (2) 1.3课题研究方法和内容 (2) 1.3.1 研究方法和关键技术 (2) 第2章开发工具及安装 (4) 2.1 Qt (4) 2.1.1 Qt简介 (4) 2.1.2 Qt的安装 (4) 2.2 Visual Studio 2017 (6) 2.2.1 Visual Studio 2017的安装和环境配置 (6) 第3章系统功能需求 (10) 3.1 系统总体功能需求 (10) 3.2 系统总体功能描述 (10) 3.2.1.实时显示Android设备屏幕 (10) 3.2.2实时键鼠控制Android设备 (11) 3.2.3屏幕录制 (11) 3.2.4无线连接 (11) 3.2.5全屏显示 (11) 3.2.6常用功能按键 (11) 3.2.7传输文件/apk (11) 3.2.8后台录制 (11) 3.2.9复制粘贴 (12) 3.2.10在计算机和设备之间双向同步剪贴板 (12) 3.3 用户子系统的功能需求 (12) 第4章系统设计 (13) 4.1 系统总体设计 (13) 第5章 UML建模 (14) 5.1 用例图 (14) 5.1.1 用户用例图 (14) 5.2 用例规约表 (14) 5.2.1 启动服务用例规约表 (14) 5.2.2 相关配置用例规约表 (15) 5.2.3 无线连接用例规约表 (16) 5.3 类图 (18) 5.3.1 系统实现类图 (18) 5.4 顺序图 (19)

ffmpeg主要数据结构和函数

FFMpeg 中比较重要的函数以及数据结构如下:1. 数据结构: (1) AVFormatContext (2) AVOutputFormat (3) AVInputFormat (4) AVCodecContext (5) AVCodec (6) AVFrame (7) AVPacket (8) AVPicture (9) AVStream 2. 初始化函数: (1) av_register_all() (2) avcodec_open() (3) avcodec_close() (4) av_open_input_file() (5) av_find_input_format() (6) av_find_stream_info() (7) av_close_input_file() 3. 音视频编解码函数: (1) avcodec_find_decoder() (2) avcodec_alloc_frame() (3) avpicture_get_size() (4) avpicture_fill() (5) img_convert() (6) avcodec_alloc_context() (7) avcodec_decode_video() (8) av_free_packet() (9) av_free() 4. 文件操作:

(1) avnew_steam() (2) av_read_frame() (3) av_write_frame() (4) dump_format() 5. 其他函数: (1) avpicture_deinterlace() (2) ImgReSampleContext() 以下就根据,以上数据结构及函数在ffmpeg测试代码output_example.c中出现的前后顺进行分析。 交待完毕进入正题。 一.FFMpeg 中的数据结构: I. AVFormatContext 一般在使用ffmpeg sdk的代码中AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。FFmpeg代码中对这个数据结构的注释是:format I/O context 此结构包含了一个视频流的格式内容。其中存有了AVInputFormat(or AVOutputFormat同一时间AVFormatContext内只能存在其中一个),和AVStream、AVPacket这几个重要的数据结构以及一些其他的相关信息,比如title,author,copyright 等。还有一些可能在编解码中会用到的信息,诸如:duration, file_size, bit_rate等。参考avformat.h头文件。 Useage: 声明: AVFormatContext *oc; (1) 初始化:由于AVFormatConext结构包含许多信息因此初始化过程是分步完成,而且有些变量如果没有值可用,也可不初始化。但是由于一般声明都是用指针因此一个分配内存过程不可少: oc = av_alloc_format_context(); (2) 结构中的AVInputFormat*(或AVOutputFormat*)是一定要初始化的,基本上这是编译码要使用什么codec的依据所在: oc->oformat = fmt; or oc->iformat = fmt; (3) 其中AVOutputFormat* fmt或AVInputFormat* fmt。(AVInputFormat and AVOutputForm at的初始化在后面介绍。随后在参考代码output_example.c中有一行: snprintf(oc-filename, sizeof(oc->filename), “%s”, filename); (4) 还不是十分清楚有什么作用,估计是先要在输出文件中写一些头信息。 在完成以上步骤後,(初始化完毕AVInputFormat*(或AVOutputFormat*)以及AVFormatContext)接下来就是要利用oc初始化本节开始讲到的AVFormatContext中的第二个重要结构。AVStream(假设已

ffmpeg命令详解

ffmpeg命令详解 一、ffmpeg命令详解 ffmpeg非常强大,轻松几条命令就可以完成你的工作。 把darkdoor.[001-100].jpg序列帧和001.mp3音频文件利用mpeg4编码方式合成视频文件darkdoor.avi: $ ffmpeg -i 001.mp3 -i darkdoor.%3d.jpg -s 1024x768 -author skypp -vcodec mpeg4 darkdoor.avi ffmpeg还支持mov格式: $ ffmpeg -i darkdoor.%3d.jpg darkdoor.mov 要查看你的ffmpeg支持哪些格式,可以用如下命令: $ ffmpeg -formats | less 还可以把视频文件导出成jpg序列帧: $ ffmpeg -i bc-cinematic-en.avi example.%d.jpg debian下安装ffmpeg很简单: #apt-get install ffmpeg ###################################### 下面是转来的使用说明,慢慢研究吧,嘿嘿 ###################################### ffmpeg使用语法 ffmpeg使用语法: ffmpeg [[options][`-i' input_file]]... {[options] output_file}... 如果没有输入文件,那么视音频捕捉就会起作用。 作为通用的规则,选项一般用于下一个特定的文件。如果你给–b 64选项,改选会设置下一个视频速率。对于原始输入文件,格式选项可能是需要的。 缺省情况下,ffmpeg试图尽可能的无损转换,采用与输入同样的音频视频参数来输出。3.选项 a) 通用选项

ffmpeg开发指南(经典)

ffmpeg开发指南(使用 libavformat 和 libavcodec) Ffmpeg 中的Libavformat和 libavcodec库是访问大多数视频文件格式的一个很好的方法。不幸的是,在开发您自己的程序时,这套库基本上没有提供什么实际的文档可以用来作为参考(至少我没有找到任何文档),并且它的例程也并没有太多的帮助。 这种情况意味着,当我在最近某个项目中需要用到 libavformat/libavcodec 库时,需要作很多试验来搞清楚怎样使用它们。这里是我所学习的--希望我做的这些能够帮助一些人,以免他们重蹈我的覆辙,作同样的试验,遇到同样的错误。你还可以从这里下载一个demo程序。我将要公开的这部分代码需要0.4.8 版本的ffmpeg库中的 libavformat/libavcodec 的支持(我正在写最新版本)。如果您发现以后的版本与我写的程序不能兼容,请告知我。 在这个文档里,我仅仅涉及到如何从文件中读入视频流;音频流使用几乎同样的方法可以工作的很好,不过,我并没有实际使用过它们,所以,我没于办法提供任何示例代码。 或许您会觉得奇怪,为什么需要两个库文件 libavformat 和 libavcodec :许多视频文件格式(AVI就是一个最好的例子)实际上并没有明确指出应该使用哪种编码来解析音频和视频数据;它们只是定义了音频流和视频流(或者,有可能是多个音频视频流)如何被绑定在一个文件里面。这就是为什么有时候,当你打开了一个AVI文件时,你只能听到声音,却不能看到图象--因为你的系统没有安装合适的视频解码器。所以, libavformat 用来处理解析视频文件并将包含在其中的流分离出来,而libavcodec 则处理原始音频和视频流的解码。 1打开视频文件: 首先第一件事情--让我们来看看怎样打开一个视频文件并从中得到流。我们要做的第一件事情就是初始化libavformat/libavcodec: av_register_all(); 这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么做却没有什么原因。 2下一步,打开文件: AVFormatContext *pFormatCtx; const char *filename="myvideo.mpg"; // 打开视频文件 if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) handle_error(); // 不能打开此文件

ffmpeg完全安装

Linux下ffmpeg的完整安装: 首先安装各种解码器 1.安装MP3音频编码器lame lame-398-1.tar.gz Url:https://www.sodocs.net/doc/1c9937687.html,/project/showfiles.php?group_id=290&package_id=309 tar zxvf lame-398-1.tar.gz cd lame-398-1 ./configure --enable-shared --prefix=/usr make & make install 2.安装汇编优化器yasm,可能安装x264要用到 https://www.sodocs.net/doc/1c9937687.html,/projects/yasm/releases/ tar zxvf yasm-1.3.0.tar.gz cd yasm-1.3.0 ./configure --enable-shared --prefix=/usr make & make install 3.安装ogg库,libogg Url:https://www.sodocs.net/doc/1c9937687.html,/releases/ogg/libogg-1.3.2.tar.gz tar zxvf libogg-1.3.2.tar.gz cd libogg-1.3.2 ./configure --enable-shared --prefix=/usr make & make install 4.安装vorbis编解码器,libvorbis Url:https://www.sodocs.net/doc/1c9937687.html,/releases/vorbis/libvorbis-1.3.3.tar.gz tar zxvf libvorbis-1.3.3.tar.gz cd libvorbis-1.3.3 ./configure --enable-shared --prefix=/usr make & make install /*********************************这里是分隔符**************************/ 出错提示: *** Could not run Ogg test program, checking why... *** The test program compiled, but did not run. This usually means *** that the run-time linker is not finding Ogg or finding the wrong *** version of Ogg. If it is not finding Ogg, you'll need to set your *** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point *** to the installed location Also, make sure you have run ldconfig if that *** is required on your system *** *** If you have an old version installed, it is best to remove it, although *** you may also be able to get things to work by modifying LD_LIBRARY_PATH configure: error: must have Ogg installed! 解决: # vi /etc/ld.so.conf

linux下安装FFMPEG全纪录

怎样安装ffmpeg? 1,cd /usr/local/src 下载最新版本的ffmpeg: svn checkout svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg 2,cd ffmpeg ./configure make && make install 以上是安装不带任何解码库的ffmpeg。 使用ffmpeg命令进行测试安装是否成功。例如: ffmpeg --help II,要加入则在./configure后面加上选项,前提是已经编译安装了这种编码库。 [root@ip-208-109-221-163 ffmpeg]# ffmpeg --help ffmpeg: error while loading shared libraries: libavdevice.so.52: cannot open shared object file: No such file or directory 找不到libavdevice.so.52库。 解决方案:(成功解决问题的方案) 首先到/usr/local/lib/目录下去查看是否有libavdevice.so.52,然后设置环境变量:export LD_LIBRARY_PA TH=/usr/local/lib/ 注意:这种方案经验证,关闭终端再打开终端,这个环境变量就失效了,也就是说只对当前终端有效

最终解决方案: //********************************************** 因为找不到库文件。编辑/etc/ld.so.conf,加入一行: /usr/local/lib 执行ldconfig命令即可。 //************************************** 然后添加软链接。 ln -s /usr/local/lib/libavdevice.so.52 /usr/lib/libavdevice.so.52 安装成功后,测试: ffmpe g -i /home/admn/public_html/ConvertVedioT est/rjmj.mpeg -ab 56 -ar 22050 -b 500 -r 15 -qscale 4 -s 640*480 rjmj.flv 转换成功。 等等。 ln -s /usr/local/lib/libavformat.so.50 /usr/lib/libavformat.so.50 ln -s /usr/local/lib/libavcodec.so.51 /usr/lib/libavcodec.so.51 ln -s /usr/local/lib/libavutil.so.49 /usr/lib/libavutil.so.49 ln -s /usr/local/lib/libmp3lame.so.0 /usr/lib/libmp3lame.so.0 ln -s /usr/local/lib/libavformat.so.51 /usr/lib/libavformat.so.51

ffmpeg 常用命令汇总

ffmpeg 常用命令汇总 2016-06-15 09:11 17116人阅读评论(0)收藏举报 (经常用到ffmpeg 做一些视频数据的处理转换等,用来做测试,今天总结了一下, 参考了网上部分朋友的经验,一起在这里汇总了一下,有需要的朋友可以收藏测试 一下,有问题欢迎在下面回帖交流,谢谢;by ternence.hsu) 1、ffmpeg使用语法 命令格式: ffmpeg -i [输入文件名] [参数选项] -f [格式] [输出文件] ffmpeg [[options][`-i' input_file]]... {[options] output_file}... 1、参数选项: (1) -an: 去掉音频 (2) -acodec: 音频选项,一般后面加copy表示拷贝 (3) -vcodec:视频选项,一般后面加copy表示拷贝 2、格式: (1) h264: 表示输出的是h264的视频裸流 (2) mp4: 表示输出的是mp4的视频 (3)mpegts: 表示ts视频流 如果没有输入文件,那么视音频捕捉(只在Linux下有效,因为Linux下把音视频设备当作文件句柄选项一般用于下一个特定的文件。如果你给–b 64选项,改选会设置下一个视频速率。对于原始输入下,ffmpeg试图尽可能的无损转换,采用与输入同样的音频视频参数来输出。(by ternence.hsu) 2、视频转换 H264视频转ts视频流 ffmpeg -i test.h264 -vcodec copy -f mpegts test.ts H264视频转mp4 ffmpeg -i test.h264 -vcodec copy -f mp4 test.mp4 ts视频转mp4 ffmpeg -i test.ts -acodec copy -vcodec copy -f mp4 test.mp4 mp4视频转flv ffmpeg -i test.mp4 -acodec copy -vcodec copy -f flv test.flv

ffmpeg绝对编译指南

编译集成ffmpeg指南 ###环境配置(centos/msys2) yum install autoconf automake cmake freetype-devel openssl-devel gcc gcc-c++ git libtool make mercurial nasm pkgconfig zlib-devel ###参考地址 https://www.sodocs.net/doc/1c9937687.html,/uid-23069658-id-4018842.html https://www.sodocs.net/doc/1c9937687.html,/wanglx_/article/details/50786307 ###2.1 Yasm(git clone --depth 1 git://https://www.sodocs.net/doc/1c9937687.html,/yasm/yasm.git) 汇编器,编译x264,ffmpeg会用到 autoreconf -fiv ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" make make install make distclean ###libx264 (git clone --depth 1 git://https://www.sodocs.net/doc/1c9937687.html,/x264) H264编码库,开启需要在FFmpeg的configure中指定:--enable-gpl 和--enable-libx264 ./configure --prefix="$HOME/ffmpeg_build" --enable-static --enable-shared make make install make distclean ###初始pkgconfig(必须) export PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ###libx265(hg clone https://https://www.sodocs.net/doc/1c9937687.html,/multicoreware/x265) H265编码库,开启需要在FFmpeg的configure中指定:--enable-gpl 和--enable-libx265 cd 到/x265/build/msys目录下执行对应.sh文件,稍后在cmake弹框中编辑安装目录 make make install ###libfdk_aac(git clone --depth 1 git://https://www.sodocs.net/doc/1c9937687.html,/p/opencore-amr/fdk-aac) AAC编码库,开启需要在FFmpeg的configure中指定:--enable-libfdk-aac autoreconf -fiv ./configure --prefix="$HOME/ffmpeg_build" --enable-shared --enable-static SPEEXDSP_CFLAGS="$HOME/ffmpeg_build/include" SPEEXDSP_LIBS="$HOME/ffmpeg_build/lib" OGG_CFLAGS="$HOME/ffmpeg_build/include" OGG_LIBS="$HOME/ffmpeg_build/lib" make make install make distclean

相关主题