现在的位置: 首页 > 综合 > 正文

FFmpeg tutorial0-4 总结

2012年04月21日 ⁄ 综合 ⁄ 共 5071字 ⁄ 字号 评论关闭

实验一总结:

      -------处理视频文件的过程

1.要打开一个音视频文件,第一步,我们要初始化媒体库,对于ffmpeg我们将avcodec.havformat.h包含进来就行了,这两个文件包含了库所支持的所有文件格式和编解码器,通过调用av_register_all() 将这些信息进行注册,这样就可以将相应的文件格式对应到合适的解码器。

1.第二步,打开文件,使用av_open_input_fileAVFormatContext **ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap),利用第二个参数filename打开文件,将文件头和关于文件格式的存储信息保存到ptr指向AVFormateContext结构体中,fmt:不为NULL时则强制规定文件的格式,buf_size:可选的缓冲区大小,ap用于规定解码时的特定参数(时基、采样率、通道数、宽、高、像素格式等),后三个参数如果不指定值,则libavformat会自动检测。

2.第三步,上个函数只是获得了文件的头信息,下面我们要知道文件stream信息,使用av_find_stream_info(),它会寻找stream的不太明显的信息,并将这些信息填充到pFormatCtxstreams域中,这对没有文件头的MPEG文件是很有用的。到此文件的相关信息都已得到,然后会调用dump_format(pFormatCtx, 0, argv[1], 0)将这些信息进行输出,以确定正确性。

3.第四步,上一步中存储在streams域中的是一个指针数组(AVStream类型),nb_streams是文件中streams的个数,然后找到所有的视频stream,并获得该视频stream的编解码器信息,即编解码器的上下文(codec contextpCodecCtx(AVCodecContext类型)(帧速率、采样率、通道数等);

4.这一步我们要找到相应的编解码器,使用avcodec_find_decoder (enum CodecID id ),它利用上一步编解码器上下文pCodecCtx的到的编解码器的id找到相应的编解码器,使用avcodec_openAVCodecContext *, AVCodec *)使用找到编解码器初始化pCodecCtx

5.下一步是分配存储帧的物理空间,首先使用avcodec_alloc_frame()进行空寂分配,然后使用avpicture_get_sizeint pix_fmt, int width, int height)计算给定尺寸和图像格式的图片需要的存储空间(byte)以便分配相应的缓冲区,av_malloc()只是简单包装了一下malloc()不保证分配过程的安全性。

6.有了上一步分配存储空间,现在我们需要把存储空间和图像帧联系起来,是用 avpicture_fillAVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int heightAVFrameAVPicture的超集,第二个参数指向上一步分配的缓冲空间,

7.所有的准备工作都完成了,这一步就可以读视频stream了,使用av_read_frame()读取帧中的packet中,packet.stream_index指示帧的位置(即是文件中的第几个stream),判断是否为视频stream,若是则调用 avcodec_decode_video()将缓冲区里的视频帧解码(使用上面得到的合适该视频流的编解码器)为图片(AVFrame类型)放到为转换格式前的视频帧分配的空间中;然后就是将得到的视频帧转换为我们需要的格式,调用sws_getContext()和sws_scale();然后就可将转换好的视频帧存到磁盘了(SaveFrame) ,使用av_free_packet()释放packet

8.最后释放之前分配的存储空间,关闭视频文件。

注意:img_convert()已经弃用了

实验二总结

-------怎样把视频输出到屏幕上

1.为了把视频输出到显示器上,我们需要使用SDLSDLSimple DirectMedia Layer)是一个自由的跨平台的多媒体开发包, 我们实际是要用的是YUVY亮度,UV表示颜色) overlaySDLYUV overlay获得未加工的YUV数据队列,并进行显示。它支持4YUV格式,但YV12(和YUV420P:亮度采样是颜色的4倍,相同)是最快的。

2.第一步,将SDL的头文件包含进来,并对其进行初始化。

#include <SDL.h>

#include <SDL_thread.h>

SDL_Init()

3.第二步,获得显示图像的区域(surface),调用 SDL_SetVideoMode()设置surface

4.第三步,定义YUV overlay来接收视频数据进行显示,SDL_CreateYUVOverlay();

5.第四步,将AVPictureSDL_overlay两种结构对应起来,然后调用sws_getContext()和sws_scale()进行格式转换;

6.现在就可以进行显示了,我们定义显示的位置和屏幕大小,然后调用SDL_DisplayYUVOverlay()进行显示。

实验三总结

      ------播放声音

1.计算机是怎样处理声音的:数字音频包含一长串的样值,每个样值代表声波的一个值,广播的采样率是22050CD的采样率为44100,很多有立体声和环绕效果的音频会有多个通道。从一个电影文件得到的数据我们并不知道会有多少个样值,但是ffmpeg的样值都是完整的(就是说不会将立体声样值分离开);SDL播放音频的方法是:由你设置采样率、通道数等等,我们也要设置一个回调函数和用户资料;当我们播放音频时,SDL会连续地调用这个回调函数,让它填充音频缓冲区,SDL会调用相应的方法来处理,并输出我们可以使用的数据。

