关键词搜索

源码搜索 ×
×

FFmpeg和SDL教程(二):输出到屏幕

发布2018-01-12浏览1641次

详情内容

英文原文地址:http://dranger.com/ffmpeg/tutorial02.html

SDL和视频

要画到屏幕上,我们要使用SDL。 SDL代表简单直接层,是一个优秀的多媒体库,是跨平台的,并在多个项目中使用。您可以在官方网站上获得该库,或者可以下载适用于您的操作系统的开发包(如果有的话)。您将需要库来编译本教程的代码(以及其他代码)。


SDL有很多方法可以将图像绘制到屏幕上,特别是用于在屏幕上显示电影的方法 - 它称为YUV覆盖。 YUV(技术上不是YUV,而是YCbCr)*注意:在“YCbCr”“YUV”的会议上有些人很烦恼。一般来说,YUV是模拟格式,YCbCr是数字格式。 ffmpeg和SDL都在其代码和宏中将YCbCr称为YUV。是一种存储像RGB原始图像数据的方法。粗略地说,Y是亮度(或“亮度”)分量,U和V是颜色分量。 (它比RGB更复杂,因为有些颜色信息被丢弃,每2个Y样本可能只有1个U和V样本。)SDL的YUV叠加接收YUV数据的原始数组并显示它。它接受4种不同的YUV格式,但是YV12是最快的。除了U和V阵列切换之外,还有另一种称为YUV420P的YUV格式,与YV12相同。 420表示以4:2:0的比例进行二次采样,基本上每4个亮度采样就有一个颜色采样,所以颜色信息被分解。这是一个节省带宽的好方法,因为人眼不会察觉到这种变化。名称中的“P”表示格式为“平面” - 仅表示Y,U和V组件在单独的数组中。 ffmpeg可以将图像转换为YUV420P,还有很多视频流已经以这种格式存在,或者很容易转换成这种格式。


所以我们目前的计划是从教程1中取代SaveFrame()函数,而不是输出我们的帧到屏幕上。但首先我们要看看如何使用SDL库。首先,我们必须包括图书馆和初级SDL:

  1. #include <SDL.h>
  2. #include <SDL_thread.h>
  3. if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
  4. fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
  5. exit(1);
  6. }
SDL_Init()从本质上告诉图书馆我们将要使用的功能。 SDL_GetError()当然是一个方便的调试功能。

创建一个显示

现在我们需要在屏幕上放置一些东西。 使用SDL显示图像的基本区域称为曲面:
  1. SDL_Surface *screen;
  2. screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
  3. if(!screen) {
  4. fprintf(stderr, "SDL: could not set video mode - exiting\n");
  5. exit(1);
  6. }
这设置了具有给定宽度和高度的屏幕。 下一个选项是屏幕的位深度 - 0是一个特殊值,意思是“与当前显示相同”。 (这不适用于OS X;请参阅源代码。)
现在我们在该屏幕上创建一个YUV叠加层,以便我们可以输入视频,并设置我们的SWSContext将图像数据转换为YUV420:

  1. SDL_Overlay *bmp = NULL;
  2. struct SWSContext *sws_ctx = NULL;
  3. bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
  4. SDL_YV12_OVERLAY, screen);
  5. // initialize SWS context for software scaling
  6. sws_ctx = sws_getContext(pCodecCtx->width,
  7. pCodecCtx->height,
  8. pCodecCtx->pix_fmt,
  9. pCodecCtx->width,
  10. pCodecCtx->height,
  11. PIX_FMT_YUV420P,
  12. SWS_BILINEAR,
  13. NULL,
  14. NULL,
  15. NULL
  16. );
如前所述,我们使用YV12来显示图像,并从ffmpeg获取YUV420数据。

显示图像

