问题1:
FFmpeg采集窗口,放入Frame中缩小显示,会失真,需要等比例压缩每一帧图片。
问题2:
窗口放大缩小时,推的视频流会出现卡半屏现象,需要检测窗口变化,重新定义变化后的新采集器,覆盖变化前旧采集器,进行窗口切换推流。
或不多说,上代码:
/**
* 添加直播素材--#窗口捕捉
*/
public class WindowSnap {
private static String pushPath = "";
private static Boolean ifLive = false;
private static int frameRate = 25;// 录制的帧率
public void setOutputPath(Boolean ifLive) {
this.ifLive = ifLive;
}
// static FFmpegFrameGrabber grabber = null;
// static FrameRecorder recorder;
static JPanel contentPanel;
static ExecutorTab executorTab;
static int tabItemIndex=0;
// 获取屏幕大小
static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
static int width = screenSize.width;
static int height = screenSize.height;
static int i = 1;
public void setI(int i) {
this.i = i;
}
/**
* 录制指定窗口到视频窗口展示
* 动态捕捉指定窗口画面,现在可以动态捕捉指定窗口所在位置画面,相当于动态局部录屏;后期须改进优化
* @param wText 捕捉窗口名称
*/
public void doCaptureWindow(String wText, JPanel contentPanel,String pushPath,int tabItemIndex, ExecutorTab executorTab) {
this.pushPath = pushPath;
this.tabItemIndex = tabItemIndex;
this.executorTab = executorTab;
this.contentPanel = contentPanel;
CanvasFrame canvasFrame = new CanvasFrame("11111");// javacv提供的图像展现窗口
//内嵌到 内部窗口
canvasFrame.setVisible(false);
Component canvas = canvasFrame.getCanvas();
contentPanel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
// 获取窗口的新大小
Dimension newSize = e.getComponent().getSize();
//调整录制窗口展示与窗口等比例变化
int roundWidth = (int) Math.round(newSize.getWidth());
int roundHeight = (int) Math.round(newSize.getHeight());
canvasFrame.setCanvasSize(roundWidth, roundHeight); // 设置窗口大小
}
});
contentPanel.setLayout(null);
contentPanel.add(canvas);
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
startWindowCapture(canvasFrame,wText);
}
});
executorService.shutdown();
}
/**
* 采集窗口
* @param canvasFrame
* @param wText
*/
static void startWindowCapture(CanvasFrame canvasFrame, String wText) throws Exception {
ExecutorEntity executorWindow = executorTab.getTabList().get(tabItemIndex).getExecutorWindow();
executorWindow.setExecutor(Executors.newSingleThreadExecutor());
executorWindow.getExecutor().execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
//创建采集器
executorWindow.setGrabber(new FFmpegFrameGrabber("title="+wText));
executorWindow.getGrabber().setFormat("gdigrab");
executorWindow.getGrabber().setFrameRate(frameRate);// 帧获取间隔
//开启采集器
try {
executorWindow.getGrabber().start();
} catch (Exception e) {
try {
executorWindow.getGrabber().restart(); //一次重启尝试
} catch (Exception e2) {
throw e;
}
}
executorWindow.setRecorder(new FFmpegFrameRecorder(pushPath, width, height)); //输出路径,画面高,画面宽
executorWindow.getRecorder().setVideoCodec(avcodec.AV_CODEC_ID_H264); //设置编码格式
executorWindow.getRecorder().setFormat("flv");
executorWindow.getRecorder().setFrameRate(frameRate);
Frame frame = null;
Java2DFrameConverter converter = new Java2DFrameConverter();
//检测窗口变化
WinDef.HWND parentWindow = User32.INSTANCE.FindWindow(null, wText);
WinDef.RECT rect = new WinDef.RECT();
//设置初始值
int rectWidth = -1;
int rectHeight = -1;
while ((frame = executorWindow.getGrabber().grab()) != null) {
User32.INSTANCE.GetWindowRect(parentWindow, rect);
//窗口宽
int nowRectWidth = rect.right- rect.left;
//窗口高
int nowRHeight = rect.bottom- rect.top;
if (rectWidth != -1){//已经赋值过了
if (rectWidth != nowRectWidth || rectHeight != nowRHeight){//窗口改变,新建采集器,关闭老采集器,使用新采集器采集
System.out.println("新老采集器开始交替!");
FFmpegFrameGrabber grabber2 = new FFmpegFrameGrabber("title="+wText);
grabber2.setFormat("gdigrab");
grabber2.setFrameRate(frameRate);// 帧获取间隔
// 设置比特率
grabber2.start();
//重新赋值
frame = grabber2.grabFrame();
executorWindow.getGrabber().close();
executorWindow.setGrabber(grabber2);
System.out.println("新老采集器交替完成!");
}
}
//重新赋值,宽高
rectWidth = nowRectWidth;
rectHeight = nowRHeight;
BufferedImage bufferedImage = converter.getBufferedImage(frame);
//等比例压缩图片
BufferedImage newBufferedImage = disposeImage(bufferedImage, (int)canvasFrame.getCanvasSize().getWidth(), (int)canvasFrame.getCanvasSize().getHeight());
//预览
canvasFrame.showImage(newBufferedImage);
if (ifLive) {
if(i==1){
i--;
//开启一次录制器
try {
executorWindow.getRecorder().start();
} catch (java.lang.Exception e) {
try {
if (executorWindow.getRecorder() != null) { //尝试重启录制器
executorWindow.getRecorder().stop();
executorWindow.getRecorder().start();
}
} catch (java.lang.Exception e1) {
e.printStackTrace();
}
}
}
Frame rotatedFrame = converter.convert(bufferedImage);
executorWindow.getRecorder().record(rotatedFrame);
}
Thread.sleep(1000/frameRate); //30毫秒/帧
}
//关闭采集器
executorWindow.getGrabber().close();
}
});
// canvasFrame.dispose();
}
public void stopWindow(int tabItemIndex) throws Exception {
try {
ExecutorEntity executorWindow = executorTab.getTabList().get(tabItemIndex).getExecutorWindow();
ifLive = false;
if (executorWindow.getExecutor() != null){
executorWindow.getExecutor().shutdown();
}
executorWindow.getGrabber().close();
if(executorWindow.getRecorder() != null){
executorWindow.getRecorder().close();
}
} catch (FrameGrabber.Exception e) {
throw new RuntimeException(e);
}
}
/**
*BufferedImage 转换 IplImage
* @param bufferedImage
* @return
*/
public static IplImage bufferedImageToIplImage(BufferedImage bufferedImage) {
Java2DFrameConverter converter = new Java2DFrameConverter();
Frame frame = converter.convert(bufferedImage);
OpenCVFrameConverter.ToIplImage iplConverter = new OpenCVFrameConverter.ToIplImage();
return iplConverter.convert(frame);
}
/**
*
* @param image
* @return
*/
private static Frame convertBufferedImageToFrame(BufferedImage image) {
Java2DFrameConverter converter = new Java2DFrameConverter() {
public Frame convert(BufferedImage image, Frame frame) {
if (frame == null) {
frame = new Frame(image.getWidth(), image.getHeight(), Frame.DEPTH_UBYTE, 3);
}
super.convert(image);
frame.imageDepth = Frame.DEPTH_UBYTE;
frame.imageChannels = 3;
frame.imageStride = frame.imageWidth * 3;
return frame;
}
};
return converter.convert(image);
}
private static InputStream bufferedImageToInputStream(BufferedImage image) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
return new ByteArrayInputStream(os.toByteArray());
}
public void stopWindowRecord() {
}
private static BufferedImage disposeImage(BufferedImage src, int new_w, int new_h) {
// 得到图片
int old_w = src.getWidth();
// 得到源图宽
int old_h = src.getHeight();
// 得到源图长
BufferedImage newImg = null;
// 判断输入图片的类型
switch (src.getType()) {
case 13:
// png,gifnewImg = new BufferedImage(new_w, new_h,
// BufferedImage.TYPE_4BYTE_ABGR);
break;
default:
newImg = new BufferedImage(new_w, new_h, BufferedImage.TYPE_INT_RGB);
break;
}
Graphics2D g = newImg.createGraphics();
// 从原图上取颜色绘制新图
g.drawImage(src, 0, 0, old_w, old_h, null);
g.dispose();
// 根据图片尺寸压缩比得到新图的尺寸
newImg.getGraphics().drawImage(
src.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0, 0,
null);
// 调用方法输出图片文件
return newImg;
}
}