2.第一步,找到所需的音频stream(方法同视频);

3.第二步,获取音频的context(采样率、格式、通道数、缓冲大小、回调函数);

4.第三步,调用 SDL_OpenAudio(),它会利用上面得到的context来打开相应的音频设备;

5.第四步,根据context寻找并打开相应的音频编解码器;

6.第五步,建立一个队列并进行初始化,使我们从电影文件中获得的连续的packets可以被SDL的回调函数使用,AVPacketList ,因为SDL是在一个独立的线程里进行音频处理的,因此对队列的访问必须进行控制,我们使用了一个互斥量和一个条件变量(线程间通信);

7.第六步,将获得packet放到队列中,过程:先为packet分配空间,然后将这个packet装入具有AVPacketList 节点结构的数据结构中,访问队列获取互斥量,修改队列指针将该节点放到队列中,通知等待该packet的线程,释放互斥量。

8.第七步,取packet的函数从队列

9.中取packet,我们表面上使用了使用无限循环来取packet,但是SDL中的函数 SDL_CondWait()避免了单纯的无限循环,它会释放获得的互斥量,然后等待其他线程调用SDL_CondSignal 或SDL_CondBroadcast ,之后它将再次锁住互斥量。

10.第八步,启动音频设备,调用SDL_PauseAudio();

11.回调函数的任务:取队列中的packet,调用audio_decode_frame()(实际上它又调用avcodec_decode_audio2())对packet进行解码,解码内容放到音频缓冲区中供SDL使用。

12.整个数据流程:读取音频stream  ——放到队列中——>回调函数读队列——>回调函数将数据给SDL——>SDL将其播送到声卡中。

附件:

1.YUV 覆盖(Overlay  多媒体领域中,尤其在涉及到 MPEG 播放时,通常使用 YUV 颜色空间来表示颜色,如果要在屏幕上显示一副 MPEG 解压之后的图片,则需要进行 YUV 颜色空间到 RGB 颜色空间的转换。YUV 覆盖最初来自一些显示芯片的加速功能。这种显示芯片能够在硬件基础上完成 YUV 到 RGB 的转换,免去软件转换带来的性能损失。在这种显示芯片上建立了 YUV 覆盖之后,可以直接将 YUV 信息写入缓冲区,硬件能够自动完成 YUV 到 RGB 的转换,从而在 RGB 显示器上显示出来。在不支持 YUV 覆盖的显示芯片上,MiniGUI 也能够通过软件实现 YUV 覆盖,这时,需要调用 DisplayYUVOverlay 函数将 YUV 信息转换并缩放显示在建立 YUV 覆盖的 DC 设备上。  MiniGUI 提供的 YUV 覆盖操作函数原型如下:

/***************************** YUV overlay support ***************************/

 /* 最常见的视频覆盖格式.*/

 #define GAL_YV12_OVERLAY0x32315659/* Planar mode: Y + V + U(3 planes) */

 #define GAL_IYUV_OVERLAY0x56555949/* Planar mode: Y + U + V(3 planes) */

 #define GAL_YUY2_OVERLAY0x32595559/* Packed mode: Y0+U0+Y1+V0 (1 plane) */

 #define GAL_UYVY_OVERLAY0x59565955/* Packed mode: U0+Y0+V0+Y1 (1 plane) */

 #define GAL_YVYU_OVERLAY0x55595659/* Packed mode: Y0+V0+Y1+U0 (1 plane) */

/* 该函数创建一个视频输出覆盖  */

 GAL_Overlay* GUIAPI CreateYUVOverlay (int width, int height,Uint32 format, HDC hdc);/* 锁定覆盖进行直接的缓冲区读写,结束后解锁 */

  int GAL_LockYUVOverlay (GAL_Overlay *overlay);

    void GAL_UnlockYUVOverlay (GAL_Overlay *overlay);

    #define LockYUVOverlay GAL_LockYUVOverlay

    #define UnlockYUVOverlay GAL_UnlockYUVOverlay/* 释放视频覆盖 */

     void GAL_FreeYUVOverlay (GAL_Overlay *overlay);

    #define FreeYUVOverlay GAL_FreeYUVOverlay/* 将视频覆盖传送到指定 DC   设备上。该函数能够进行 维缩放  */

    void GUIAPI DisplayYUVOverlay (GAL_Overlay* overlay, const RECT* dstrect); 

2

void *memcpy(void *dest, const void *src, size_t n);

  由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内。  #include <string.h>#include <memory.h>

  函数返回一个指向dest的指针。

 1.sourcedestin所指内存区域不能重叠,函数返回指向destin的指针。 

 2.strcpy相比,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。 

 3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则
   每次执行
memcpy后,要将目标数组地址增加到你要追加数据的地址。 

抱歉!评论已关闭.