数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
OpenGL线纹理函数




OpenGL表面纹理函数
OpenGL体纹理函数
OpenGL纹理图案的颜色选项
OpenGL纹理映射选项

OpenGL纹理环绕
复制帧缓存中的OpenGL纹理图案
OpenGL纹理坐标数组


OpenGL纹理图案命名
OpenGL纹理子图案
OpenGL纹理边界
OpenGL代理纹理
多媒体
2018-11-15 23:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
纹理映射
线性纹理图案

表面纹理图案


体纹理图案

纹理缩减图案
过程式纹理映射方法
多媒体
2018-11-15 22:36:01
「深度学习福利」大神带你进阶工程师,立即查看>>>
OpenGL点光源函数
指定一个OpenGL光源位置和类型

指定OpenGL光源颜色
指定OpenGL光源的辐射强度衰减系数
OpenGL方向光源

OpenGL全局光照参数

OpenGL表面特性函数

OpenGL光照模型
OpenGL雾气效果

OpenGL透明函数

OpenGL表面绘制函数

OpenGL半色调操作
多媒体
2018-11-15 18:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>> #include #include using namespace cv; int main(int argc, char** argv ) { if ( argc != 2 ) { printf("usage: DisplayImage.out \n"); return -1; } Mat image; image = imread( argv[1], 1 ); if ( !image.data ) { printf("No image data \n"); return -1; } namedWindow("Display Image", WINDOW_AUTOSIZE ); imshow("Display Image", image); waitKey(0); return 0; } #!/usr/bin/env python import cv2 print(cv2.__version__) import numpy as np img=cv2.imread("a.png",cv2.IMREAD_COLOR) cv2.imshow(" this is test",img) cv2.waitKey(0) cv2.waitKey(27) #cv2.waitKey(VK_ESCAPE) #%hist -f DisplayImage.py
多媒体
2018-11-09 21:04:00
「深度学习福利」大神带你进阶工程师,立即查看>>> package com.product.utils; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.net.URLConnection; /** * 图片边框类 * * @author 剑来V * */ public final class ImageBorderUtil { /** * * @param url * 需要加边框的图片URL地址 * @param pressImg * 边框图片的存放路径 * @param targetImg * 图片的路径 * @param handleImg * 新处理的图片路径 */ public static void pressImage(String url, String pressImg, String targetImg, String handleImg) { try { int maxLength = 0;// 求出绘制的正方形长度 int direction = 0;// 默认 0正方形 1横向长方形 2纵向长方形 // 目标文件 File _file = toFile(url, new File(targetImg)); Image src = ImageIO.read(_file); int width = src.getWidth(null); int height = src.getHeight(null); // 边框文件 File _filebiao = new File(pressImg); Image src_biao = ImageIO.read(_filebiao); if (width == height) { maxLength = width; } else if (width - height > 0) { maxLength = width; direction = 1; } else { maxLength = height; direction = 2; } // 调整坐标 int x = 0, y = 0; switch (direction) { case 1:// 横向长方形 y = (maxLength - height) / 2; break; case 2:// 纵向长方形 x = (maxLength - width) / 2; break; } BufferedImage image = new BufferedImage(maxLength, maxLength, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.setBackground(Color.WHITE);// 设置白色背景填充 g.clearRect(0, 0, maxLength, maxLength);// 重置画布进行填充 g.drawImage(src, 0 + x, 0 + y, width, height, null);// 底层画布 g.drawImage(src_biao, 0, 0, maxLength, maxLength, null);// 边框PNG图片覆盖 g.dispose(); // 直接修改源文件 // FileOutputStream out = new FileOutputStream(targetImg); // JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); // encoder.encode(image); // out.flush(); // out.close(); // 生成新的文件 File sf = new File(handleImg); ImageIO.write(image, "jpg", sf); // 保存图片 } catch (Exception e) { e.printStackTrace(); } } /** * 文件保存 * * @param link * 图片链接 * @param file * 存入的文件地址 * @return 下载的文件 */ public static File toFile(String link, File file) { try { URL url = new URL(link); URLConnection uri = url.openConnection(); // 获取数据流 InputStream ins = uri.getInputStream(); OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); return file; } catch (Exception e) { return null; } } public static void main(String[] args) { pressImage("http://imgsrc.baidu.com/imgad/pic/item/7af40ad162d9f2d3dd3f80d6a2ec8a136327ccd9.jpg", "D:\\imgin\\99-FS.png", "D:\\imgout\\test111111111.jpg", "D:/imgout/" + "test2018" + "." + "jpg"); System.out.println("结束"); } }
多媒体
2018-11-07 09:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
WebM 常常用于很多网站背景的动态视频效果
开放、免费、开源、基于 HTML5
WebM 由 Google 提出,是一个开放、免费的媒体文件格式。
WebM 标准的网络视频更加偏向于开源并且是基于HTML5标准的。
基于 MKV 容器格式,内含 VP8 影片轨和 Ogg Vorbis 音轨
WebM 影片格式其实是以 Matroska(即 MKV)容器格式为基础开发的新容器格式,里面包括了 VP8 影片轨和 Ogg Vorbis 音轨,
其中 Google 将其拥有的 VP8 视频编码技术以类似 BSD 授权开源,Ogg Vorbis 本来就是开放格式。
多媒体
2018-11-01 11:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
写在前面:
你是否注意到我最近特别勤奋了,每周一篇技术博客了?
天真了,那是因为我们一小家子定了个规矩,每周要发一篇,不然要罚款嘛!
而且这个倡议还是我提的 ̄□ ̄||
是滴,这又是一篇水贴,2015年的时候发在自己QQ空间里的老东西。硕士小论文需要一幅带有乘性噪声的SAR图像作为实验用图。
上周头痛,这周又要赶两篇AR专利,实在没时间啊!
好了,废话完了,开始不长的正文。
正文:
别人的论文里算法测试用图是仿真的SAR图像,清楚地知道哪里是正确检测到的目标,哪里是没有正确检测到的目标,而自己又没有这种图,所以寻思自己做一幅。想了想,做图像测试之类的还是Matlab好做,于是研究了一小下,开始动手。我需要测试的算法是SAR图像的边缘检测,需要类似于别人的:
这幅图是深浅相间的条带图,条带宽度由左及右从2递增到18个象元,加上SAR图像特有的乘性噪声之后,就是我需要的图(b)了。于是,我用Matlab写了两个函数:incstripe和raylspeckle,分别用来生成递增宽度的条带和添加瑞利分布的乘性随机噪声。

然后,写一个runme的执行程式:
就得到所需的仿真SAR图像了:

多媒体
2018-10-28 21:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
所谓图片元数据,就是除了我们肉眼看到的图片内容外,隐藏在这些内容背后的一些技术数据。
本文介绍如何使用Java代码将一张图片的隐藏信息读取出来。
首先不需要下载任何额外的Java库,用JDK自带的库就能工作。 import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataNode; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.sun.imageio.plugins.png.PNGMetadata; 新建一个Java类,这个类的main方法也是非常直接的: static public void main(String[] arg) throws IOException{ byte[] content = getContent("C:
多媒体
2018-10-28 16:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>> package com.product.utils; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; import org.apache.commons.lang.StringUtils; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.net.URLConnection; @SuppressWarnings("restriction") public final class ImageUtil { /** * 给图片+图片水印 * * @param url --url 地址 * @param pressImg -- 水印图片 * @param targetImg -- 目标文件 * @param location 水印位置:left-top:左上角,right-top:右上角,left-bottom:左下角,right-bottom:右下角 * @param degree 水印旋转角度 */ public static void pressImage(String url, String pressImg, String targetImg, String location, Integer degree) { try { // 目标文件 File _file = toFile(url, new File(targetImg)); Image src = ImageIO.read(_file); int width = src.getWidth(null); int height = src.getHeight(null); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.drawImage(src, 0, 0, width, height, null); // 水印文件 File _filebiao = new File(pressImg); Image src_biao = ImageIO.read(_filebiao); int width_biao = src_biao.getWidth(null); int height_biao = src_biao.getHeight(null); // 水印坐标 int x = 0, y = 0; if (StringUtils.equals(location, "left-top")) { x += 30; y += 30; } else if (StringUtils.equals(location, "right-top")) { x = width - width_biao - 30; y += 30; } else if (StringUtils.equals(location, "left-bottom")) { y = height - height_biao - 30; x += 30; } else if (StringUtils.equals(location, "right-bottom")) { x = width - width_biao - 30; y = height - height_biao - 30; } else { x = (width - width_biao) / 2; y = (height - height_biao) / 2; } if (null != degree) { // 设置水印旋转 g.rotate(Math.toRadians(degree), x, y); } g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.drawImage(src_biao, x, y, width_biao, height_biao, null); // 水印文件结束 g.dispose(); //直接修改源文件 FileOutputStream out = new FileOutputStream(targetImg); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); encoder.encode(image); out.flush(); out.close(); //生成新的文件 //File sf = new File("D:/imgout/" + "test" + "." + "jpg"); //ImageIO.write(image, "jpg", sf); // 保存图片 } catch (Exception e) { e.printStackTrace(); } } /** * 给图片加文字水印 * * @param url 图片的网络地址 * @param pressText s水印文字 * @param targetImg 目标文件 * @param fontName 字体名称 * @param fontStyle 字体风格 * @param fontSize 字体大小 * @param location 字体位置:left-top:左上角,right-top:右上角,left-bottom:左下角,right-bottom:右下角 */ public static void pressText(String url, String pressText, String targetImg, String fontName, int fontStyle, int fontSize, String location,Color color) { try { int textWidth = getFontWidth(fontName, fontStyle, fontSize, pressText); //File _file = new File(targetImg); File _file = toFile(url, new File(targetImg)); Image src = ImageIO.read(_file); int width = src.getWidth(null); int height = src.getHeight(null); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); g.setColor(color); g.setFont(new Font(fontName, fontStyle, fontSize)); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.45f)); int x = 0, y = 0; if (StringUtils.equals(location, "left-top")) { x = 30; y = 30; } else if (StringUtils.equals(location, "right-top")) { x = width - textWidth - 30; y = 30; } else if (StringUtils.equals(location, "left-bottom")) { x += 30; y = height - 30; } else if (StringUtils.equals(location, "right-bottom")) { x = width - textWidth - 30; y = height - 30; } else { x = (width - textWidth) / 2; y = (height) / 2; } g.drawString(pressText, x, y); g.dispose(); FileOutputStream out = new FileOutputStream(targetImg); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); encoder.encode(image); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 文件保存 * * @param link 图片链接 * @param file 存入的文件地址 * @return 下载的文件 */ public static File toFile(String link, File file) { try { URL url = new URL(link); URLConnection uri = url.openConnection(); //获取数据流 InputStream ins = uri.getInputStream(); OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); return file; } catch (Exception e) { return null; } } /** * 计算文本占用的width * * @param fontName 字体名称 * @param fontStyle 字体风格 * @param fontSize 字体大小 * @param pressText 输入文本 * @return 文字所占用的宽带 */ public static int getFontWidth(String fontName, int fontStyle, int fontSize, String pressText) { Font f = new Font(fontName, fontStyle, fontSize); FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(f); // 高度 //System.out.println(fm.getHeight()); // 单个字符宽度 //System.out.println(fm.charWidth('A')); // 整个字符串的宽度 //System.out.println(fm.stringWidth(pressText)); return fm.stringWidth(pressText); } public static void main(String[] args) { //pressImage("https://cbu01.alicdn.com/img/ibank/2017/041/711/4771117140_1239574879.jpg", "D:/imgin/20181017110944.png", "D:/imgout/x2.jpg", "left-bottom", null);// //pressImage("D:/imgin/20181017110944.png", "D:/imgout/1.png", "right-top", null); //pressImage("D:/imgin/20181017110944.png", "D:/imgout/1.png", "center", null); //pressImage("D:/imgin/20181017110944.png", "D:/imgout/1.png", "left-bottom", null); //pressImage("D:/imgin/20181017110944.png", "D:/imgout/1.png", "right-bottom", null); //pressText("https://cbu01.alicdn.com/img/ibank/2017/041/711/4771117140_1239574879.jpg", "miraclad发送到发烧是打发打发esgrocery", "D:/imgout/r1.jpg", "黑体", Font.BOLD + Font.ITALIC, 30, "right-top",Color.GRAY); //getFontWidth("黑体", Font.BOLD + Font.ITALIC, 30, "miraclesgrocery"); //System.out.println(Color.RED); //System.out.println(Color.WHITE); //System.out.println(Color.GRAY); } }
多媒体
2018-10-27 15:56:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如果需要压缩一个很大的视频,网上有很多介绍,但是都需要安装各种视频处理软件,比如格式工厂等等。本文介绍一个非常方便的小技巧,无需任何视频处理软件,只需要微软的Powerpoint,这个最常用的办公软件相信大家电脑上都有安装。
下面是详细步骤。
1. 创建一个空的powerpoint:
直接把要压缩的视频文件用ctrl+c拷贝进来:
2. 点Powerpoint的菜单Info->Compress Media:
有几种压缩率可供选择。我就选压缩率最高,画质最低的Standard (480p):
选择之后,Powerpoint的视频压缩就开始了。可以看到进度条:
压缩完毕,提示我们压缩后节省了1GB的空间:
此时可以将压缩后的视频另存到本地了:
要获取更多Jerry的原创文章,请关注公众号"汪子熙":
多媒体
2018-10-26 13:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
网易云音乐上有很多适合程序猿的歌单,但是今天文章介绍的不是这些适合程序员工作时听的歌,而是一个用Python开发的开源播放器,专门适用于网易云音乐的播放。这个播放器的名称为MusicBox, 特色是用命令行版本执行音乐的播放。
github地址:
https://github.com/darknessomi/musicbox
将该仓库clone到本地,执行安装脚本setup.py install即可安装。
执行musicbox即可进入命令行模式。
这个命令行工具的一些功能:查看网易云音乐的排行榜和云音乐新歌榜:
播放选中的歌曲,并显示实时播放进度:

可以根据歌手名搜索想听的歌曲:
新碟上架:
这个播放器没有广告,非常清爽,适合程序员使用。

要获取更多Jerry的原创文章,请关注公众号"汪子熙":
多媒体
2018-10-26 13:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
导入 >> import cv2 >> import numpy as np
读图片 >> image_arr = cv2.imread('file_path')
灰度图扩展成彩色图 可以通过图片的channel判断是否是灰度图。如果需要可以将灰度图扩展到RGB的彩色图(复制灰度图的数据到各通道) >> if image_arr.shape[2] == 1: image_arr_rgb = cv2.cvtColor(image_arr, cv2.COLOR_GRAY2RGB)
彩色图像素存储格式 imread 读的彩色图按照BGR像素存储,如果转换成RGB则需要用cvtColor函数进行转换 >> image_arr_rgb = cv2.cvtColor(image_arr, cv2.COLOR_BGR2RGB)
图片size存储格式 imread 读的图片按照 H,W,C 格式存储 >> image_arr_rgb.shape (H, W, C) H,W,C格式转换到C,H,W格式 >> image_arr_rgb_chw = np.transpose(image_arr_rgb, (2,0,1))
多媒体
2018-10-22 11:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
当然你可以直接用现成的虚拟摄像头软件实现这个功能。不过当初我开发这个插件的原因是,需要在Flash产品里面共享桌面,如果此时需要引导用户安装一个第三方的虚拟摄像头体验不好,所以公司希望我自己开发一个虚拟摄像头,一键安装减少用户的使用门槛。所谓的虚拟摄像头实际上在windows系统上注册了一个特殊dll,这个dll是一个COM组件。
虚拟摄像头需要用到Direct Show编程。
下载Direct Show开发代码
里面有如下的文件夹,我只需要用到第一个文件夹里面的代码—— baseclasses baseclasses capture common dmo dvd filters misc players vmr9
创建工程
打开Visual Studio ,新建一个win32 Dll项目。 打开属性页,在VC++ 目录一栏中的库目录里面添加刚才的baseclasses的路径,这样我们就能在项目中引用这个目录里的代码了。 在C/C++属性页里面的附加库目录里面也把baseclasses的路径填入。 在dll.cpp中,我们需要把Filter注册成COM组件。 分别需要调用AMovieSetupRegisterServer函数、CreateComObject函数以及IFilterMapper2接口的RegisterFilter函数完成注册。
主逻辑
在头文件中,我们需要声明两个类 class CVCamStream; class CVCam : public CSource { public: ////////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////////// static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr); STDMETHODIMP QueryInterface(REFIID riid, void **ppv); IFilterGraph *GetGraph() {return m_pGraph;} static int cx, cy; static HANDLE SocketThread; static SOCKET ClientSocket; private: CVCam(LPUNKNOWN lpunk, HRESULT *phr); }; class CVCamStream : public CSourceStream, public IAMDroppedFrames,public IAMStreamConfig, public IKsPropertySet { public:
COM组件只需要实现CUnknown接口即可。我们继承了Direct Show的CSource类,那么就已经实现了这个接口。 CVCamStream类用来实现图像数据的输出。 在CVCam的构造函数里面我们创建CVCamStream类的实例 m_paStreams = (CSourceStream **) new CVCamStream*[1]; m_paStreams[0] = new CVCamStream(phr, this, L"Flex COM");
在实现COM接口的QueryInterface函数中,我们调用了CVCamStream类的QueryInterface HRESULT CVCam::QueryInterface(REFIID riid, void **ppv) { //Forward request for IAMStreamConfig & IKsPropertySet to the pin if(riid == _uuidof(IAMStreamConfig) || riid == _uuidof(IAMDroppedFrames) || riid == _uuidof(IKsPropertySet)) return m_paStreams[0]->QueryInterface(riid, ppv); else return CSource::QueryInterface(riid, ppv); }
定义媒体类型 HRESULT CVCamStream::GetMediaType(int iPosition, CMediaType *pmt)
在这个函数中,我们配置了媒体的具体的格式参数,比如24位RGB格式,图像的宽高等等。 另外还需要对GetStreamCaps函数进行实现,配置媒体的格式。 HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
捕获桌面
系统会调用FillBuffer函数,在这个函数中,我们将捕获到的数据填充到缓冲里面,Direct Show会处理剩下的事情。 HRESULT CVCamStream::FillBuffer(IMediaSample *pms)
捕获桌面只需要用到一个函数CopyScreenToBitmap HANDLE hDib = CopyScreenToBitmap(&ScreenRect, pData, (BITMAPINFO *)&(pVih->bmiHeader), m_hCursor); if (hDib) DeleteObject(hDib);
pData是我们定义的一个指针,通过下面的代码,我们的pData就指向了缓存,数据填充到pData指向的内存中。 BYTE *pData; pms->GetPointer(&pData);
进阶
实际产品会有很多需求,光实现捕获桌面是远远不够的,我们需要对这个捕获进行控制,比如捕获制定区域,停止捕获,恢复捕获等等。那么就涉及到和COM进行通讯了。 我们可以通过VS的窗口设计器创建一个windows窗口,然后提供一个用户操作界面。 如何响应这个窗口的用户操作呢? 通过windows消息 INT_PTR CALLBACK WindowMessage(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
当然最关键是需要在Flash产品的程序里面唤起这个窗口,需要用的socket编程。 SOCKET Listen_Sock = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN serverAddr; ZeroMemory((char *)&serverAddr, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(1234); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(Listen_Sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); listen(Listen_Sock, 5);
C++的socket编程十分的繁琐,其他语言都会进行封装,让开发变的十分便利。 在一个线程里面写个死循环进行读取socket的数据,这是比较初级的多线程阻塞式的socket编程。对于我们这个程序是绰绰有余了。毕竟不是服务器,不需要面对并发的问题。
源码
https://github.com/langhuihui/FlexCOM
多媒体
2018-10-21 14:38:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.安装相关依赖: yum install -y gcc libpng libjpeg libpng-devel libjpeg-devel ghostscript libtiff libtiff-devel freetype freetype-devel
2.下载并解压到目录/usr/local/ wget ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/1.3/GraphicsMagick-1.3.29.tar.gz tar -zxvf GraphicsMagick-1.3.29.tar.gz
3.编译并安装 cd /usr/local/GraphicsMagick-1.3.29 ./configure --prefix=/usr/local/GraphicsMagick-1.3.29 make make install
4.设置环境变量 vi /etc/profile
在文件的后边追加 GM_HOME=/usr/local/GraphicsMagick-1.3.29 PATH=$GM_HOME/bin:$PATH export PATH CLASSPATH GM_HOME
保存后执行,使配置生效 source /etc/profile
5.检查是否安装成功
用命令: convert -version
会显示: Version: ImageMagick 6.7.2-7 2017-03-22 Q16 http://www.imagemagick.org Copyright: Copyright (C) 1999-2011 ImageMagick Studio LLC Features: OpenMP
用命令 gm version
会显示: GraphicsMagick 1.3.29 2018-04-29 Q8 http://www.GraphicsMagick.org/ Copyright (C) 2002-2018 GraphicsMagick Group. Additional copyrights and licenses apply to this software. See http://www.GraphicsMagick.org/www/Copyright.html for details. Feature Support: Native Thread Safe yes Large Files (> 32 bit) yes Large Memory (> 32 bit) yes BZIP yes DPS no FlashPix no FreeType yes Ghostscript (Library) no JBIG no JPEG-2000 yes JPEG yes Little CMS no Loadable Modules no OpenMP yes (200805) PNG yes TIFF yes TRIO no UMEM no WebP no WMF no X11 yes XML no ZLIB yes Host type: x86_64-unknown-linux-gnu Configured using the command: ./configure '--prefix=/usr/local/GraphicsMagick-1.3.29' Final Build Parameters: CC = gcc -std=gnu99 CFLAGS = -fopenmp -g -O2 -Wall -pthread CPPFLAGS = -I/usr/include/freetype2 CXX = g++ CXXFLAGS = -pthread LDFLAGS = LIBS = -ltiff -lfreetype -ljasper -ljpeg -lpng12 -lXext -lSM -lICE -lX11 -lbz2 -lz -lm -lgomp -lpthread
6.im4java 代码设置CMD路径为
/usr/local/GraphicsMagick-1.3.29/bin
多媒体
2018-10-18 16:49:01
「深度学习福利」大神带你进阶工程师,立即查看>>>
音视频的格式是一个有歧义的说法。我们熟知的诸如Flv、Mp4、Mov啥的都是包装格式,可以理解为一种容器,就像一个盒子。里面放到是经过编码的音视频数据,而这些音视频数据都有自己的编码格式,如AAC、H264、H265等等。 今天要展示的是从直播流中获取到的音频编码数据进行解码并使用H5的音频API进行播放的过程。
这些格式分别是 speex aac mp3
这些格式都有开源的解码库,不过都是c库,在H5中需要通过emscripten编译成js执行。
引入头文件 #ifdef USE_SPEEX #include #endif #ifdef USE_AAC #include "aacDecoder/include/neaacdec.h" // #include "libfdk-aac/libAACdec/include/aacdecoder_lib.h" #endif #ifdef USE_MP3 #include "libmad/mad.h" //#include "libid3tag/tag.h" #endif
定义变量 int bufferLength; int bufferFilled; u8 *outputBuffer; #ifdef USE_AAC faacDecHandle faacHandle; #endif #ifdef USE_SPEEX i16 *audioOutput; void *speexState; SpeexBits speexBits; #endif #ifdef USE_MP3 MP3Decoder mp3Decoder; #endif
bufferLength 用于指定缓冲区的长度,bufferFilled用于指示缓冲中没有使用的数据,outputBuffer用来存放解码后的数据。 MP3Decoder是自己写的一个类,需要定义这几个成员 mad_stream inputStream; mad_frame frame; mad_synth synth;
初始化 outputBuffer = (u8 *)malloc(bufferLength); #ifdef USE_SPEEX audioOutput = (i16 *)malloc(640); auto mode = speex_lib_get_mode(SPEEX_MODEID_WB); speexState = speex_decoder_init(mode); speex_bits_init(&speexBits); #endif #ifdef USE_AAC faacHandle = faacDecOpen(); #endif
mp3的初始化 mad_stream_init(&inputStream); mad_frame_init(&frame); mad_synth_init(&synth);
解码
input对象中包含了经过协议拆包后的原始音频数据(RTMP协议或Flv格式中的格式)缓冲大小虽然是自己定义,但必须遵循下面的规则 aac:1024的倍数(AAC一帧的播放时间是= 1024 1000/44100= 22.32ms) speex:320的倍数(320 1000/16000 = 20ms) MP3:576的倍数(双声道1152 * 1000 /44100 = 26.122ms) 根据这些数据可以估算缓冲大小引起的音频的延时,然后需要和视频的延迟进行同步。 #ifdef USE_SPEEX if (input.length() <= 11) { memset(output, 0, 640); } else { speex_bits_read_from(&speexBits, (const char *)input, 52); speex_decode_int(speexState, &speexBits, audioOutput); memcpy(output, audioOutput, 640); } return 640; #endif #ifdef USE_AAC //0 = AAC sequence header ,1 = AAC raw if (input.readB<1, u8>()) { faacDecFrameInfo frame_info; auto pcm_data = faacDecDecode(faacHandle, &frame_info, (unsigned char *)input.point(), input.length()); if (frame_info.error > 0) { emscripten_log(1, "!!%s\n", NeAACDecGetErrorMessage(frame_info.error)); } else { int samplesBytes = frame_info.samples << 1; memcpy(output, pcm_data, samplesBytes); return samplesBytes; } } else { unsigned long samplerate; unsigned char channels; auto config = faacDecGetCurrentConfiguration(faacHandle); config->defObjectType = LTP; faacDecSetConfiguration(faacHandle,config); faacDecInit2(faacHandle, (unsigned char *)input.point(), 4, &samplerate, &channels); emscripten_log(0, "aac samplerate:%d channels:%d", samplerate, channels); } #endif
mp3 比较复杂,这里不贴代码了,主要是mad库不能直接调用其提供的API,直播流中的MP3数据和mp3文件的格式有所不同导致。如果本文火的话,我就详细说明。
释放资源 #ifdef USE_AAC faacDecClose(faacHandle); #endif #ifdef USE_SPEEX speex_decoder_destroy(speexState); speex_bits_destroy(&speexBits); free(audioOutput); #endif free(outputBuffer);
mp3 mad_synth_finish(&synth); mad_frame_finish(&frame);
播放
创建AudioContext对象 window.AudioContext = window.AudioContext || window.webkitAudioContext; var context = new window.AudioContext();
创建audioBuffer var audioBuffers = [] var audioBuffer = context.createBuffer(channels, frameCount, samplerate);
播放音频(带缓冲) var playNextBuffer = function() { isPlaying = false; if (audioBuffers.length) { playAudio(audioBuffers.shift()); } if (audioBuffers.length > 1) audioBuffers.shift(); //console.log(audioBuffers.length) }; var copyAudioOutputArray = resampled ? function(target) { for (var i = 0; i < allFrameCount; i++) { var j = i << 1; target[j] = target[j + 1] = audioOutputArray[i] / 32768; } } : function(target) { for (var i = 0; i < allFrameCount; i++) { target[i] = audioOutputArray[i] / 32768; } }; var copyToCtxBuffer = channels > 1 ? function(fromBuffer) { for (var channel = 0; channel < channels; channel++) { var nowBuffering = audioBuffer.getChannelData(channel); if (fromBuffer) { for (var i = 0; i < frameCount; i++) { nowBuffering[i] = fromBuffer[i * (channel + 1)]; } } else { for (var i = 0; i < frameCount; i++) { nowBuffering[i] = audioOutputArray[i * (channel + 1)] / 32768; } } } } : function(fromBuffer) { var nowBuffering = audioBuffer.getChannelData(0); if (fromBuffer) nowBuffering.set(fromBuffer); else copyAudioOutputArray(nowBuffering); }; var playAudio = function(fromBuffer) { if (isPlaying) { var buffer = new Float32Array(resampled ? allFrameCount * 2 : allFrameCount); copyAudioOutputArray(buffer); audioBuffers.push(buffer); return; } isPlaying = true; copyToCtxBuffer(fromBuffer); var source = context.createBufferSource(); source.buffer = audioBuffer; source.connect(context.destination); source.onended = playNextBuffer; //setTimeout(playNextBuffer, audioBufferTime-audioBuffers.length*200); source.start(); };
其中playNextBuffer 函数用于从缓冲中取出数据 copyAudioOutputArray 函数用于将音频数据转化成浮点数。 copyToCtxBuffer 函数用于将音频数据拷贝进可以播放的缓冲数组中。 这些函数对单声道和双声道进行了处理 var resampled = samplerate < 22050;
对于频率小于22khz的数据,我们需要复制一份,模拟成22khz,因为H5只支持大于22khz的数据。
多媒体
2018-10-18 08:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如题 。——这是一篇工作日志。
今天基本上没写代码,我感觉整个身体的细胞都活跃了一些。干了件啥事儿呢?去年在服务器和地面站上都搞了个全中国的DEM数据服务,方便用户在线和离线查询高程,也方便一些航线规划算法的实现,比如“仿地飞行”啦之类的。这两年销售和技服陆陆续续打出国门,开始服务国外客户了,中国区域的DEM数据不够用了,最近韩国和马拉西亚卖出去的无人机配套服务都需要这玩意儿。所以,今天应同事要求先把这两个区域的DEM数据制备了。DEM数据当然用的SRTM_GL1,目前30m分辨率下我觉得质量最好的一款免费产品。N多种数据下载福利链接看这里: 圣地亚哥超级计算中心的教育资源 。
工作内容很简单,但为什么决定说一说呢?——因为我翻出以前用IDL写的批处理小程序时,那洋洋得意之情需要宣泄啊!(*^▽^*)
说起IDL( Interactive Data Language )这门语言(有两种IDL,我说的是交互式数据语言),啧啧啧,真是不堪回首的东西。曾几何时的研究生阶段,自学了一学期的IDL。我倾心于它那优异的运算性能和庞大的数学和图像计算库,冀望于用它搞出基于我自研的 SAR图像边缘检测算法IROEWA&NMS 的应用软件,但从入门到放弃。那时国内就仅有一本中文教程,买来一看那简陋得就只剩语法了。然鹅,语法还很不C系,写起来超级别扭;处理事件的消息泵只能放在每篇代码末尾,特别不自由,学习了一下和CSharp的混合编程最后也放弃了。
其实IDL是一门很强大的语言,怎么说呢?它和Matlab一样,是科学计算语言,根子里的世界观都是“矩阵”,强大的数学和图像计算库,非常适合搞算法实现和仿真(听说现在很火的是Python?);但我当初用它搞图形界面的应用软件,毫无意外会很痛苦。如果你和我一样是遥感或GIS专业,应该对它有所了解或者耳闻。遥感学界强大的ENVI软件,就是用IDL写的,并且IDL是其官方的二次开发语言,IDL和ENVI的库经常搅在一起傻傻分不清,编程模式也分为纯IDL和IDL+ENVI等等。用IDL最多的人应该主要集中在——NASA和美国国家地质局。总之,Matlab有的优点它基本都有,它还比Matlab小而且快。我曾经还对比过OpenCV和IDL实现的Canny算子,论起检测效果和速度来说OpenCV被爆了菊花,——鹅,我的意思是测试用图是一朵菊花。
研究生毕业以后,我算是离开这个恶魔很久了。但是自去年搞DEM数据服务,IDL又瞬间变成了天使,对于空间栅格数据的各种批处理,真的是又快又能干,需要转个格式或者加个高程异常修正啥的,短短几行代码搞定。说到这儿,我就把DEM数据HGT转GeoTIFF文件格式的批处理小程序放出来分享分享吧!
博客编辑器没有提供这门语言的代码着色,只好凑合一下: PRO ENVI_BATCH_HGT2TIFF compile_opt IDL2 ; ; First restore all the base save files. ; envi, /restore_base_save_files ; ; Init ENVI batch, and set the log file ; envi_batch_init, log_file = 'batch_hgt2tif.log' ; ; Turn off the status window and do the same ; envi_batch_status_window, /off ; ; Open the input files ; files = envi_pickfile(FILTER = '*.hgt', /MULTIPLE_FILES, /NO_CHANGE) print, files print, n_elements(files) ; ; Set the saving folder ; out_dir = envi_pickfile(/DIRECTORY, /NO_CHANGE) print, out_dir ; ; Convert hgt to tiff ; for i = 0, n_elements(files) - 1 do begin envi_open_file, files[i], r_fid = fid envi_file_query, fid, dims = dims, nb = nb if (fid eq -1) then begin print, 'miss:' + files[i] + '!' continue endif out_name = out_dir + '\' + file_basename(files[i], '.hgt') + '.tif' print, out_name envi_output_to_external_format, out_name = out_name, fid = fid, dims = dims, pos = indgen(nb), /TIFF endfor print, 'done!' ; ; Exit ENVI Batch ; envi_batch_exit END
如你所见:①是的,IDL大小写不敏感,并且分号是注释符号;②函数(FUNCTION)和程式(PRO)有两类参数——占位参数Arguments和关键字参数Keywords,占位参数通过在参数表中的确定位置来赋值,而形如FILTER = '*.hgt'的是关键字参数,这里是给FILTER关键字参数赋值,而不是C系的参数默认值语法;③形如/KEYWORD的是一类特殊的关键字参数,可以理解成KEYWORD=1,主要用于某些可选功能的“开”和“闭”;④关键字参数都是顺序无关、位置无关的,甚至是可以缩写的(这要求同一函数或程式中任何关键字不等于或部分包含其他关键字),因此IDL的参数表是可变的,不像C系语法是强签名;⑤关键字参数还有一个特性,那就是如果你给一个关键字参数赋以外部变量,那么程式内部改变关键字参数的值,外部变量的值跟着改变,这相当于C系的引用传参;⑥调用程式的语法是“程式名称, 参数表“,而调用函数的语法是“返回值=函数名称(参数表)”;⑦另外函数和程式的代码文件后缀都是.pro,和Qt工程文件一样,呵呵~
工作流程很简单:
先在ArcGIS里添加了一个世界街道地图作为底图,用1°×1°的网格划分一下,把完全覆盖目标区域的网格左下角经纬度读出来,组成形如“N03E110”的文件名,去前面介绍的网站的对应目录下载对应文件(当然要下的数据量大的话可以自己写个爬虫,我这里就没有必要了,都是些小国家)。

下载完数据之后是HGT格式的数据,这个格式只有专业遥感和GIS软件才能打开,运行一下我的IDL批处理小程序,转成大家都能接受的TIFF格式(实际上是GeoTIFF,并带有附属文件WorldFile(.tfw)存着仿射变换参数)——控制台回车,多选HGT文件,选择一个文件夹存放转出的*.tif文件,然后小酌一口咖啡吧!
最后在ArcGIS里看一下,没有转换损坏的文件,就OK啦!
多媒体
2018-10-13 11:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
pjsip同时进行多路通话, 每路通话都能指定使用不同的mic和喇叭,搞它?
二话不说,抄代码: HHow can I use multiple sound devices simultaneously? You can use multiple audio devices simultaneously with PJSUA-LIB. The first sound device is managed by PJSUA-LIB as usual, and you access it as slot #0 in the conference bridge. The following steps describe how to open and use the second and subsequent sound devices in your application: Create a ​sound device port for the sound device you want to use. pjmedia_snd_port *sndport; pj_status_t status; status = pjmedia_snd_port_create(..., &sndport); Create a ​splitter/combiner port. pjmedia_port *splitcomb; status = pjmedia_splitcomb_create(..., &splitcomb); Create a reverse channel from the splitcomb. pjmedia_port *revch; status = pjmedia_splitcomb_create_rev_channel(pool, splitcomb, 0, 0, &revch); Register the reverse channel above to the conference bridge. int slot; status = pjsua_conf_add_port(pool, revch, &slot); Connect the sound device port to the splitcomb. status = pjmedia_snd_port_connect(sndport, splitcomb); After these, you can use the sound device (that you open in step 1 above) by using the slot number (step 4) just as you would with other media ports. For example, to play a media to the sound device, just connect (with pjsua_conf_connect()) the slot number of that media to the slot number of the sound device, and vice versa. 原文链接 https://trac.pjsip.org/repos/wiki/FAQ#multi-snd
多媒体
2018-10-05 16:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
pjsip默认在没有mic的时候基本无法拨号,就算接通呼入也是没有音频的。
要搞它?
搞吧。
接入null device 又是一种现象, 不东写出来了。 #if 1 if (mic_cnt == 0) { //没有mic就使用null_device showStatus("set null-device"); //status = pjsua_set_null_snd_dev(); pjmedia_port *media_port = pjsua_set_no_snd_dev(); pjmedia_snd_port *snd_port; status = pjmedia_snd_port_create_player(g_pool, -1, pjsua_var.media_cfg.clock_rate, pjsua_var.mconf_cfg.channel_count, pjsua_var.mconf_cfg.samples_per_frame, pjsua_var.mconf_cfg.bits_per_sample, 0, &snd_port); if (status != PJ_SUCCESS) { showStatus("pjmedia_snd_port_create_player faild."); return; } pjmedia_snd_port_connect(snd_port, media_port); if (status != PJ_SUCCESS) { showStatus("pjmedia_snd_port_connect faild."); return; } } #endif
多媒体
2018-10-05 16:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
https://www.cnblogs.com/xiaodingmu/p/6055042.html
多媒体
2018-09-29 17:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一般做视频监控的业务的人,经常访问摄像头,很多版本的摄像头用的是activeX控件播放视频。
使用电脑浏览某些网站或者系统的时候,有时需要进行更加复杂化的交互,需要 浏览器支持ActiveX插件的功能 。进行安装这个插件的时候,由于电脑的配置安全性比较高,安装不会出成功,但是为了电脑安全,有些设置的安全级别比较高,这就会导致不能启动这个插件,小编介绍下 如何更改这个优先级 。 首先,打开电脑上桌面上的浏览器按钮, 双击打开, 启动浏览器 。只能在浏览器打开的状态下才可以进行相应的设置找到任何一个浏览器都可以进行直接设置,设置成功之后,其他浏览器就会自动生效,。
在浏览器的最上面是 导航的工具栏 ,可以看到有个 工具按钮 ,优先级等问题都是在这个工具中进行设定导航栏工具一般在浏览器的顶部就可以看到工具选项,。
点击工具选项,在工具现象的最下面有一个 Internet选项 ,点击这里就可以设置一些浏览网页时的一些功能。其他浏览器可以通过网络搜索,找到对应的INTERNET,
进入Internet选项界面之后,点击第二个 安全按钮 ,进行对浏览器安全级别等信息的控制调节。通过调节安全按钮的级别,就可以控制安全,
5
选择 自定义级别 ,如果自己设置乱了,可以点击后面的默认级别,点击之后浏览器就 恢复到默认的级别 。恢复成默认浏览器设置,
6
在 自定义级别中,找到带有ActiveX控件的选项 ,根据自己的实际需要进行勾选相应的级别信息。 选择好之后,点击应用和确定 。
多媒体
2018-09-29 14:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>> #include "watcher.h" #include #include #include #include Watcher::Watcher(QWidget *parent) : QWidget(parent) { QStringList args=qApp->arguments(); QString path; //读取命令行指定的目录作为监听目录。 if(args.count()>1) { path=args[1]; } else //获取没有指定,监听当前目录 { path=QDir::currentPath(); } pathLabel = new QLabel; pathLabel->setText(tr("监视的目录:")+path); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(pathLabel); fsWatcher.addPath(path); connect(&fsWatcher,SIGNAL(directoryChanged(QString)),this,SLOT(directoryChanged(QString))); } Watcher::~Watcher() { } void Watcher::directoryChanged(QString path) { QMessageBox::information(NULL,tr("目录发生变化"),path); }
多媒体
2018-09-28 15:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>> #include "dialog.h" #include #include Dialog::Dialog(QWidget *parent) : QDialog(parent) { fileNameLabel = new QLabel(tr("文件名:")); fileNameLineEdit = new QLineEdit; fileBtn = new QPushButton(tr("文件")); sizeLabel = new QLabel(tr("大小:")); sizeLineEdit = new QLineEdit; createTimeLabel = new QLabel(tr("创建时间:")); createTimeLineEdit = new QLineEdit; lastModifiedLabel = new QLabel(tr("最后修改时间:")); lastModifiedLineEdit = new QLineEdit; lastReadLabel = new QLabel(tr("最后访问时间:")); lastReadLineEdit = new QLineEdit; propertyLabel = new QLabel(tr("属性:")); isDirCheckBox = new QCheckBox(tr("目录")); isFileCheckBox = new QCheckBox(tr("文件")); isSymLinkCheckBox = new QCheckBox(tr("符号连接")); isHiddenCheckBox = new QCheckBox(tr("隐藏")); isReadableCheckBox = new QCheckBox(tr("读")); isWritableCheckBox = new QCheckBox(tr("写")); isExecutableCheckBox = new QCheckBox(tr("执行")); getBtn = new QPushButton(tr("获得文件信息")); QGridLayout *gridLayout = new QGridLayout; gridLayout->addWidget(fileNameLabel,0,0); gridLayout->addWidget(fileNameLineEdit,0,1); gridLayout->addWidget(fileBtn,0,2); gridLayout->addWidget(sizeLabel,1,0); gridLayout->addWidget(sizeLineEdit,1,1,1,2); gridLayout->addWidget(createTimeLabel,2,0); gridLayout->addWidget(createTimeLineEdit,2,1,1,2); gridLayout->addWidget(lastModifiedLabel,3,0); gridLayout->addWidget(lastModifiedLineEdit,3,1,1,2); gridLayout->addWidget(lastReadLabel,4,0); gridLayout->addWidget(lastReadLineEdit,4,1,1,2); QHBoxLayout *layout2 = new QHBoxLayout; layout2->addWidget(propertyLabel); layout2->addStretch(); QHBoxLayout *layout3 = new QHBoxLayout; layout3->addWidget(isDirCheckBox); layout3->addWidget(isFileCheckBox); layout3->addWidget(isSymLinkCheckBox); layout3->addWidget(isHiddenCheckBox); layout3->addWidget(isReadableCheckBox); layout3->addWidget(isWritableCheckBox); layout3->addWidget(isExecutableCheckBox); QHBoxLayout *layout4 = new QHBoxLayout; layout4->addWidget(getBtn); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addLayout(gridLayout); mainLayout->addLayout(layout2); mainLayout->addLayout(layout3); mainLayout->addLayout(layout4); connect(fileBtn,SIGNAL(clicked()),this,SLOT(slotFile())); connect(getBtn,SIGNAL(clicked()),this,SLOT(slotGet())); } Dialog::~Dialog() { } void Dialog::slotFile() { QString fileName = QFileDialog::getOpenFileName(this,"打开","/","files (*)"); fileNameLineEdit->setText(fileName); } void Dialog::slotGet() { QString file = fileNameLineEdit->text(); QFileInfo info(file); qint64 size = info.size(); //获取大小 QDateTime created = info.created();//创建时间 QDateTime lastModified = info.lastModified();//最后修改时间 QDateTime lastRead = info.lastRead();//最后访问时间 bool isDir = info.isDir(); //是否是目录 bool isFile = info.isFile(); //是否是文件 bool isSymLink = info.isSymLink(); //符合链接 bool isHidden = info.isHidden(); //隐藏属性 bool isReadable = info.isReadable(); //读属性 bool isWritable = info.isWritable(); //写属性 bool isExecutable = info.isExecutable(); //可执行属性 sizeLineEdit->setText(QString::number(size)); createTimeLineEdit->setText(created.toString()); lastModifiedLineEdit->setText(lastModified.toString()); lastReadLineEdit->setText(lastRead.toString()); isDirCheckBox->setCheckState(isDir?Qt::Checked:Qt::Unchecked); isFileCheckBox->setCheckState(isFile?Qt::Checked:Qt::Unchecked); isSymLinkCheckBox->setCheckState(isSymLink?Qt::Checked:Qt::Unchecked); isHiddenCheckBox->setCheckState(isHidden?Qt::Checked:Qt::Unchecked); isReadableCheckBox->setCheckState(isReadable?Qt::Checked:Qt::Unchecked); isWritableCheckBox->setCheckState(isWritable?Qt::Checked:Qt::Unchecked); isExecutableCheckBox->setCheckState(isExecutable?Qt::Checked:Qt:: Unchecked); }
---
多媒体
2018-09-28 14:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
新建QtGUI应用,基于QDialog,取消界面创建 #include "dialog.h" Dialog::Dialog(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("File View")); fileLineEdit = new QLineEdit(tr("/")); fileListWidget = new QListWidget; mainLayout = new QVBoxLayout(this); mainLayout->addWidget(fileLineEdit); mainLayout->addWidget(fileListWidget); connect(fileLineEdit,SIGNAL(returnPressed()),this,SLOT(slotShow(QDir))); connect(fileListWidget,SIGNAL(itemDoubleClicked(QListWidgetItem*)),this,SLOT(slotDirShow(QListWidgetItem*))); QString root = "/"; QDir rootDir(root); QStringList string; string << "*"; QFileInfoList list = rootDir.entryInfoList(string); showFileInfoList(list); } Dialog::~Dialog() { } //显示dir下的所有文件 void Dialog::slotShow(QDir dir) { QStringList string; string<<"*"; QFileInfoList list=dir.entryInfoList(string,QDir::AllEntries,QDir::DirsFirst); //文件名的过滤方式。列出目录,文件,磁盘驱动器。目录优先排序 showFileInfoList(list); } //用户双击进入下一级目录,单击".."返回上级,顶部的编辑框显示当前所在的目录,类表显示该目录下所有文件 void Dialog::showFileInfoList(QFileInfoList list) { fileListWidget->clear(); for(unsigned int i = 0; i < list.count(); i++) //从类表中依次取出所有项 { QFileInfo tmpFileInfo = list.at(i); if(tmpFileInfo.isDir()) { QIcon icon("dir.png"); QString fileName = tmpFileInfo.fileName(); QListWidgetItem *tmp = new QListWidgetItem(icon, fileName); fileListWidget->addItem(tmp); } else if(tmpFileInfo.isFile()) { QIcon icon("file.png"); QString fileName=tmpFileInfo.fileName(); QListWidgetItem *tmp = new QListWidgetItem(icon,fileName); fileListWidget->addItem(tmp); } } } //显示下一级目录中的所有文件 void Dialog::slotDirShow(QListWidgetItem * item) { QString str = item->text(); //将下一级的目录名保存在str中 QDir dir; dir.setPath(fileLineEdit->text());//设置QDir对象的路径为当前目录路径 dir.cd(str);//切换路径 fileLineEdit->setText(dir.absolutePath());//刷新当前的目录路径 slotShow(dir); }
entryInfoList按照某种过滤方式获取目录下的文件列表

多媒体
2018-09-28 13:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>> #include "mainwindow.h" #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { fileFun(); } MainWindow::~MainWindow() { } void MainWindow::fileFun() { //将二进制数据写到数据流。每一个条目都以二进制格式写入文件 //注意,读取的时候也要使用相同的类型读出 QFile file("binary.dat"); file.open(QIODevice::WriteOnly | QIODevice::Truncate); QDataStream out(&file); //将数据序列化 out << QString(tr("周何骏:")); //字符串序列化 out << QDate::fromString("1994/09/25", "yyyy/MM/dd"); out << (qint32)19; //整数序列化 file.close(); //从文件种读取数据 file.setFileName("binary.dat"); if(!file.open(QIODevice::ReadOnly)) { qDebug()<< "error!"; return; } QDataStream in(&file); //从文件中读出数据 QString name; QDate birthday; qint32 age; in >> name >> birthday >> age; //获取字符串和整数 qDebug() << name << birthday << age; file.close(); }
多媒体
2018-09-28 12:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
新建Qt控制台应用,修改main.cpp
1、QFile读写文件 #include #include #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFile file("C:\
多媒体
2018-09-28 12:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
大家好!我是赵军,现就职于英特尔的DCG从事基于FFmpeg的硬件优化工作,两年多前加入FFmpeg社区,2018年4月成为FFmpeg的其中的一个FFmpeg Maintainer,主要负责FFmpeg的硬件优化工作。


概览:
什么是FFmpeg FFmpeg组件 FFmpeg开发 我们的主要工作 回顾与总结

1、什么是FFmpeg


FFmpeg诞生于十几年前,最初是作为一个MPlayer播放器的一个子项目出现。因为当时的播放器有需要支持各种各样解码的需求, 其中有一位Mplayer的开发者看到了这样的需求,于是编写了FFmpeg。

它作为迄今为止最流行的一个开源多媒体框架之一,FFmpeg有两种基本使用方式——作为库或者作为工具,其中后者的使用场景更多,同时它也被称为多媒体开发的“瑞士军刀”。FFmpeg库中90%的代码以上使用C,同时也有一些汇编语言上的优化,还有一些基于GPU的优化。对于汇编优化而言,由于YASM对最新的CPU指令支持效果不好,FFmpeg的汇编现在正在向NASM转变。FFmpeg本身有一些基本的开发策略,希望所有的Codec集成在内部库中随时调用;当然它也在必要时可以依赖一些外部第三方库,例如像众所周知的X.264。(X.264作为一个Encoder来说已经足够优秀,我们可以看到大部分的商业产品都以X.264为对标,常会看到某某Codec宣称比X.264好多少,似乎X.264已经成为业内一个基本对标点)。FFmpeg同样也是一个跨平台的产品,主要的License是GNU GPLv2,或GNU LGPLv2.1+的,讲到这里我想说的是,希望大部分的使用者也能够在项目通过声明使用了FFmpeg这一点为开源社区带来正面的反馈。

1.1 FFmpeg的发展历史


这里需要说明的是FFmpeg与Libav之间的关系, 2011年FFmpeg社区中的一部分开发者因为某些原因脱离了FFmpeg社区并创立了Libav社区,而后来使用Libav的大部分的发行版本又慢慢迁移回了FFmpeg。但是,直到现在仍有几位脱离FFmpeg社区的主要开发者坚守在Libav,而大部分的开发者与资源都重新迁回了FFmpeg社区。现在FFmpeg的最新稳定版本为2018年4月更新的v4.0.0,从v3.4.2 到最新的v4.0.0,其中最大的改进之一便是硬件加速。


1.2 使用场景

FFmpeg有很多使用场景,其中较为典型的有播放器、媒体编辑器、云转码等等。据我所知有上图中左侧的这些公司在以上场景中使用FFmpeg,而右侧的公司则是将FFmpeg作为第三方Codec使用。

2、FFmpeg组件


关于FFmpeg组件,大家可以在网上查到使用FFmpeg的API编写播放器的教程文章。FFmpeg组件中应用最多的是FFmpeg,它被用于进行转码,而FFprobe则被用于进行码流分析(这些都是基于命令行的工具);而FFserver的代码库已经被删除,其最主要的原因是FFmpeg Server的维护状态并不好,出现很多问题。最近社区又有人在重写FFmpeg Server,估计不久之后代码库会得以恢复。但这时的FFserver的实现方式跟之前已经基本没什么关系了。在上图中我们可以看到其中的Libavdevice主要使用硬件Capture或者进行SDI (流化),Libavformat是一些常见容器格式例如Mux或Demux等。我个人的大部分工作是在Libavcodec与Libavfilter,在Libavcodec上进行一些基于英特尔平台的优化,而Libavfilter主要是针对图像的后处理。我认为在AI盛行的时代Libavfilter会出现非常大的改动,加入更多新功能而非仅仅基于传统信号处理方式的图像处理。最近实际上已经有人尝试在其中集成Super Resolution,但在性能优化上仍有待改进,预计还需要持续一段时间才能真正做到实时与离线。除去作为基础组件的Libavutil,其他像Libavresample、Libswresample、Libpostproc这几个库现在都有逐渐被废弃的趋势。

2.1 基本介绍


为什么FFmpeg会有那么高的使用量? FFmpeg和Gstreamer究竟是什么关系?我也在反复思考这些问题,为什么我会用FFmpeg而不用Gstreamer?将这个问题引申来看可能会考虑:FFmpeg适合做哪些?不适合做哪些?我想人们热衷于使用FFmpeg的原因之一是FFmpeg的API非常简洁。上图是一个范例,我们可以看到只需五步就可写出一个基本的Decoder。这个例子非常容易理解,如果能将这个例子从API到底层逐步研习其细节便可对FFmpeg有更深层的认识。

2.1.1 Libavformat


Libavformat的主要任务是Demuxer/Muxer功能。我们可以看到FFmpeg的框架设计得十分精炼,基本上如果需要实现一个AVFormat或AVCodec以对应新的Format/Codec;所以即使一位开发者不了解FFmpeg框架也可以编写一个简单的Format或Codec,需要做的最主要是实现对应的AVFormat/AVCodec。

2.1.2 Libavcodec


需要提及一下的,有两种方案实现对应的Libavcodec,一种是以Native方式实现在FFmpeg内部,另一种是利用集成的第三方库,我们现在看到的一些Encoder相关的是以集成的第三方库为基础。而对于Decoder ,FFmpeg社区的开发者做得非常快。耐人寻味的是,据说FFmpeg内部VP9 的Decoder速度比Google Libvpx Decoder还要快,我们知道Google是VP9 Codec的创立者,但是Google的表现还不如FFmpeg自身的VP9 的Decoder 。

2.1.3 Libavfilter


Libavfilter是FFmpeg内部最复杂的部分之一,其代码一直在反复重构。Libavfilter的思想可能借鉴了Windows 的DirectX上一些思想,但代码却有些复杂。其构成并非像图片展示的那样是一个简单的串行关系,实际上它可以构成一个有向无环图,这意味着只要能够构成一个DAG这个LibavFilter就能工作。但是Libavfilter的综合表现并不是特别好,我一直在想尝试对其进行改进,但因为这一部分的复杂度比较高,总是令我感觉不入其门。

2.1.4 FFmpeg Transcoding


FFmpeg的应用场景之一是Transcoding转码,涉及Demux/Decoder/Encoder/Muxer,同时我们可以把Demux+Mux的流程看作是它的一个特例。另一应用场景是作为Player播放器,上图展示了Transcoding与Player两种应用场景的流程。

3、FFmpeg开发


FFmpeg中比较重要的API包括如何进行Decoder、Postprocess、Encoder等。如果你对此感兴趣,我认为最好的办法是去认真看一看FFmpeg里一些很好的例子。最近两年FFmpeg把过去的API进行了重构,如果我以原来的FFmpeg API为基础进行解码,其做法是输入一个已经压缩过的Frame数据并希望得到一个解码的Frame,但实际上此过程存在的限制是需要确保输入的Frame与解码出的数据一一对应。后来随着开发的深入,特别是H.264提供的MVC这种模式以后,有时我们输入一个Frame后需要分左右解码两个帧,此时它的API便无法支持这种场景。因此最近FFmpeg的API被从输入一个Frame输出一个Packet修改为两个API,这样便可解除它们之间的耦合。当然由于输入与解码变成了两个分离的步骤,导致代码中需要大量的While循环来判断此解码过程是否结束。

4、硬件加速


我在英特尔负责FFmpeg硬件加速的工作,因此更关注FFmpeg的硬件加速在英特尔GPU上的表现。我们一直在考虑如何更快地将英特尔的硬件加速方案推荐给客户使用,让用户能够有机会体验到硬件加速的强大功能。现在英特尔提出的两种通过FFmpeg驱动GPU的硬件加速方案,其中一种方案基于MediaSDK,我想如果你用过英特尔的GPU便应该会对其有所了解,已经有很多客户基于MediaSDk进行了部署,其中,MediaSDK的VPP部分作为AVFilter也在FFmpeg内部;另一种更为直接的方案是VA-API,VA-API类似于Windows上提供的DXV2或是MacOS上提供的Video Toolbox等基于OS层面的底层硬件加速API,我现在的大部分的工作专注于此领域。FFmpeg同样集成了OpenCL的一些加速,它使得你可以借助GPU进行转码工作并在整套流程中不涉及GPU与CPU的数据交换,这个方案方案会带来明显的性能提升。我们虽希望从解码到VPP再到编码的整条流程都可以在GPU内完成,但GPU的一些功能上的缺失需要其他硬件加速功能来弥补,此时就可考虑使用OpenCL优化。其次是因为OpenCV已经进行了大量的OpenCL加速,所以当面对这种图像后处理的硬件加速需求时可以考虑把OpenCV集成到FFmpeg中,但在OpenCV发展到v3.0后其API从C切换到了C++,而FFmpeg自身对C++的API支持并不友好,这也导致了FFmpeg的官方版本中只支持OpenCV到v2.4。如果你对此感兴趣,可以尝试基于在OpenCV v3.0以上的版本做一个新的C Warper,再考虑集成进FFmpeg。但如果你对性能要求足够高,直接使用VA-API和OpenCL去做优化,保证整个流程能够在GPU内部完整运行,达到最好的性能表现。


上图是对GFFmpeg硬件加速的流程概览图,大部分人可能对英特尔的两套方案有比较清晰的认识,最关键的点在于QSV方案依赖于MediaSDK,而VA-API则可以理解为将整个MediaSDK做的工作完整的放进了FFmpeg的内部,与FFmpeg融为一体,FFmpeg开发者与社区更推荐后者。现在的OCL方案最近也正不停的在有一些Patch进来,这里主要是对AVFilter的处理的过程进行硬件加速。需要说明一下,因为社区曾经有尝试用OpenCL加速X.264使其成为一个更快的Codec,但结果并不是特别好。所以用OpenCL去硬件加速Encoder,其整体性能提升并不是特别明显。


上图展示了更多细节,我们可以看到每种方案支持的Codec与VPP的功能与对应的Decoder和Encoder。


这两种方案的差异在于实际上是QSV Call第三方的Library,而VA-API直接基于VA-API 的Interface,使用FFmpeg的Native 实现而并不依赖任何第三方外部库。

VA-API


经常会有人提出疑问:VA-API是什么?它的本质类似于Microsoft在Windows上提出的DXVA2,也就是希望用一套抽象的接口去隐藏底层硬件细节,同时又暴露底层硬件的基本能力。VA-API有多个可用后端驱动,最常见的是原先英特尔OTC提供的VA (i965)的驱动,现如今在Linux发行版本中也存在;而Hybird驱动则更多被用于当硬件的一些功能还没有准备好的情景,需要先开发一个仿真驱动;等硬件部分准备完成后再使正式驱动。第三个是iHD/Media driver,这部分驱动在去年年底时Intel便已经开源,这一套驱动对比i965驱动其图像质量和性能表现更优但稳定性较差。现在我一直在此领域工作,希望它能够更好地支持FFmpeg。Mesa’s State-Trackers主要支持AMD的GPU,但是由于现在只有Decoder而Encoder处在试验阶段一直未开放,所以AMD的GPU在FFmpeg上无法进行Encoder加速。


上图是VA-API的一些基本概念,在这里我就不做过多阐述。


这是基于VA-API 一个基本流程。FFmpeg的VA-API也是基于此流程做的。

开放问题


FFmpeg的QSV硬件加速方案究竟有什么优缺点?如果将 FFmpeg与GStreamer比较,什么情况下选择FFmmpeg什么情况下选择GStreamer,这是我一直在反反复复考虑的内容,还有FFmpeg与OpenMAX的差别这些(Android使用了OpenMAX)。对于未来趋势,我们期待基于FFmpeg与英特尔的GPU构建一个全开源的解决方案,将整个开发流程透明化;在之后我们也考虑OpenCL的加速 ,顺带说一句,作为OpenCL最初的支持者的Apple,在不久前的WWDC上称要放弃OpenCL,不过从现实来看,如果想在GPU或异构上进行硬件加速开发,OpenCL仍然是最优的选择。其他方案直到现在还没有OpenCL的广泛适应度。实际上OpenCL本身的推出并不是特别的成功,在OpenCL过去的十年发展中并没有出现杀手级应用;另一个趋势是,Vulkan作为OpenGL的后继者开始流行,因此业界也在考虑直接把OpenCL作为Vulkan部分合并在一起。另外,OpenCV有大量的OpenCL优化,如果你不愿意重写OpenCL的优化,可以考虑用FFmpeg与 OpenCV一起加速来构建整个流程。


上图展示了你所见的基于各个OS与硬件厂商的硬件加速状态。大部分专注于硬件加速的开发者更关心播放器的表现,在Windows上进行硬件编码的需求并不强。

Q&A

Q1:FFmpeg Server最近的一些大改动是什么?

A: FFmpeg Server的代码在最新版本的FFmpeg里已经不存在了,主要是由于维护者并不积极。现在又有开发者正在重写FFmpeg Server并且已经Review到第三轮,我相信最快需要一两个月它又会回到FFmpeg里面,但和以前的FFmpeg Server完全不一样。

Q2:FFmpeg 4.0已经有VA-API的方案吗?

A: VA-API的Encoder从3.3.1开始支持,这部分的代码从2016年到2018年一直在进行重构,在4.0.0时VA-API的Encoder都可以支持。届时是一个开箱即用的状态。

Q3:安卓平台现在可以硬件加速吗?

A: VA-API的方案是英特尔的,由于英特尔的产品生态缘故,安卓的解决方案是基于MediaCodec而非VA-API,其硬件加速就目前而言只有解码加速没有编码加速。

Q4:后台的多任务转码服务器需要用硬件来编码,那么可以同时进行多少任务?如果根据硬件的核心数量来决定,那么超过性能极限是否会导致创建编码器失败?

A: 如果是基于CPU的编码方案,那么编码的性能与CPU的线程数有关,而FFmpeg性能并未和CPU的核心数量构成一个线性关系;如果是基于GPU的编码方案,包括1对n的转码,这需要以官方测试为准。英特尔在官方网站的GPU参数有相关数据,这与硬件平台有非常大的关系,具有强大性能的硬件平台可以保证良好的编解码运算处理能力。

Q5:还有一个跟WebRTC相关的问题,他说这个在WebRTC 在Chrome里FFmpeg实现硬件加速有哪些,可以替换其他版本的FFmpeg吗?

A: 据我所知在ChromeOS中只有当自身API硬件加速不工作的情况下才会使用FFmpeg,Chrome可以说是把FFmpeg作为一个备选方案,并没有直接用作硬件加速。

Q6:英特尔 Collabration 的客户端SDK,支持喂数据给WebRTC的,这里硬件编码是用的WebRTC内部,还是自己替换的?

A: 英特尔 Collabration的客户端我不知道这个事情,在Server端采用了三套方案,一套方案是MediaSDK进行硬件加速,第二套方案是VPX以支持VP8,VP9 ,其他还有支持另外格式的方案。我无法准确推断是否会用FFmpeg进行硬件加速与软件解码,之前与内部有过相关的的交流,但最终没有决定。

Q7:还有个问题,FFmpeg有哪些Filter是使用了硬件加速,有没有这方面的加速计划?

A: 现在是有这个计划,以下图片可以说明


现在已经有一些基本的硬件加速,主要是一些ColorSpace的转换,有一些Scaling,再就是 Deinterlace,FRC现在考虑OpenCL去做,因为 iHD driver这块支持应当没有了。预计更多的OpenCL会进行加速,我们希望Decoder + Filter + Encoder的整个过程都在GPU内部运算完成从而减少CPU的性能损耗,同时也希望OpenCL具有一定的灵活度。

Q8:VA-API在Linux下支持哪些型号CPU?

A: 这与驱动有关,总体来说i965支持更多的处理器,iHD支持英特尔Skylake架构以后的处理器

Q9:如何提升硬件编解码的质量?

A: 这是硬件编解码方面的老大难问题,每一个做硬件编解码的人都会提出类似的问题。因特尔曾提出了一个被称为FEI的解决方案,其原理是仅提供GPU中与硬件加速相关的最基本功能,而像图像质量等方面的提升则基于搜索算法等非硬件加速功能。这就使得可以让用户考虑使用自己的算法,而与计算量相关的问题则交给GPU处理,但此方案并未出现一个特别成熟的应用。

Q10:基于CPU、GPU设置FFmpeg线程数,线程数和核心数有什么对应关系?

A: 其实对GPU而言处理速度已经足够快,运行多进程的转码对GPU而言基本没有什么影响。根据实测来看,例如运行4进程的转码,对CPU和GPU的消耗没有特别大的区别。但在这种情况下如果是用GPU进行,我的建议是用进程会更好管理。其次是FFmpeg自身1对n的特性使得在价格上比较敏感,这也是我们一直致力改进的重点。相信改进之后会为1对n转码带来一个比较大的提升,但就目前而言仍处于内部设计的初级阶段。
多媒体
2018-09-26 07:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
研究了一段时间rtmp,接触了一些相关的开源项目。总结如下

red5 java 开源。功能提供 rtmp 。java编写效率内存开销比较大
nginx-rtmp c++开源。基于nginx的扩展,提供rtmp HTTP-FLV HLS。但经过测试,感觉rtmp功能有bug经常播放有声无图..hls正常
srs c++开源。功能提供 rtmp HTTP-FLV HLS 等等。商业级服务端,支持多台服务器扩展
bblive go语言 开源。功能提供 rtmp
gortmp go语言 开源。功能提供 rtmp
livego go语言 开源。 功能提供 rtmp HTTP-FLV HLS ,功能比较全面。可以覆盖pc 手机安卓 ios
sms go语言 开源。 功能提供 rtmp HTTP-FLV HLS ,功能比较全面。可以覆盖pc 手机安卓 ios
go-rtmp-server go语言 开源。 功能提供 rtmp HTTP-FLV

。。。未完待续。,
多媒体
2018-09-25 16:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
阿里视频点播
前言
笔者在开发过程中需要使用视频流的方式开发视频,如何选择正确的流媒体服务器呢?市面上有很多的流媒体服务器,这里给大家介绍下阿里的视频服务:视频点播,媒体转码,视频直播
什么是视频点播
阿里云视频点播(VoD)是集音视频采集、编辑、上传、自动化转码处理、媒体资源管理、分发加速、视频播放于一体的一站式音视频点播解决方案。 视频点播服务构建在阿里云强大的基础设施服务之上,借助灵活和可伸缩的存储、高质量的视频转码处理技术,以及稳定快速的内容分发服务,帮助企业和开发者快速搭建安全、弹性、高可定制的点播平台和应用。 视频点播服务提供Web管理控制台和软件开发工具包(API+SDK)。您可以通过它们使用、管理视频点播服务,也可以与您自己的应用和服务集成。 所有服务按使用付费,服务能力自动伸缩,告别复杂的架构设计和编程开发,维护成本几近于零,使您可以专注于业务逻辑实现及最终用户体验的提升。(说实在,阿里云的视频点播的文档写的不咋滴)
阿里视频点播服务价格:
首先要购买视频点播服务,阿里云的价格有四种: 2216(元)/年、6488(元)/年、19900(元)/年、49900(元)/年
如何购买根据自己的需求, 当购买完成之后阿里会给你分配两个key(上传视频和播放视频都需要用到这两个数据):Access Key ID Access Key Secret
如何使用
阿里视频文文档
上传视频有四种: 1、JavaScript上传视频、IOS上传视频、Android上传视频、Java上传视频 ; 这里介绍下JavaScript上传视频。
我们先看下阿里的 demo 其实我们只要注意第一个点播上传配置就ok了,使用点播配置上传需要获取上传凭证和上传地址(这里使用到JAVA版本),另外还有PHP版本和Python版本
阿里视频点播上传SDK-JAVA版
生成凭证和地址代码(简化了一些可选代码): public static CreateUploadVideoResponse createUploadVideo(CoursewareInfoVo vo) { DefaultAcsClient aliyunClient = new DefaultAcsClient(DefaultProfile.getProfile("cn-shanghai",accessKeyId,accessKeySecret)); CreateUploadVideoRequest request = new CreateUploadVideoRequest(); CreateUploadVideoResponse response = null; try { /*必选,视频源文件名称(必须带后缀, 支持 "3GP","AVI","FLV","MP4","M3U8","MPG","ASF","WMV","MKV","MOV","TS","WebM","MPEG","RM","RMVB","DAT","ASX","WVX","MPE","MPA","F4V","MTS","VOB","GIF")*/ request.setFileName(vo.getFileName());上传的视频文件名称 //必选,视频源文件字节数 request.setFileSize(Long.valueOf(vo.getSize()));上传视频的大小 //必选,视频标题 request.setTitle(vo.getFileName()); 上传视频的标题 response = aliyunClient.getAcsResponse(request); } catch (ServerException e) { System.out.println("CreateUploadVideoRequest Server Exception:"); e.printStackTrace(); } catch (ClientException e) { System.out.println("CreateUploadVideoRequest Client Exception:"); e.printStackTrace(); } System.out.println("UploadAuth:"+response.getUploadAuth()); System.out.println("UploadAddress:"+response.getUploadAddress()); System.out.println("videoId:"+response.getVideoId()); videoId很重要,后续播放的时候需要用到,要记得保存 return response; }
前端的js代码: 这里引入 初始化上传的js window.onload = new function() { uploader = new VODUpload({ // 文件上传失败 'onUploadFailed': function (uploadInfo, code, message) {}, // 文件上传完成 'onUploadSucceed': function (uploadInfo) {}, // 文件上传进度 'onUploadProgress': function (uploadInfo, totalSize, uploadedSize) { aliyunProgress(Math.ceil(uploadedSize * 100 / totalSize)); }, // STS临时账号会过期,过期时触发函数 'onUploadTokenExpired': function () { // 实现时,从新获取UploadAuth // uploader.resumeUploadWithAuth(uploadAuth); }, // 开始上传 'onUploadstarted': function (uploadInfo) { uploadAuth,uploadAddress 这两个参数是通过上面的代码传到前台 uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress); } }); if (isVodMode()) { // 点播上传。每次上传都是独立的鉴权,所以初始化时,不需要设置鉴权 uploader.init(); } }; function start() { uploader.startUpload(); aliyunProgress(0); } function stop() { uploader.stopUpload(); } function resumeWithToken() { var uploadAuth = document.getElementById("uploadAuth").value; if (isVodMode()) { uploader.resumeUploadWithAuth(uploadAuth); } } function clearList() { uploader.cleanList(); } function deleteFile() { if (document.getElementById("deleteIndex").value) { var index = document.getElementById("deleteIndex").value log("delete file index:" + index); uploader.deleteFile(index); } } 添加视频到 uploader的中
document.getElementById("files").addEventListener('change', function (event) { for(var i=0; i操作截图
选择上传的视频文件
读取视频信息获取到阿里的uploadAuth和uplaodAddress后点击开始上传 uploader.startUpload() 上传的进度可以在onUploadProgress方法中实现,这样阿里云的视频就上传完成了,接下来就是如何调用视频
浏览上传视频
1、获取播放凭证 阿里视频点播获取播放凭证JAVA版 ,另外还有PHP版本和Python版本
代码如下: public static GetVideoPlayAuthResponse getVideoPlayAuth(String videoId) { DefaultAcsClient aliyunClient = new DefaultAcsClient(DefaultProfile.getProfile("cn-shanghai",accessKeyId,accessKeySecret)); GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest(); request.setVideoId(videoId); GetVideoPlayAuthResponse response = null; try { response = aliyunClient.getAcsResponse(request); } catch (ServerException e) { throw new RuntimeException("GetVideoPlayAuthRequest Server failed"); } catch (ClientException e) { throw new RuntimeException("GetVideoPlayAuthRequest Client failed"); } response.getPlayAuth(); //播放凭证 response.getVideoMeta(); //视频Meta信息 return response; }
2、web端播放 Web播放器SDK (此外还有android版本播放和ios版本播放)
代码如下(简化版): http://g.alicdn.com/de/prismplayer/1.7.6/prism-h5-min.js http://g.alicdn.com/de/prismplayer/1.7.6/skins/default/index-min.css var player= new prismplayer({ id: 'J_prismPlayer', width:'100%', height:$("#player").height()+"px", autoplay: true, 是否自动播放 vid : videoId, 视频ID playauth :playAuth 后台获取到的 });
预览效果如图
总结
阿里视频点播服务,还有其功能,这里通过阿里的文档简单的介绍下创建自己的流媒体服务器级使用方法,笔者是做java开发的,在开发中其实会用到很多的功能插件,后面会记录下来,同大家分享,也欢迎大家留言 ahpome的博客
多媒体
2018-09-25 10:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
基础
许多人所称的“ Linux ”实际上不是 Linux。Linux 从技术上说只是 Linux 内核,典型的 Linux 发行版则包括了 Linux 内核和许多软件。这是为什么 Linux 有时被称为 GNU/Linux。事实上,许多在 Linux 上使用的软件同样也在 BSD 上使用。
Linux 和 BSD 都是类 UNIX 操作系统。我们可以通过阅读类 UNIX 操作系统历史发现 Linux 和 BSD 有不同的谱系。Linux 是由 Linus Torvalds 在芬兰上大学的时候开发的。BSD 则代表“Berkeley Software Distribution,伯克利软件套件”,其源于对加州大学伯克利分校所开发的贝尔实验室UNIX的一系列修改,它最终发展成一个完整的操作系统,现在有多个不同的BSD分支。
内核 vs. 完整操作系统
严格的说,Linux 是只是一个内核。制作 Linux 发行版所要做的工作就是,汇集那些创建一个完整 Linux 操作系统所需的所有软件,将它组合成一个像 Ubuntu、Mint、Debian、RedHat 或者是 Arch 这样的 Linux 发行版。有许多不同的 Linux 发行版。
与此相反的是,BSD 这个名字则代表其内核和操作系统。例如,FreeBSD 提供了 FreeBSD 内核和 FreeBSD 操作系统。它是作为一个单一的项目维护的。换句话说,如果你想要安装 FreeBSD,就只有一个 FreeBSD 可供你安装。如果你想要安装 Linux,你首先需要在许多 Linux 发行版之间选择。
BSD 包括一个名为 Ports 的系统,它提供了一种安装软件包的方式。Ports 系统包含了软件包的源代码,所以您的计算机如果想安装软件的话,则需要先编译他们。(如果您曾经使用过以前流行的 Gentoo,有点类似那样。)不过,软件包也可以是预安装的二进制形式,以便你不需要花时间和系统资源编译他们就能运行。
许可证
许可证是典型的差异,虽然它不会对大多数人产生影响。Linux 使用 GNU 通用公共许可证,即 GPL。如果你修改了 Linux 内核,并将其分发,你就必须放出您的修改的源代码。
BSD 使用 BSD 许可证。如果你修改了 BSD 内核或发行版,并且发布它,你根本不需要必须发布其源代码。你可以自由地对你的 BSD 代码做任何你想做的事情,你没有义务发布的你修改的源代码,当然你想发布也行。
两者都是开放源码的,但是以不同的方式。人们有时会陷入关于哪种许可证是“更自由”的辩论。GPL 可以帮助用户以确保他们可以拥有 GPL 软件的源代码,并限制开发人员迫使他们开放代码。BSD 许可证并不能确保用户可以拥有源代码,而是给开发人员选择是否公布代码的权利,即使他们想要把它变成一个闭源项目。
BSD分支
以下是通常认可的三个“主流” BSD 操作系统:
●FreeBSD: FreeBSD 是最受欢迎的 BSD,针对高性能和易用性。它支持英特尔和 AMD 的32位和64位处理器。
●NetBSD: NetBSD 被设计运行在几乎任何架构上,支持更多的体系结构。在他们的主页上的格言是"理所当然,我们运行在 NetBSD 上"。
●OpenBSD:OpenBSD 为最大化的安全性设计的 —— 这不仅仅它宣称的功能,在实践中也确实如此。它是为银行和其他重要机构的关键系统设计的。
还有两个其他的重要 BSD 操作系统:
●DragonFly BSD: DragonFly BSD 的设计目标是提供一个运行在多线程环境中的操作系统 —— 例如,计算机集群。
●Darwin / Mac OS X: Mac OS X 实际上基于 Darwin 操作系统,而 Darwin 系统基于 BSD。它与其他的 BSD 有点不同,虽然底层内核和其他的软件是开源代码(BSD 代码),但操作系统的大部分是闭源的 Mac OS 代码)。苹果在 BSD 基础上开发了 Mac OS X 和 iOS,这样他们就不必写操作系统底层,就像 谷歌在 Linux 基础上开发 android 系统一样。
你为什么会选择 BSD 而不是 Linux?
Linux 显然比 FreeBSD 更受欢迎。例如,Linux 往往会比 FreeBSD 更早提供新硬件的支持。BSD 有一个兼容包可用,使之能像大多数的其他软件一样原生的执行 Linux 二进制程序。
如果您使用过 Linux, FreeBSD 不会让你感觉到太大的不同。如果把 FreeBSD 作为桌面操作系统,你也可以使用相同的 GNOME,KDE 或 Xfce 桌面环境,你也可以在BSD上使用 Linux 上的大多数的其他软件。有一点需要注意,FreeBSD 不会自动安装的图形化桌面,所以你要花相对于 Linux 更多的心思来照顾你的BSD。BSD 更守旧一些。
FreeBSD 的可靠性和稳定性也许更适合作为服务器的操作系统。而厂商也会选择 BSD 而不是 Linux 作为其操作系统,因为这样他们就不必放出他们修改的代码。
如果你是一个 PC 桌面用户,你真的不需要太过在意 BSD。你可能会喜欢 Linux,因为它具有更先进的硬件支持,更容易安装,具有现代操作系统的特点。如果你关注服务器或嵌入式的设备,你可能会更喜欢 FreeBSD。
我们可能会听到一些人说他们在桌面电脑上使用 FreeBSD,你当然也可能是其中之一!但像 Ubuntu 或 Mint 一样的开源操作系统对于多数用户来说更体验良好和更先进些。 原文来自: https://mp.weixin.qq.com/s?__biz=MzI4MDEwNzAzNg==&mid=400886707&idx=1&sn=392c27afb27c63938411ebeedbe5a111#rd
本文地址: https://www.linuxprobe.com/linux-freebsd.html 编辑:朱培棋,审核员:逄增宝
多媒体
2018-09-24 17:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>> void ScreenCap(void* buf, int w, int h) { HWND hDesk = GetDesktopWindow(); HDC hScreen = GetDC(hDesk); int width = w;//GetDeviceCaps(hScreen, HORZRES); int height = h;//GetDeviceCaps(hScreen, VERTRES); // if (w != 0) // *w = width; // if (h != 0) // *h = height; HDC hdcMem = CreateCompatibleDC(hScreen); HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, width, height); BITMAPINFOHEADER bmi = { 0 }; bmi.biSize = sizeof(BITMAPINFOHEADER); bmi.biPlanes = 1; bmi.biBitCount = 24; bmi.biWidth = width; bmi.biHeight = height; bmi.biCompression = BI_RGB; bmi.biSizeImage = width*height; SelectObject(hdcMem, hBitmap); BitBlt(hdcMem, 0, 0, width, height, hScreen, 0, 0, SRCCOPY); GetDIBits(hdcMem, hBitmap, 0, height, buf, (BITMAPINFO*)&bmi, DIB_RGB_COLORS); DeleteDC(hdcMem); ReleaseDC(hDesk, hScreen); CloseWindow(hDesk); DeleteObject(hBitmap); } ////////////////////////////////////////////////////////////////////////// // rgb转yuv420 ////////////////////////////////////////////////////////////////////////// bool RGB2YUV(unsigned char* RgbBuf,UINT nWidth,UINT nHeight,LPBYTE yuvBuf,unsigned long len) { int i, j; unsigned char*bufY, *bufU, *bufV, *bufRGB,*bufYuv; memset(yuvBuf,0,nWidth*nHeight*1.5); bufY = yuvBuf; bufV = yuvBuf + nWidth * nHeight; bufU = bufV + (nWidth * nHeight* 1/4); unsigned char y, u, v, r, g, b,testu,testv; unsigned int ylen = nWidth * nHeight; unsigned int ulen = (nWidth * nHeight)/4; unsigned int vlen = (nWidth * nHeight)/4; for (j = 0; j> 8) + 16 ; u = (unsigned char)( ( -38 * r - 74 * g + 112 * b + 128) >> 8) + 128 ; v = (unsigned char)( ( 112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ; *(bufY++) = max( 0, min(y, 255 )); if (j%2==0&&i%2 ==0) { if (u>255) { u=255; } if (u<0) { u = 0; } *(bufU++) =u; //存u分量 } else { //存v分量 if (i%2==0) { if (v>255) { v = 255; } if (v<0) { v = 0; } *(bufV++) =v; } } } } //len = nWidth * nHeight+(nWidth * nHeight)/2; return true; } char*rgb=new char[width*height*3]; ScreenCap(rgb,width,height); RGB2YUV((unsigned char*)rgb,width,height, yuv_buffer,0);
这样获得的yuv420 可以直接x264 encode 压缩成视频。即屏幕录像或者屏幕直播
多媒体
2018-09-23 17:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
首先可以通过ffprobe查看视频信息 (可选)
执行: ./ffprobe Beach-Ball.mp4 iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static # ./ffprobe Beach-Ball.mp4 ffprobe version 4.0.2-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2007-2018 the FFmpeg developers built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg libavutil 56. 14.100 / 56. 14.100 libavcodec 58. 18.100 / 58. 18.100 libavformat 58. 12.100 / 58. 12.100 libavdevice 58. 3.100 / 58. 3.100 libavfilter 7. 16.100 / 7. 16.100 libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Beach-Ball.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 creation_time : 2015-08-24T11:27:32.000000Z encoder : Lavf55.33.100 Duration: 00:00:15.70, start: 0.000000, bitrate: 2526 kb/s Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2525 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default) Metadata: creation_time : 2015-08-24T11:27:32.000000Z handler_name : VideoHandler iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static #
再用ffmpeg把Beach-Ball.mp4文件转换为beachball.ts文件:
./ffmpeg -y -i Beach-Ball.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb beachball.ts iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static # ./ffmpeg -y -i Beach-Ball.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb beachball.ts ffmpeg version 4.0.2-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg libavutil 56. 14.100 / 56. 14.100 libavcodec 58. 18.100 / 58. 18.100 libavformat 58. 12.100 / 58. 12.100 libavdevice 58. 3.100 / 58. 3.100 libavfilter 7. 16.100 / 7. 16.100 libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Beach-Ball.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 creation_time : 2015-08-24T11:27:32.000000Z encoder : Lavf55.33.100 Duration: 00:00:15.70, start: 0.000000, bitrate: 2526 kb/s Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2525 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default) Metadata: creation_time : 2015-08-24T11:27:32.000000Z handler_name : VideoHandler Output #0, mpegts, to 'beachball.ts': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.12.100 Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 2525 kb/s, 30 fps, 30 tbr, 90k tbn, 15360 tbc (default) Metadata: creation_time : 2015-08-24T11:27:32.000000Z handler_name : VideoHandler Stream mapping: Stream #0:0 -> #0:0 (copy) Press [q] to stop, [?] for help frame= 471 fps=0.0 q=-1.0 Lsize= 5276kB time=00:00:15.66 bitrate=2758.8kbits/s speed=1.31e+03x video:4840kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 9.009212% iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static # ll total 201608 -rw-r--r-- 1 root root 4958325 Sep 22 2017 Beach-Ball.mp4 -rw-r--r-- 1 activemq 1000 35147 Jul 19 09:16 GPLv3.txt -rw-r--r-- 1 root root 5402744 Sep 19 21:37 beachball.ts -rwxr-xr-x 1 activemq 1000 64639464 Jul 19 08:56 ffmpeg -rwxr-xr-x 1 activemq 1000 65855848 Jul 19 09:16 ffmpeg-10bit -rwxr-xr-x 1 activemq 1000 64541160 Jul 19 08:56 ffprobe drwxr-xr-x 2 activemq 1000 4096 Jul 19 09:00 manpages drwxr-xr-x 2 activemq 1000 4096 Jul 19 07:35 model -rwxr-xr-x 1 activemq 1000 742480 Jul 19 08:56 qt-faststart -rw-r--r-- 1 activemq 1000 2958 Jul 19 09:16 readme.txt
最后用ffmpeg把beachball.ts文件切片并生成playlist.m3u8文件,5秒一个切片:
./ffmpeg -i beachball.ts -c copy -map 0 -f segment -segment_list playlist.m3u8 -segment_time 5 beachball%03d.ts iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static # ./ffmpeg -i beachball.ts -c copy -map 0 -f segment -segment_list playlist.m3u8 -segment_time 5 beachball%03d.ts ffmpeg version 4.0.2-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg libavutil 56. 14.100 / 56. 14.100 libavcodec 58. 18.100 / 58. 18.100 libavformat 58. 12.100 / 58. 12.100 libavdevice 58. 3.100 / 58. 3.100 libavfilter 7. 16.100 / 7. 16.100 libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100 Input #0, mpegts, from 'beachball.ts': Duration: 00:00:15.70, start: 1.400000, bitrate: 2752 kb/s Program 1 Metadata: service_name : Service01 service_provider: FFmpeg Stream #0:0[0x100]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 30 fps, 30 tbr, 90k tbn, 60 tbc [segment @ 0x4df3180] Opening 'beachball000.ts' for writing Output #0, segment, to 'beachball%03d.ts': Metadata: encoder : Lavf58.12.100 Stream #0:0: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 30 fps, 30 tbr, 90k tbn, 30 tbc Stream mapping: Stream #0:0 -> #0:0 (copy) Press [q] to stop, [?] for help [segment @ 0x4df3180] Opening 'playlist.m3u8.tmp' for writing [segment @ 0x4df3180] Opening 'beachball001.ts' for writing [segment @ 0x4df3180] Opening 'playlist.m3u8.tmp' for writing [segment @ 0x4df3180] Opening 'beachball002.ts' for writing [segment @ 0x4df3180] Opening 'playlist.m3u8.tmp' for writing frame= 471 fps=0.0 q=-1.0 Lsize=N/A time=00:00:15.66 bitrate=N/A speed= 765x video:4843kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static # ll total 206904 -rw-r--r-- 1 root root 4958325 Sep 22 2017 Beach-Ball.mp4 -rw-r--r-- 1 activemq 1000 35147 Jul 19 09:16 GPLv3.txt -rw-r--r-- 1 root root 5402744 Sep 19 21:37 beachball.ts -rw-r--r-- 1 root root 1740692 Sep 19 21:38 beachball000.ts -rw-r--r-- 1 root root 1729036 Sep 19 21:38 beachball001.ts -rw-r--r-- 1 root root 1933016 Sep 19 21:38 beachball002.ts -rwxr-xr-x 1 activemq 1000 64639464 Jul 19 08:56 ffmpeg -rwxr-xr-x 1 activemq 1000 65855848 Jul 19 09:16 ffmpeg-10bit -rwxr-xr-x 1 activemq 1000 64541160 Jul 19 08:56 ffprobe drwxr-xr-x 2 activemq 1000 4096 Jul 19 09:00 manpages drwxr-xr-x 2 activemq 1000 4096 Jul 19 07:35 model -rw-r--r-- 1 root root 213 Sep 19 21:38 playlist.m3u8 -rwxr-xr-x 1 activemq 1000 742480 Jul 19 08:56 qt-faststart -rw-r--r-- 1 activemq 1000 2958 Jul 19 09:16 readme.txt iZm5ecuy8n2epwa3gno33xZ:/opt/yizhichao/ffmpeg-4.0.2-64bit-static #
多媒体
2018-09-19 21:42:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
QListView默认是可以编辑的,可以用setEditTrigers设置QListView的条目是否可以编辑,以及如何进入编辑状态。比如: ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
表示在双击,或者选择并单击列表项目
设置不可编译 ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
多媒体
2018-09-19 11:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、新建QtGUI应用,基类选择QDialog,取消创建界面
2、构造函数中: setWindowTitle("Custom Dialog"); QLabel *lbl = new QLabel("Name"); QLineEdit *edit = new QLineEdit(""); QPushButton *okbtn = new QPushButton("ok"); QPushButton *cancelbtn = new QPushButton("cancel"); QHBoxLayout *hlay1 = new QHBoxLayout(); hlay1->addWidget(lbl); hlay1->addWidget(edit); QHBoxLayout *hlay2 = new QHBoxLayout(); hlay2->addWidget(okbtn); hlay2->addWidget(cancelbtn); QVBoxLayout *vlay = new QVBoxLayout(); vlay->addLayout(hlay1); vlay->addLayout(hlay2); setLayout(vlay);
3、效果
多媒体
2018-09-18 19:26:00
「深度学习福利」大神带你进阶工程师,立即查看>>>

设置标题
填充数据
方法2:

多媒体
2018-09-18 19:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、splitter提供了一个可以动态调整控件大小的GUI


#include "widget.h" #include #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); // QDirModel model; //新建一个QDirModel对象,为数据访问做准备。QDirModel的插件还可以设置过滤器 //QDirModel继承自QAbstractModel类,为访问本地文件系统提供数据模型,它提供新建/删除目录等操作,此处 //只是用来显示本地文件系统 QFileSystemModel model; model.setRootPath(QDir::currentPath()); //QFileSystemModel&QDirModel相同的效果,他们时用于获取磁盘文件目录。不同之处在于QFileSystemModel //使用单独的线程,QDirModel不是单独的线程。单独的线程不会阻碍主线程,因此推荐QFileSystemModel //新建3种不同的View对象,以便文件系统可以以三种不同的方式显示 QTreeView tree; QListView list; QTableView table; tree.setModel(&model); list.setModel(&model); table.setModel(&model); tree.setSelectionMode(QAbstractItemView::MultiSelection); //鼠标拖动连续多选 list.setSelectionModel(tree.selectionModel());//list使用与tree相同的选择模型 table.setSelectionModel(tree.selectionModel()); QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&list,SLOT(setRootIndex(QModelIndex))); QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&table,SLOT(setRootIndex(QModelIndex))); //双击tree时,list&table种显示此选定目录下的所有目录和文件 //也就是实现的功能是:在双击treeView的一个节点时,此节点就设置listView和tableView的根节点,因为treeView的 //doubleClicked(QModelIndex)/Clicked(QModelIndex)信号会传递一个QModeIndex变量,是当前节点的模型索引, //将此模型索引传递给QListView&QTableView的槽函数setRootIndex,QListView&QTableView就会显示此节点下的 //目录和文件 QSplitter *splitter = new QSplitter; splitter->addWidget(&tree); splitter->addWidget(&list); splitter->addWidget(&table); splitter->setWindowTitle(QObject::tr("Model/view")); splitter->show(); return a.exec(); }
多媒体
2018-09-18 17:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
QSpliter控件提供可以调整控件大小的GUI。将其布局到QTreeView和QListView控件即可调节控件大小。
使用QTreeView控件

只能查看但是无法进行打开,删除等操作

QTreeView控件以属性结构显示目录和文件信息,Qlist控件以列表形式显示目录和文件
多媒体
2018-09-18 16:13:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、创建菜单栏 //使用QMene和QMenuBar类再主窗口创建菜单栏。使用QMenu控件的成员函数addAction()添加QAction类的菜单栏 QAction *newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this); newAct->setShortcuts(QKeySequence::New); newAct->setStatusTip(tr("Create a new file")); QAction *openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this); openAct->setShortcuts(QKeySequence::Open); openAct->setStatusTip(tr("Open an existing file")); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAct); fileMenu->addAction(openAct); // connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); // connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
2、创建工具栏

3、创建锚街控件

4、设置中心组件

5、添加状态栏



---
多媒体
2018-09-18 15:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>> this->resize(300, 200); QPushButton *button = new QPushButton("Button", this); button->setGeometry(10, 10, 100, 30); QStateMachine *machine = new QStateMachine; QState *state1 = new QState(machine); state1->assignProperty(button, "geometry", QRect(10, 10, 100, 30)); machine->setInitialState(state1); QState *state2 = new QState(machine); state2->assignProperty(button, "geometry", QRect(250, 150, 100, 30)); QSignalTransition *transition1 = state1->addTransition( button, SIGNAL(clicked()), state2); //state1--->state2 transition1->addAnimation(new QPropertyAnimation(button, "geometry")); QSignalTransition *transition2 = state2->addTransition( button, SIGNAL(clicked()), state1); // transition-2 transition2->addAnimation(new QPropertyAnimation(button, "geometry")); machine->start();
多媒体
2018-09-18 14:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>> #include "widget.h" #include #include #include Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(300, 200); QPushButton *btn1 = new QPushButton("first", this); btn1->setGeometry(10, 10, 100, 30); QPushButton *btn2 = new QPushButton("second", this); btn2->setGeometry(10, 45, 100, 30); QPropertyAnimation *anim1 = new QPropertyAnimation(btn1, "geometry"); anim1->setDuration(2000); anim1->setStartValue(QRectF(10, 10, 100, 30)); anim1->setEndValue(QRectF(200, 150, 100, 30)); QPropertyAnimation *anim2 = new QPropertyAnimation(btn2, "geometry"); anim2->setDuration(2000); anim2->setStartValue(QRect(10, 45, 100, 30)); anim2->setEndValue(QRect(200, 195, 100, 30)); // sGroup = new QSequentialAnimationGroup; // Sequential Group // sGroup->addAnimation(anim1); // sGroup->addAnimation(anim2); pGroup = new QParallelAnimationGroup; // Parallel Group pGroup->addAnimation(anim1); pGroup->addAnimation(anim2); connect(btn1, SIGNAL(clicked()), this, SLOT(btn_clicked())); connect(btn2, SIGNAL(clicked()), this, SLOT(btn_clicked())); } void Widget::btn_clicked() { // sGroup->start(QPropertyAnimation::DeleteWhenStopped); //如果动画完成自动清理内存 //因此移动一次之后就算再次点击按钮也不会移动了 pGroup->start(QPropertyAnimation::DeleteWhenStopped); }

任意一个按钮点击都都会移动按钮,先移动按钮1,再移动按钮2

任意一个按钮点击都都会移动按钮:按钮1&按钮2并行移动

串行顺序运行动画对象



多媒体
2018-09-18 14:38:00
「深度学习福利」大神带你进阶工程师,立即查看>>> QPushButton *btn = new QPushButton("Button", this); btn->setGeometry(10, 10, 100, 30); QPropertyAnimation *animation; animation = new QPropertyAnimation(btn, "geometry", this); animation->setDuration(3000);//从起始坐标到结束坐标的时长[单位ms]3s,默认250ms animation->setStartValue(QRect(10, 10, 100, 30)); //设置起始坐标 animation->setEndValue(QRect(200, 150, 100, 30)); //结束坐标,当坐标值改变时,会发生valueChanged信号事件 animation->start();

多媒体
2018-09-18 13:55:00