[笔记]音视频学习之ffmpeg实践《一》常用结构体及裁剪画面思路(下)

简介: [笔记]音视频学习之ffmpeg实践《一》常用结构体及裁剪画面思路(下)

左下角裁剪

思路

用 左下角 1/2 hSrc * 1/2 wSrc 像素内容 替换 左上角内容

实现

//获得pCropFrame
    ...
    int wDst = pPicFrame->width * 2 / 4;
  int hDst = pPicFrame->height * 2 / 4;
  int wSrc = pPicFrame->width;
  int hSrc = pPicFrame->height;
  //AVFrame* pCropFrame = WXMediaUtilsAllocVideoFrame(AV_PIX_FMT_RGB32, wDst, hDst);//新的
  //右上角
  int lineSizeY = pPicFrame->linesize[0];
  int lineSizeU = pPicFrame->linesize[1];
  int lineSizeV = pPicFrame->linesize[2];
  //crop Y
  for (size_t line = 0; line < hDst; line++)
  {
    //起始[0]+起始行hSrc/2*line +行中心开始lineSize/2
    memcpy(pPicFrame->data[0] + line *lineSizeY,
      pPicFrame->data[0] + (hSrc / 2 + line) * lineSizeY,
      lineSizeY / 2);
  }
  //crop U
  for (size_t line = 0; line < hDst / 2; line++)
  {
    //起始[0]+起始行hDst/2*line +行中心开始lineSize/2
    memcpy(pPicFrame->data[1] + line * lineSizeU,
      pPicFrame->data[1] + (hDst / 2 + line) * lineSizeU,
      lineSizeU / 2);
  }
  //crop V
  for (size_t line = 0; line < hDst / 2; line++)
  {
    memcpy(pPicFrame->data[2] + line * lineSizeV,
      pPicFrame->data[2] + (hDst / 2 + line) * lineSizeV,
      lineSizeV / 2);
  }
  pPicFrame->width = wDst;
  pPicFrame->height = hDst;
  ...
  //显示pCropFrame

右下角裁剪

思路

用 右下角 1/2 hSrc * 1/2 wSrc 像素内容 替换 左上角内容

实现

//获得pCropFrame
    ...
  int wDst = pCropFrame->width * 2 / 4;
  int hDst = pCropFrame->height * 2 / 4;
  int wSrc = pCropFrame->width;
  int hSrc = pCropFrame->height;
  //左上角
  //pCropFrame->width = wDst;
  //pCropFrame->height = hDst;
  //右上角
  int lineSizeY = pCropFrame->linesize[0];
  int lineSizeU = pCropFrame->linesize[1];
  int lineSizeV = pCropFrame->linesize[2];
  //crop Y
  for (size_t line = 0; line < hDst; line++)
  {
    //起始[0]+起始行hSrc/2*line +行中心开始lineSize/2
    memcpy(pCropFrame->data[0] + line *lineSizeY,
      pCropFrame->data[0] + (hSrc / 2 + line) * lineSizeY  + lineSizeY / 2,
      lineSizeY / 2);
  }
  //crop U
  for (size_t line = 0; line < hDst/2; line++)
  {
    //起始[0]+起始行hDst/2*line +行中心开始lineSize/2
    memcpy(pCropFrame->data[1] + line * lineSizeU,
      pCropFrame->data[1] +  (hDst / 2 + line) * lineSizeU + lineSizeU / 2 ,
      lineSizeU / 2);
  }
  //crop V
  for (size_t line = 0; line < hDst/2; line++)
  {
    memcpy(pCropFrame->data[2] + line * lineSizeV,
      pCropFrame->data[2] + (hDst / 2 + line) * lineSizeV + lineSizeV / 2,
      lineSizeV / 2);
  }
  pCropFrame->width = wDst;
  pCropFrame->height = hDst;
  ...
  //显示pCropFrame

中心裁剪

思路

用 中心 1/2 hSrc * 1/2 wSrc 像素内容 替换 左上角内容

实现

