基于JMF录制和播放视频源码
仅仅播放摄像头拍摄内容(修改版)
package org.bruce.myown_product;import java.awt.Component;import java.awt.Dimension;import java.awt.GridLayout;import java.awt.Panel;import javax.media.CaptureDeviceInfo;import javax.media.CaptureDeviceManager;import javax.media.Manager;import javax.media.MediaLocator;import javax.media.Player;import javax.media.protocol.DataSource;import javax.swing.JFrame;import javax.swing.JPanel;public class PlayCapturedVideo extends JPanel { private static final long serialVersionUID = -8040938476462674297L; // 本地摄像头的注册信息(通过JMF.exe获得) String url = "vfw:Microsoft WDM Image Capture (Win32):0"; // 获取的摄像头信息 private CaptureDeviceInfo captureDeviceInfo = null; // 从获取的摄像头信息中提取的摄像头地址 private MediaLocator mediaLocator = null; private DataSource datasource = null; public static Player player = null; // 构造函数! public PlayCapturedVideo() { Panel editor = new Panel(); editor.setPreferredSize(new Dimension()); editor.setLayout(null); editor.setLayout(new GridLayout(1, 3)); // CaptureDeviceInfo: 捕获设备信息 captureDeviceInfo = CaptureDeviceManager.getDevice(url); // MediaLocator: 媒体定位器。根据捕获设备信息(是一份设备的列表) 获取 媒体定位器 mediaLocator = captureDeviceInfo.getLocator(); try { datasource = Manager.createDataSource(mediaLocator); // 根据媒体定位器创建一个数据源 player = Manager.createRealizedPlayer(datasource); player.start(); // 调用 player.start() 以后就可以用那个一个可视的组件来呈现摄像头拍摄的景象了! Component comp = null; if ((comp = player.getVisualComponent()) != null) { this.add(comp); // 这个 this 继承了 Applet(相当于一个顶级容器) } } catch (Exception b) { b.printStackTrace(); } } public static void main(String[] args) { JFrame jf = new JFrame("用于呈现摄像头拍摄景象"); PlayCapturedVideo pcv = new PlayCapturedVideo(); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.add(pcv); jf.pack(); jf.setResizable(false); jf.setLocation(200, 200); jf.setVisible(true); }}
播放+录制代码(修改版)
package org.bruce.myown_product;import java.awt.BorderLayout;import java.awt.Component;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.File;import java.util.Vector;import javax.media.CaptureDeviceInfo;import javax.media.CaptureDeviceManager;import javax.media.DataSink;import javax.media.Format;import javax.media.Manager;import javax.media.MediaLocator;import javax.media.Player;import javax.media.Processor;import javax.media.ProcessorModel;import javax.media.control.FormatControl;import javax.media.format.VideoFormat;import javax.media.protocol.DataSource;import javax.media.protocol.FileTypeDescriptor;import javax.media.protocol.SourceCloneable;import javax.swing.JButton;import javax.swing.JFileChooser;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;// 这是我自己改写的一个示例,实现了简单的录制和再次录制功能!public class RecordVideo extends JFrame { private static final long serialVersionUID = -2570548473293856129L; private CaptureDeviceInfo captureDevice; private MediaLocator mediaLocator; private Component visualComponent; private Processor processor; private Player player; private DataSource source; private DataSource cloneableSource; private DataSource clonedDataSource; private DataSink dataSink; private JPanel videoPanel = new JPanel(new BorderLayout()); private JPanel controlPanel = new JPanel(); private JPanel contentPane; private JButton btnStart; private JButton btnStop; private JFileChooser fileChooser = new JFileChooser(); public RecordVideo() { this.setTitle("视频采集软件"); contentPane = (JPanel) this.getContentPane(); btnStart = new JButton("开始采集"); btnStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { start(); } }); btnStop = new JButton("停止采集"); btnStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { stop(); } }); btnStop.setEnabled(false); controlPanel.add(btnStart); controlPanel.add(btnStop); contentPane.add("South", controlPanel); this.captureDevice = this.getCaptureDeviceInfo(); try { mediaLocator = captureDevice.getLocator(); source = Manager.createDataSource(mediaLocator); // 在调用 createCloneableDataSource(source) 方法以后,上句代码中的 source 引用不再能被使用。 cloneableSource = Manager.createCloneableDataSource(source); player = Manager.createRealizedPlayer(cloneableSource); player.start(); } catch (Exception e) { this.processException(e); } visualComponent = player.getVisualComponent(); if (visualComponent != null) { videoPanel.add(visualComponent); } contentPane.add("North", videoPanel); // 打包使整个界面显得紧凑! this.pack(); // 注释掉这句代码的话,窗口就缩成一个小布丁儿了! this.setResizable(false); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setLocationRelativeTo(null); this.setVisible(true); } private void start() { // ①第一个参数 clonedDataSource = ((SourceCloneable)cloneableSource).createClone(); // ②第二个参数 FormatControl formatControl = (FormatControl) player.getControl("javax.media.control.FormatControl"); Format defaultFormat = formatControl.getFormat(); // defaultFormat的值为: MJPG, 640x480, FrameRate=15.0, Length=921600 0 extra bytes // ③第三个参数 FileTypeDescriptor outputFileType = new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME); //.QUICKTIME ProcessorModel processorModel = new ProcessorModel(clonedDataSource, new Format[] { defaultFormat }, outputFileType); // DataSource 可能为 mixed, 故用数组 try { processor = Manager.createRealizedProcessor(processorModel); } catch (Exception e) { this.processException(e); } String locatorString = this.getLocatorString(); //locatorString 表示存储录像数据的文件的路径 if (locatorString == null) { return; } MediaLocator dest = new MediaLocator(locatorString); DataSource outputDataSource = processor.getDataOutput(); try { // 本地文件传输 dataSink = Manager.createDataSink(outputDataSource, dest); dataSink.open(); dataSink.start(); } catch (Exception e) { this.processException(e); } processor.start(); btnStart.setEnabled(false); btnStop.setEnabled(true); } private void stop() { processor.close(); processor.deallocate(); // 必须在processor 关闭并调用这个方法以后,录像文件才能保存下来 dataSink.close(); processor = null; // 清空 processor,回收无用的内存! btnStop.setEnabled(false); btnStart.setEnabled(true); } private void processException(Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, e.toString(), "错误", JOptionPane.ERROR_MESSAGE); System.exit(0); } private String getLocatorString() { if (JFileChooser.APPROVE_OPTION != fileChooser.showSaveDialog(this)) { return null; } File file = fileChooser.getSelectedFile(); if (file == null) { return null; }System.out.println(file); // C:/Documents and Settings/Administrator/桌面/123 String locatorString = file.getAbsolutePath();System.out.println(locatorString); // C:/Documents and Settings/Administrator/桌面/123 if (!locatorString.endsWith(".QUICKTIME")) { // 没有后缀的话就程式化地加上 .QUICKTIME 的后缀! locatorString += ".QUICKTIME"; } locatorString = "file://" + locatorString;System.out.println(locatorString); // file://C:/Documents and Settings/Administrator/桌面/123.QUICKTIME return locatorString; } @SuppressWarnings("unchecked") private CaptureDeviceInfo getCaptureDeviceInfo() { // 以下是原来的代码,害惨我了,老是找不到我的摄像头,原来是因为我的摄像头输出不属于 RGB 格式! // Format videoFormat = new VideoFormat(VideoFormat.RGB); Format videoFormat = new VideoFormat(VideoFormat.MJPG); // 在改成 MJPG 后顺利找到我的视频设备 Vector<CaptureDeviceInfo> deviceList = CaptureDeviceManager.getDeviceList(videoFormat); if (deviceList.size() < 1) { JOptionPane.showMessageDialog(this, "未检测到视频输入设备!", "错误", JOptionPane.ERROR_MESSAGE); System.exit(0); } String[] deviceNames = new String[deviceList.size()]; for (int i = 0; i < deviceList.size(); i++) { deviceNames[i] = deviceList.get(i).getName(); } String deviceName = (String) JOptionPane.showInputDialog(this, "请选择视频输入设备", "请选择", JOptionPane.QUESTION_MESSAGE, null, deviceNames, deviceNames[0]); if (deviceName == null) { System.exit(0); } CaptureDeviceInfo captureDevice = null; for (int i = 0; i < deviceList.size(); i++) { captureDevice = deviceList.get(i); if (deviceName.equals(captureDevice.getName())) { return captureDevice; } } return null; } public static void main(String[] args) { new RecordVideo(); }}
播放+录制+照相代码(简化版)
package org.bruce.most_valuable;import java.applet.Applet;import java.awt.BorderLayout;import java.awt.Component;import java.awt.Dimension;import java.awt.FileDialog;import java.awt.Frame;import java.awt.Graphics;import java.awt.GridLayout;import java.awt.Image;import java.awt.Panel;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.image.RenderedImage;import java.io.File;import java.io.IOException;import javax.imageio.ImageIO;import javax.media.Buffer;import javax.media.CaptureDeviceInfo;import javax.media.CaptureDeviceManager;import javax.media.ConfigureCompleteEvent;import javax.media.ControllerEvent;import javax.media.ControllerListener;import javax.media.DataSink;import javax.media.Manager;import javax.media.MediaLocator;import javax.media.Player;import javax.media.Processor;import javax.media.RealizeCompleteEvent;import javax.media.control.FrameGrabbingControl;import javax.media.format.VideoFormat;import javax.media.protocol.DataSource;import javax.media.protocol.FileTypeDescriptor;import javax.media.protocol.SourceCloneable;import javax.media.util.BufferToImage;import javax.swing.JButton;import javax.swing.JComponent;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;public class WebCamSwingSimplified extends Applet implements ActionListener { private static final long serialVersionUID = -8040938476462674297L; // 本地摄像头的注册信息(通过JMF.exe获得) String url = "vfw:Microsoft WDM Image Capture (Win32):0"; // 获取的摄像头信息 private CaptureDeviceInfo captureDeviceInfo = null; // 从获取的摄像头信息中提取的摄像头地址 private MediaLocator mediaLocator = null; // 原始的数据源 private DataSource dataSource = null; // 由原始数据源转变成的,可以被克隆的数据源 private DataSource cloneableDataSource = null; // 由可以克隆的数据源 cloneableDataSource 克隆出来的 clonedDataSource private DataSource clonedDataSource = null; // 用来播放的 player public static Player player = null; // 处理录制的视频的 Processor private Processor processor = null; // 保存录制数据的数据池(datasink: 数据接收装置; 数据接收器) private DataSink dataSink = null; // StateHelper 是处理 Player 或 Processor 的同步状态转换的一个很有用的类。 private StateHelper stateHelper = null; private ImagePanel imagePanel = null; // buffer 用于截取实时图像的缓冲区 private Buffer buffer = null; // 将 buffer 转换为图像的“中间变量” private BufferToImage buffer2image = null; // 拍下来的照片赋给img private Image img = null; // 保存图片的宽度和高度 private int imgWidth = 320; private int imgHeight = 240; // 拍照按钮 private JButton capture = null; // 保存照片按钮 private JButton save = null; // 录像按钮 private JButton recordVideo = null; // 停止录像按钮 private JButton quitRecord = null; /* * 此 WebCamSwing 函数完成的功能: 创建一个对话框。 将摄像头的物理地址转化为电脑能够识别的 medialocator(媒体定位器) , * 并且通过 medialocator 产生一个数据源 dataSource , 通过这个 dataSource 数据源生成 player 用来播放视频。 * 然后将数据源进行处理,产生一个能够被克隆的 cloneableDataSource, * 再这个 cloneableDataSource 进行克隆,产生一个 clonedDataSource 数据源, 用于后面的 processor 录制视频 */ public WebCamSwingSimplified() { Panel editor = new Panel(); editor.setPreferredSize(new Dimension()); editor.setLayout(null); editor.setLayout(new GridLayout(1, 3)); // ImagePanel 初始化 imagePanel = new ImagePanel(); // 各 Button 的初始化 capture = new JButton("拍照"); capture.addActionListener(this); save = new JButton("保存照片"); save.addActionListener(this); recordVideo = new JButton("开始录像"); recordVideo.addActionListener(this); quitRecord = new JButton("停止录像"); quitRecord.addActionListener(this); captureDeviceInfo = CaptureDeviceManager.getDevice(url); // CaptureDeviceInfo: 捕获设备信息 // 根据捕获设备信息(是一份设备的列表) 获取 媒体定位器 mediaLocator = captureDeviceInfo.getLocator(); // MediaLocator: 媒体定位器 try { dataSource = Manager.createDataSource(mediaLocator); // 根据媒体定位器创建一个数据源 cloneableDataSource = Manager.createCloneableDataSource(dataSource); clonedDataSource = ((SourceCloneable)cloneableDataSource).createClone(); player = Manager.createRealizedPlayer(cloneableDataSource); player.start(); // 调用 player.start() 以后就可以用那个一个可视的组件来呈现摄像头拍摄的景象了! Component comp = null; if ((comp = player.getVisualComponent()) != null) { this.add(comp); // 这个 this 继承了 Applet(相当于一个顶级容器) } } catch (Exception e) { e.printStackTrace(); } // 将4个按钮添加到主界面上 JPanel rightJPanel = new JPanel(); rightJPanel.setLayout(new BorderLayout()); JPanel buttonJPanel = new JPanel(new GridLayout(1, 4)); // 1行4列 buttonJPanel.add(capture); buttonJPanel.add(save); buttonJPanel.add(recordVideo); buttonJPanel.add(quitRecord); rightJPanel.add(buttonJPanel, BorderLayout.NORTH); rightJPanel.add(imagePanel, BorderLayout.SOUTH); // 这个是干什么用的?是拍照时看效果用的 this.add(rightJPanel); } /* * 点击拍照 * ActionEvent: 指示发生了组件定义的动作的语义事件。 当特定于组件的动作(比如 * 被按下)发生时, 由组件(比如 Button)生成此高级别事件。 事件被传递给每一个 ActionListener 对象, 这些对 * 象是使用组件的 addActionListener 方法注册的,用以接收这类事件。 注:要使用键盘在 Button 上触发 * ActionEvent,请使用空格键。 */ public void actionPerformed(ActionEvent e) { // 捕获 ActionEvent,将其转化为 JComponent 类,应该转化为是被单击的 “按钮” 的意思 // 这种写法比用 setActionCommand("capture") 要节省代码(当然是在按钮比较少的 时候)! JComponent c = (JComponent) e.getSource(); // ① 拍照 if (c == capture) { // 取得对 Player 的控制(反射机制的典型应用) FrameGrabbingControl fgc = (FrameGrabbingControl)player .getControl("javax.media.control.FrameGrabbingControl"); buffer = fgc.grabFrame(); // System.out.println(buffer.getFormat()); buffer2image = new BufferToImage((VideoFormat) buffer.getFormat()); img = buffer2image.createImage(buffer); // 在 imagePanel 上显示拍下来的样照,由用户决定是否保存下来 imagePanel.setImage(img); } // 如果还没有拍照就保存的话,弹出对话框进行提醒! else if (c == save && img == null) { JOptionPane.showMessageDialog(null, "请先拍照再保存"); } // ②保存照片(弹出提示窗口让用户输入保存图片的路径) else if (c == save && img != null) { Frame savePhotoFrame = new Frame("保存"); FileDialog savePhotoFileDialog = new FileDialog(savePhotoFrame, "保存图像文件", FileDialog.SAVE); savePhotoFileDialog.setFile("*.GIF"); // 用于设置保存图片的格式 savePhotoFileDialog.setVisible(true); String savepath = savePhotoFileDialog.getDirectory(); String savename = savePhotoFileDialog.getFile(); // ImageIO.write是将图片保存到上面已经写好的路径里,并且用上面的savename命名 try { ImageIO.write((RenderedImage)img, "GIF", new File(savepath + savename)); } catch (IOException e1) { e1.printStackTrace(); } img = null; // 将 img 清空(回收所占的内存资源) } // ③ 如果单击了“开始录像”按钮,但是processor不为空,那么就弹出提示表明正在录像 else if (c == recordVideo && processor != null) { JOptionPane.showMessageDialog(null, "正在录像!"); } // ④ 如果单击了“开始录像”按钮,且processor为空,那么就开始录像 else if (c == recordVideo && processor == null) { Frame saveVideoFrame = new Frame("保存视频文件"); FileDialog saveVideoFileDialog = new FileDialog(saveVideoFrame, "保存视频文件", FileDialog.SAVE); saveVideoFileDialog.setFile("*.QUICKTIME"); saveVideoFileDialog.setVisible(true); String videoFileSavePath = saveVideoFileDialog.getDirectory(); String videoFileSaveName = saveVideoFileDialog.getFile(); // 如果输入了保存名字(没有点取消)的话,才可运行 if (videoFileSaveName != null) { videoFileSavePath = videoFileSavePath.replace("//", "/"); try { // player 用 cloneableDataSource 数据源,processor 用 clonedDataSource 的数据源 processor = Manager.createProcessor(clonedDataSource); stateHelper = new StateHelper(processor); } catch (Exception e1) { e1.printStackTrace(); System.exit(-1); } // configure the processor, 让 processor 进入 configured 状态 if (!stateHelper.configure(10000)) { System.out.println("configure wrong!"); System.exit(-1); } // 设置存储录像文件的格式为 .QUICKTIME processor.setContentDescriptor(new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME)); // realize the processor,让 processor 进入 realized 状态 if (!stateHelper.realize(10000)) { System.out.println("realize wrong!"); System.exit(-1); } // 取得 processor 的输出,并且启动 processor // DataSource 被送进 processor 处理,输出地时候还是以 DataSource 的类型输出! DataSource outSource = processor.getDataOutput(); // 替换前:file:///C:/Documents and Settings/Administrator/桌面/12312.QUICKTIME // 替换后:file:///C:/Documents and Settings/Administrator/桌面/12312.QUICKTIME String destinationString = "file:///" + videoFileSavePath + videoFileSaveName; // MediaLocator 类的两个构造方法: // ①MediaLocator(java.lang.String locatorString) // ②MediaLocator(java.net.URL url) MediaLocator destination = new MediaLocator(destinationString); processor.start(); try { dataSink = Manager.createDataSink(outSource, destination); dataSink.open(); dataSink.start(); } catch (Exception e1) { e1.printStackTrace(); System.exit(-1); } } } // 停止录像(如果还没开始录像就单击这个按钮的话,弹出提示框提示)! else if (c == quitRecord && processor == null) { JOptionPane.showMessageDialog(null, "请先单击录像才能停止!"); } // ⑤停止录像 else if (c == quitRecord && processor != null) { // 如果要是能够连续录像,关键在于两点: // 1、重新设置 clonedDataSource ,我认为在 clonedDataSource 被调用后 ,clonedDataSource被改变了 // 2、清空 processor processor.close(); processor.deallocate(); dataSink.close(); clonedDataSource = ((SourceCloneable) cloneableDataSource).createClone(); processor = null; } } public class StateHelper implements ControllerListener { Player xplayer = null; boolean configured = false; boolean realized = false; // boolean prefetched = false; // boolean eom = false;// End of media. boolean failed = false; boolean closed = false; public StateHelper(Player p) { xplayer = p; p.addControllerListener(this); } public boolean configure(int timeOutMillis) { // RealizeCompleteEvent 发生了的话使 ce 事件与之比较,若相等,那么 realized 为 true。 /* * 监听 ConfigureCompleteEvent 和 ConfigureCompleteEvent 事件的发生。 * 如 ConfigureCompleteEvent 事件发生,那么就会赋给 configured 为 ture, 使得 public * boolean configure 方法中的 while (!configured && !failed){} 这个循环退出。 */ long startTime = System.currentTimeMillis(); synchronized (this) { if (xplayer instanceof Processor) { ((Processor) xplayer).configure(); } else { return false; } while (!configured && !failed) { try { wait(timeOutMillis); } catch (InterruptedException ex) { ex.printStackTrace(); } if (System.currentTimeMillis() - startTime > timeOutMillis) { break; } } } return configured; } public boolean realize(int timeOutMillis) { long startTime = System.currentTimeMillis(); synchronized (this) { xplayer.realize(); while (!realized && !failed) { try { wait(timeOutMillis); } catch (InterruptedException ie) { ie.printStackTrace(); } if (System.currentTimeMillis() - startTime > timeOutMillis) { break; } } } return realized; } public synchronized void controllerUpdate(ControllerEvent ce) { if (ce instanceof RealizeCompleteEvent) { realized = true; } else if (ce instanceof ConfigureCompleteEvent) { configured = true; } else { return; } notifyAll(); } } // 显而易见,ImagePanel 是用于呈现 Image 的 Panel!实践检验我错了! // 把 Panel 改成 JPanel 妈的竟然显示不出来了! class ImagePanel extends Panel { private static final long serialVersionUID = -5371206459688057671L; private Image ImagePanelImg = null; public ImagePanel() { setLayout(null); setSize(imgWidth, imgHeight); } public void setImage(Image img) { this.ImagePanelImg = img; this.setVisible(true); repaint(); } public void update(Graphics graphics) { if (ImagePanelImg != null) { graphics.drawImage(ImagePanelImg, 0, 0, this); } } } public static void main(String[] args) { JFrame jf = new JFrame("拍照程序"); WebCamSwingSimplified cf = new WebCamSwingSimplified(); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.add("Center", cf); jf.pack(); jf.setResizable(false); jf.setVisible(true); }}
基于JMF录制和播放视频源码相关推荐
- php+api抖音随机播放视频源码
简介: php+api抖音随机播放视频源码,前端纯静态html+css,数据采用php+API接口调用第三方数据,不需要配置环境,上传即用. 源码体积小,视频采集自网络,无视频资源,内置多条播放线路. ...
- 基于Java毕业设计音乐播放平台源码+系统+mysql+lw文档+部署软件
基于Java毕业设计音乐播放平台源码+系统+mysql+lw文档+部署软件 基于Java毕业设计音乐播放平台源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S架构 开发语言: ...
- 【Qt 开源音视频框架模块QtAV】03:QTAV主要接口展示以及播放器源码分享
介绍 QtAV 是一个基于 Qt 和 FFmpeg 的跨平台.高性能多媒体播放框架,能够帮助你轻而易举地编写出一个播放器. [Qt 开源音视频框架模块QtAV]01:介绍.编译以及简单使用 因为网上使 ...
- Python基于OpenCV的实时疲劳检测[源码&演示视频&部署教程]
1.图片演示 2.视频演示 [项目分享]Python基于OpenCV的实时疲劳检测[源码&演示视频&部署教程]_哔哩哔哩_bilibili 3.检测方法 1)方法 与用于计算眨眼的传统 ...
- 最新哔哩哔哩视频弹幕播放器源码+带后台/亲测无错误
正文: 最新哔哩哔哩视频弹幕播放器源码+带后台/亲测无错误,此款播放器源码添加及修复了很多功能,且所有源码均本地化,不存在外链的情况,测试环境:PHP7.0+MySQL5.6 安装教程: 1,访问/d ...
- 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇...
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- 最新哔哩bilibili视频弹幕播放器源码+完整无错带后台/亲测
正文: 最新哔哩bilibili视频弹幕播放器源码+完整无错带后台/亲测,此款播放器源码添加及修复了很多功能,且所有源码均本地化,不存在外链的情况,测试环境:PHP7.0+MySQL5.6 播放器跨域 ...
- PHP哔哩哔哩bilibili视频弹幕播放器源码 带后台版本
简介: PHP哔哩哔哩bilibili视频弹幕播放器源码 带后台版本 此款播放器源码添加及修复了很多功能,且所有源码均本地化,不存在外链的情况. 环境:PHP7.0+MySQL5.6 优化修复功能: ...
- iOS短视频源码音频采集过程中的音效实现
在移动短视频直播中, 声音是主播和观众互动的重要途径之一, 为了丰富直播的内容,大家都会想要在声音上做一些文章, 在短视频源码采集录音的基础上玩一些花样. 比如演唱类的直播间中, 主播伴随着背景音乐演 ...
最新文章
- JMeter自学笔记3-创建自己的第一个测试用例
- 客户关系管理:客户关系选型
- 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)
- 中山大学曾兆阳_2010—2011学年度中山大学信科院优秀学生奖学金评选结果名单...
- 致程序猿:专业课老师没教的,都在这8本书里了
- 大华的支持rtmp推流吗_RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播...
- oracle startup作用,【学习笔记】Oracle打补丁后startup migrate、startup upgrade区别分析...
- Kettle 简介和实例
- 【java学习之路】(java SE篇)(练习)关于常用类的心血来潮小练习
- [笔记一]Essential JavaScript Design Patterns For Beginners
- 2018年高考631选计算机,2021年高考650分可以上什么大学 650分左右的院校
- pcie协议_如何通过PCIE协议实现FPGA 配置?详情请戳这里!
- 三角网导线平差实例_三角网闭合导线平差计算表0
- 新东方 词根词缀 excel_背单词 | 新东方的杨鹏老师用什么方法?
- 存储过程,函数——mysql批量添加随机生成用户信息(生成随机数)
- 12个CSS高级技巧汇总
- 移动简报026—智慧餐厅出新服务:吃饭用微信就可排队;支付宝上线银行卡安全险:盗刷最高获赔 50 万;高德正式发布车载导航App...
- HTML5 基础练习题总结(一)
- 服务监控可以监控哪些
- AICS188-Project6-Q1-Q3
热门文章
- win10x64下安装anaconda、配置tensorflow并在jupyter和pycharm中使用tf
- 计算机专业会涉及数学吗,数学不好的人还适合学计算机吗?
- 元宇宙持续火爆,各地纷纷布局元宇宙
- 基于Java的学生兼职平台设计与实现
- 8Manage PPM,助力北京测威提升项目运营效益
- 华为初面+综合面试(技术面)
- 资源分享:17 张程序员专属高清壁纸
- Agarose bound-WFA,WFL;琼脂糖结合紫藤凝集素(WFA,WFL)
- C#实现图书管理系统(课程设计)——第五步、查询界面及操作
- 解析春运玄学:携程飞猪去哪儿们的抢票加速包,到底灵不灵?