[笔记]音视频学习之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;
...
//显示
...


相关文章
|
3月前
|
Web App开发 5G Linux
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
一年一度的毕业季来临,计算机专业的毕业设计尤为重要,不仅关乎学业评价还积累实战经验。选择紧跟5G技术趋势的音视频APP作为课题极具吸引力。这里推荐三类应用:一是融合WebRTC技术实现视频通话的即时通信APP;二是具备在线直播功能的短视频分享平台,涉及RTMP/SRT等直播技术;三是具有自定义动画特效及卡拉OK歌词字幕功能的视频剪辑工具。这些项目不仅技术含量高,也符合市场需求,是毕业设计的理想选择。
79 6
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
|
2月前
|
Android开发 计算机视觉 C++
FFmpeg开发笔记(五十一)适合学习研究的几个音视频开源框架
音视频编程对许多程序员来说是一片充满挑战的领域,但借助如OpenCV、LearnOpenGL、FFmpeg、OBS Studio及VLC media player等强大的开源工具,可以降低入门门槛。这些框架不仅覆盖了计算机视觉、图形渲染,还包括多媒体处理与直播技术,通过多种编程语言如Python、C++的应用,使得音视频开发更为便捷。例如,OpenCV支持跨平台的视觉应用开发,FFmpeg则擅长多媒体文件的处理与转换,而VLC media player则是验证音视频文件质量的有效工具。
92 0
FFmpeg开发笔记(五十一)适合学习研究的几个音视频开源框架
|
2月前
用ffmpeg提取合并音视频
用ffmpeg提取合并音视频
|
25天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
100 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
1月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
51 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
1月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
127 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
69 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
2月前
|
XML Java Android开发
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
GSYVideoPlayer是一款国产移动端视频播放器,支持弹幕、滤镜、广告等功能,采用IJKPlayer、Media3(EXOPlayer)、MediaPlayer及AliPlayer多种内核。截至2024年8月,其GitHub星标数达2万。集成时需使用新版Android Studio,并按特定步骤配置依赖与权限。提供了NormalGSYVideoPlayer、GSYADVideoPlayer及ListGSYVideoPlayer三种控件,支持HLS、RTMP等多种直播链接。
95 18
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
|
29天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
81 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
EasyPusher是一款国产RTSP直播录制推流客户端工具,支持Windows、Linux、Android及iOS等系统。尽管其GitHub仓库(安卓版:https://github.com/EasyDarwin/EasyPusher-Android)已多年未更新,但通过一系列改造,如升级SDK版本、迁移到AndroidX、指定本地NDK版本及更新Gradle版本等,仍可在最新Android Studio上运行。以下是针对Android Studio Dolphin版本的具体改造步骤。
59 3
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher

热门文章

最新文章