//获得pCropFrame
    ...
    int wDst = pPicFrame->width * 2 / 4;
  int hDst = pPicFrame->height * 2 / 4;
  int wSrc = pPicFrame->width;
  int hSrc = pPicFrame->height;
  //AVFrame* pCropFrame = WXMediaUtilsAllocVideoFrame(AV_PIX_FMT_RGB32, wDst, hDst);//新的
  //右上角
  int lineSizeY = pPicFrame->linesize[0];
  int lineSizeU = pPicFrame->linesize[1];
  int lineSizeV = pPicFrame->linesize[2];
  //crop Y
  for (size_t line = 0; line < hDst; line++)
  {
    memcpy(pPicFrame->data[0] + line * lineSizeY,
      pPicFrame->data[0] + (hSrc / 4 + line) * lineSizeY +  lineSizeY * 4 / 16,
      lineSizeY / 2);
  }
  //crop U
  for (size_t line = 0; line < hDst / 2; line++)
  {
    memcpy(pPicFrame->data[1] + line * lineSizeU,
      pPicFrame->data[1] + (hDst / 4 + line) * lineSizeU + lineSizeU * 4 / 16,
      lineSizeU / 2);
  }
  //crop V
  for (size_t line = 0; line < hDst / 2; line++)
  {
    memcpy(pPicFrame->data[2] + line * lineSizeV,
      pPicFrame->data[2] + (hDst / 4 + line) * lineSizeV +  lineSizeV * 4 / 16,
      lineSizeV / 2);
  }
  pPicFrame->width = wDst;
  pPicFrame->height = hDst;
  ...
  //显示pCropFrame

任意区域裁剪

思路

确认裁剪区域起始坐标和尺寸

实现

AVFrame* pPicFrame = WXMediaUtilsFromPicture(m_pwszPicPath);
//目标裁剪矩形 及其坐标
  typedef struct Rect{
    int x;
    int y;
    int width;
    int height;
  }Rect;
  Rect dstRect;
  //设定裁剪中心和尺寸  
  dstRect.x = pPicFrame->width  * 4 / 16;
  dstRect.y = pPicFrame->height * 4 / 16;
  dstRect.width  = pPicFrame->width  * 4 / 16;
  dstRect.height = pPicFrame->height * 4 / 16;
  //
  wchar_t text[260] = {0};
  wsprintf(text,L"rect (%d,%d) w:%d,h:%d", dstRect.x, dstRect.y, dstRect.width, dstRect.height);
  MessageBox(text, L"提示", 0);
  int maxHeight = pPicFrame->height;
  int maxWidth = pPicFrame->width;
  if (dstRect.x<0 || dstRect.x >maxWidth) {
    return;
  }
  if (dstRect.y<0 || dstRect.y >maxHeight) {
    return;
  }
  int wDst = dstRect.width;
  int hDst = dstRect.height;
  int wSrc = pPicFrame->width;
  int hSrc = pPicFrame->height;
...
  //创建pCropFrame 新尺寸
  //AVFrame* pCropFrame = AllocFrame(AV_PIX_FMT_YUV420P, wDst, hDst);//新的
...
  //右上角
  int srcLineSizeY = pPicFrame->linesize[0];
  int srcLineSizeU = pPicFrame->linesize[1];
  int srcLineSizeV = pPicFrame->linesize[2];
  int fromHeight = dstRect.y - dstRect.height / 2;
  int fromWidth = dstRect.x - dstRect.width / 2;
  if (fromHeight < 0) {
    fromHeight = dstRect.y;
  }
  if (fromHeight+dstRect.height > hSrc) {
  }
  if (fromWidth < 0) {
    fromWidth = dstRect.x;
  }
  if (fromHeight + dstRect.height > hSrc) {
  }
  //crop Y
  for (size_t line = 0; line < dstRect.height; line++)
  {
    void *dst = pCropFrame->data[0] + line * pCropFrame->linesize[0];
    int offset = (fromHeight + line) * srcLineSizeY + fromWidth;
    void *from = pPicFrame->data[0] + offset;
    int size = srcLineSizeY * dstRect.width / pPicFrame->width;
    memcpy(dst,from, size);
  }
  //crop U
  for (size_t line = 0; line < dstRect.height / 2; line++)
  {
    memcpy(pCropFrame->data[1] + line * pCropFrame->linesize[1],
      pPicFrame->data[1] + (fromHeight / 2 + line) * srcLineSizeU + fromWidth/2,
      srcLineSizeY * dstRect.width / pPicFrame->width);
  }
  //crop V
  for (size_t line = 0; line < dstRect.height / 2; line++)
  {
    memcpy(pCropFrame->data[2] + line * pCropFrame->linesize[2],
      pPicFrame->data[2] + (fromHeight / 2 + line) * srcLineSizeV + fromWidth/2,
      srcLineSizeY * dstRect.width / pPicFrame->width);
  }
  pCropFrame->width = wDst;
  pCropFrame->height = hDst;
  AVFrame* pResizeFrame = pCropFrame;