那很简单! 现在我们只需要显示图像。 让我们一直走到我们完成框架的地方。 我们可以摆脱我们为RGB框架所做的所有工作,并且将用我们的显示代码替换SaveFrame()。 为了显示图像,我们将制作一个AVPicture结构,并设置它的数据指针和lineize到我们的YUV覆盖:

  1. if(frameFinished) {
  2. SDL_LockYUVOverlay(bmp);
  3. AVPicture pict;
  4. pict.data[0] = bmp->pixels[0];
  5. pict.data[1] = bmp->pixels[2];
  6. pict.data[2] = bmp->pixels[1];
  7. pict.linesize[0] = bmp->pitches[0];
  8. pict.linesize[1] = bmp->pitches[2];
  9. pict.linesize[2] = bmp->pitches[1];
  10. // Convert the image into YUV format that SDL uses
  11. sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
  12. pFrame->linesize, 0, pCodecCtx->height,
  13. pict.data, pict.linesize);
  14. SDL_UnlockYUVOverlay(bmp);
  15. }
首先,我们锁定叠加层,因为我们将要写入它。这是一个很好的习惯,以便你以后没有问题。如前所示,AVPicture结构有一个数据指针,它是一个由4个指针组成的数组。由于我们在这里处理YUV420P,所以我们只有3个通道,因此只有3组数据。其他格式可能有一个alpha通道或其他东西的第四个指针。 lineize是它听起来像。我们的YUV覆盖中的类似结构是像素和间距变量。 (“pitch”是SDL用于指代给定数据行的宽度的术语)。因此,我们所做的是将pict.data的三个数组指向我们的叠加层,所以当我们写入pict的时候,我们实际上写入我们的覆盖层,这当然已经分配了必要的空间。同样,我们直接从我们的叠加层获取线路信息。我们将转换格式更改为PIX_FMT_YUV420P,并像以前一样使用sws_scale。

绘制图像

但是我们仍然需要告诉SDL实际显示我们提供的数据。我们还通过这个函数一个矩形,说明电影应该去哪里,什么样的宽度和高度应该缩放。这样,SDL就可以为我们进行缩放,并且可以通过图形处理器来加速缩放:
  1. SDL_Rect rect;
  2. if(frameFinished) {
  3. /* ... code ... */
  4. // Convert the image into YUV format that SDL uses
  5. sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
  6. pFrame->linesize, 0, pCodecCtx->height,
  7. pict.data, pict.linesize);
  8. SDL_UnlockYUVOverlay(bmp);
  9. rect.x = 0;
  10. rect.y = 0;
  11. rect.w = pCodecCtx->width;
  12. rect.h = pCodecCtx->height;
  13. SDL_DisplayYUVOverlay(bmp, &rect);
  14. }
现在我们的视频显示!
让我们花点时间向您展示SDL的另一个特性:事件系统。 SDL的设置是为了在SDL应用程序中键入或移动鼠标或发送信号时生成事件。 如果您的程序想要处理用户输入,则会检查这些事件。 您的程序也可以组成事件来发送SDL事件系统。 这在使用SDL进行多线程编程时特别有用,我们将在教程4中看到。在我们的程序中,我们将在完成处理数据包后立即轮询事件。 现在,我们只是要处理SDL_QUIT事件,所以我们可以退出:
  1. SDL_Event event;
  2. av_free_packet(&packet);
  3. SDL_PollEvent(&event);
  4. switch(event.type) {
  5. case SDL_QUIT:
  6. SDL_Quit();
  7. exit(0);
  8. break;
  9. default:
  10. break;
  11. }
我们之前就是这样! 摆脱所有的旧版本,你准备好编译。 如果您正在使用Linux或变体,使用SDL库进行编译的最佳方法是:
  1. gcc -o tutorial02 tutorial02.c -lavformat -lavcodec -lswscale -lz -lm \
  2. `sdl-config --cflags --libs`
sdl-config只是打印出适当的gcc标志来正确包含SDL库。 你可能需要做一些不同的事情来让它在你的系统上编译。 请检查您的系统的SDL文档。 一旦编译完成,继续运行。

当你运行这个程序时会发生什么? 视频疯了! 实际上,我们只是尽可能快地显示所有的视频帧,我们可以从电影文件中提取它们。 我们现在还没有任何代码来确定何时需要显示视频。 最后(在教程5中),我们将开始同步视频。 但首先我们错过了更重要的东西:声音!


相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载