...
  //display pResizeFrame
...

缩放

思路

思路比较简单

基于原画面数据的缩放

放大就是裁剪需要放大部分+Resize画面尺寸为原画面大小,连续放大就是每次将上次放大的Frame再作为下次放大的srcFrame 进行操作;

缩小就是将原画面放到新画面中心 四周填黑,宽高扩大。

实现

放大

...
    //s_pPicFrame为src AVFrame
...
  int wDst = s_pPicFrame->width * 2 / 4;
  int hDst = s_pPicFrame->height * 2 / 4;
  int wSrc = s_pPicFrame->width;
  int hSrc = s_pPicFrame->height;
  AVFrame* pCropFrame = AllocFrame(AV_PIX_FMT_YUV420P, wDst, hDst);//新的
                                   //右上角
  int lineSizeY = s_pPicFrame->linesize[0];
  int lineSizeU = s_pPicFrame->linesize[1];
  int lineSizeV = s_pPicFrame->linesize[2];
  //crop Y
  for (size_t line = 0; line < hDst; line++)
  {
    memcpy(pCropFrame->data[0] + line * pCropFrame->linesize[0],
      s_pPicFrame->data[0] + (hSrc / 4 + line) * lineSizeY + lineSizeY * 4 / 16,
      lineSizeY / 2);
  }
  //crop U
  for (size_t line = 0; line < hDst / 2; line++)
  {
    memcpy(pCropFrame->data[1] + line * pCropFrame->linesize[1],
      s_pPicFrame->data[1] + (hDst / 4 + line) * lineSizeU + lineSizeU * 4 / 16,
      lineSizeU / 2);
  }
  //crop V
  for (size_t line = 0; line < hDst / 2; line++)
  {
    memcpy(pCropFrame->data[2] + line * pCropFrame->linesize[2],
      s_pPicFrame->data[2] + (hDst / 4 + line) * lineSizeV + lineSizeV * 4 / 16,
      lineSizeV / 2);
  }
  pCropFrame->width = wDst;
  pCropFrame->height = hDst;
...
    //通过libyuv scale pCropFrame
...
...
    //然后显示出来
...

缩小 四周填黑

...
//pPicFrame 为src AVFrame
...
  int wDst = pPicFrame->width  * 2;
  int hDst = pPicFrame->height * 2;
  int wSrc = pPicFrame->width;
  int hSrc = pPicFrame->height;
//扩大宽高
  AVFrame* pCropFrame = AllocFrame(AV_PIX_FMT_YUV420P, wDst, hDst);//新的
  int lineSizeY = pPicFrame->linesize[0];
  int lineSizeU = pPicFrame->linesize[1];
  int lineSizeV = pPicFrame->linesize[2];
//扩大宽高
  pCropFrame->width = wDst;
  pCropFrame->height = hDst;
  pCropFrame->linesize[0] = pPicFrame->linesize[0] * 2;
  pCropFrame->linesize[1] = pPicFrame->linesize[1] * 2;
  pCropFrame->linesize[2] = pPicFrame->linesize[2] * 2;
//默认填黑
  memset(pCropFrame->data[0],   0, pCropFrame->linesize[0] * hDst );
  memset(pCropFrame->data[1], 128, pCropFrame->linesize[1] * hDst / 2);
  memset(pCropFrame->data[2], 128, pCropFrame->linesize[2] * hDst / 2);
  //Process Y
  for (size_t line = 0; line < hSrc; line++)
  {
    memcpy(pCropFrame->data[0] + (hSrc / 2 + line) * pCropFrame->linesize[0] + pCropFrame->linesize[0] * 4 / 16,
      pPicFrame->data[0] + line*pPicFrame->linesize[0],
        wDst / 2);
  }
  //Process U
  for (size_t line = 0; line < hSrc / 2; line++)
  {
    memcpy(pCropFrame->data[1] + (hSrc / 4 + line) * pCropFrame->linesize[1] + pCropFrame->linesize[1] * 4 / 16,
      pPicFrame->data[1] + line*pPicFrame->linesize[1],
      wDst / 4);
  }
  //Process V
  for (size_t line = 0; line < hSrc / 2; line++)
  {
    memcpy(pCropFrame->data[2] + (hSrc / 4 + line) * pCropFrame->linesize[2] + pCropFrame->linesize[2] * 4 / 16,
      pPicFrame->data[2] + line*pPicFrame->linesize[2],
      wDst / 4);
  }
  pCropFrame->width  = wDst;
  pCropFrame->height = hDst;
...
//显示
...


相关文章
|
13天前
|
编解码 Linux 计算机视觉
python 调用ffmpeg使用usb摄像头录制视频,输出h264格式,自动获取摄像头的最佳帧率和最大画面尺寸
使用 Python 调用 FFmpeg 进行 USB 摄像头视频录制,需先确保安装 FFmpeg 和 Python 的 `subprocess` 模块。代码示例展示了如何自动获取摄像头的最佳帧率和最大分辨率,然后录制视频。首先通过 FFmpeg 列出摄像头格式获取信息,解析出帧率和分辨率,选择最优值。之后调用 FFmpeg 命令录制视频,设置帧率、分辨率等参数。注意 `/dev/video0` 是 Linux 的摄像头设备路径,Windows 系统需相应调整。代码中未直接实现自动获取最佳参数,通常需要借助其他库如 OpenCV。
|
13天前
|
存储 编解码 Linux
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
21 1
|
1月前
|
存储 缓存 调度
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
《FFmpeg开发实战》第10章示例playsync.c在处理音频流和视频流交错的文件时能实现同步播放,但对于分开存储的格式,会出现先播放全部声音再快速播放视频的问题。为解决此问题,需改造程序,增加音频处理线程和队列,以及相关锁,先将音视频帧读入缓存,再按时间戳播放。改造包括声明新变量、初始化线程和锁、修改数据包处理方式等。代码修改后在playsync2.c中,编译运行成功,控制台显示日志,SDL窗口播放视频并同步音频,证明改造有效。
41 0
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
|
1月前
|
编解码 安全 计算机视觉
FFMPEG常用命令 音视频合并
FFMPEG常用命令 音视频合并
34 2
|
1月前
|
Web App开发 编解码 vr&ar
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
75 2
|
1月前
|
存储 编解码 缓存
ffmpeg音视频同步
ffmpeg音视频同步
30 0
|
1月前
|
开发工具
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(三)
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(三)
43 0
|
1月前
|
Linux 编译器 数据安全/隐私保护
Windows10 使用MSYS2和VS2019编译FFmpeg源代码-测试通过
FFmpeg作为一个流媒体的整体解决方案,在很多项目中都使用了它,如果我们也需要使用FFmpeg进行开发,很多时候我们需要将源码编译成动态库或者静态库,然后将库放入到我们的项目中,这样我们就能在我们的项目中使用FFmpeg提供的接口进行开发。关于FFmpeg的介绍这里就不过多说明。
121 0
|
9月前
|
C++ Windows
FFmpeg入门及编译 3
FFmpeg入门及编译
72 0
|
1月前
|
Linux
Linux编译FFmpeg
Linux编译FFmpeg
50 0