提交 d43e1a32 编写于 作者: E eguid

1、支持截图缩放功能,可以设置width和height来达到缩放效果,支持放大和缩小

2、修复截图检索流时间超时导致的截图失败问题
3、修复非关键帧解码失败问题
4、修复截图服务示例中不返回base64编码的问题
上级 212b5cdc
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
<modules> <modules>
<module>videoimageshot</module> <module>videoimageshot</module>
<module>videoRecorder</module> <!--
<module>videoRecorder</module> -->
</modules> </modules>
<dependencies> <dependencies>
......
...@@ -11,10 +11,11 @@ ...@@ -11,10 +11,11 @@
</parent> </parent>
<groupId>cc.eguid.cv.corelib</groupId> <groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoRecorder</artifactId> <artifactId>videoRecorder</artifactId>
<version>1.1.0-2019.3.25</version> <version>2019.3.25</version>
<name>videoRecorder</name> <name>videoRecorder</name>
<url>https://blog.eguid.cc</url> <url>https://blog.eguid.cc</url>
<description>2018年10月17日 <description>
2018年10月17日
1、修复javacv录制mp4视频的编码问题,修改为强制使用h264编码 1、修复javacv录制mp4视频的编码问题,修改为强制使用h264编码
2018年10月16日 2018年10月16日
1、增加历史任务持久化接口,原有内置历史任务存储实现转移到DefaultRecordInfoStorage中,如果未指定持久化,则使用默认存储器 1、增加历史任务持久化接口,原有内置历史任务存储实现转移到DefaultRecordInfoStorage中,如果未指定持久化,则使用默认存储器
......
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:28 CST 2019
version=1.1.0-2019.3.25
groupId=cc.eguid.cv.corelib
m2e.projectName=videoRecorder
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\corelib\\videoRecorder
artifactId=videoRecorder
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>corelib</artifactId>
<version>1.0</version>
</parent>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoRecorder</artifactId>
<version>1.1.0-2019.3.25</version>
<name>videoRecorder</name>
<url>https://blog.eguid.cc</url>
<description>2018年10月17日
1、修复javacv录制mp4视频的编码问题,修改为强制使用h264编码
2018年10月16日
1、增加历史任务持久化接口,原有内置历史任务存储实现转移到DefaultRecordInfoStorage中,如果未指定持久化,则使用默认存储器
2、增加目录设置,如果目录不为空,则使用该目录作为录像存储目录
2018年10月10日
完成主体程序开发,并完成整体流程测试
基本架构:
任务管理器(管理单元)--&gt;管理一个任务处理器对象池
| |
持久化接口 任务处理器(控制单元,预处理和控制工作子线程处理)----&gt;工作子线程(计算单元)
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
</dependency>
-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<exclusions>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flycapture</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libdc1394</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>videoinput</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>librealsense</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>artoolkitplus</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flandmark</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>leptonica</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>tesseract</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
</dependency>
-->
</dependencies>
</project>
...@@ -11,12 +11,17 @@ ...@@ -11,12 +11,17 @@
</parent> </parent>
<groupId>cc.eguid.cv.corelib</groupId> <groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoimageshot</artifactId> <artifactId>videoimageshot</artifactId>
<version>1.1.0-2019.3.25</version> <version>2019.4.18</version>
<name>videoimageshot</name> <name>videoimageshot</name>
<url>https://blog.eguid.cc</url> <url>https://blog.eguid.cc</url>
<description> <description>
版本更新 版本更新
2019年4月18日
1、支持截图缩放功能,可放大也可以缩小
2、修复因为视频检索超时和缓存上限设置导致无法截图的bug
3、修复非关键帧解码失败问题,只能截关键帧
4、调整FFmpeg日志等级,不再显示非错误的FFmpeg日志
2018.9.21 2018.9.21
1、多项性能优化 1、多项性能优化
①base64转换性能优化 ①base64转换性能优化
......
...@@ -12,20 +12,6 @@ import cc.eguid.cv.corelib.videoimageshot.threaddata.CurrentThreadData; ...@@ -12,20 +12,6 @@ import cc.eguid.cv.corelib.videoimageshot.threaddata.CurrentThreadData;
* *
*/ */
public class FFmpegScreenshot implements Screenshot { public class FFmpegScreenshot implements Screenshot {
@Override
public boolean shot(String url, String imgurl, String format) throws IOException {
if (format == null) {
format =CurrentThreadData.DETAULT_FORMAT;
}
BufferedImage img = CurrentThreadData.grabber.get().grabBufferImage(url);
if (img != null) {
JavaImgConverter.saveImage(img, format, imgurl);
return true;
}
return false;
}
@Override @Override
public boolean shot(String url, String imgurl) throws IOException { public boolean shot(String url, String imgurl) throws IOException {
...@@ -36,25 +22,23 @@ public class FFmpegScreenshot implements Screenshot { ...@@ -36,25 +22,23 @@ public class FFmpegScreenshot implements Screenshot {
} }
return shot(url, imgurl, fomrat); return shot(url, imgurl, fomrat);
} }
@Override @Override
public String getImgBase64(String url, String format) throws IOException { public boolean shot(String url, String imgurl, String format) throws IOException {
if (format == null) { return shot(url, imgurl, format, null, null);
format =CurrentThreadData.DETAULT_FORMAT;
}
BufferedImage img = CurrentThreadData.grabber.get().grabBufferImage(url);
// ByteBuffer buffer=CurrentThreadData.grabber.get().grabBuffer(url);
if (img!= null) {
String base64=JavaImgConverter.bufferedImage2Base64(img, format);
return base64;
}
return null;
} }
@Override @Override
public String getImgBase64(String url) throws IOException { public String getImgBase64(String url) throws IOException {
return getImgBase64(url, null); return getImgBase64(url, null);
} }
@Override
public String getImgBase64(String url, String format) throws IOException {
return getImgBase64(url, format, null, null);
}
@Override @Override
public String shotAndGetBase64(String url, String imgurl)throws IOException { public String shotAndGetBase64(String url, String imgurl)throws IOException {
...@@ -63,10 +47,42 @@ public class FFmpegScreenshot implements Screenshot { ...@@ -63,10 +47,42 @@ public class FFmpegScreenshot implements Screenshot {
@Override @Override
public String shotAndGetBase64(String url, String imgurl, String format) throws IOException { public String shotAndGetBase64(String url, String imgurl, String format) throws IOException {
return shotAndGetBase64(url,imgurl,format,null,null);
}
@Override
public boolean shot(String url, String imgurl, String format, Integer width, Integer height) throws IOException {
if (format == null) {
format =CurrentThreadData.DETAULT_FORMAT;
}
BufferedImage img = CurrentThreadData.grabber.get().setWidth(width).setHeight(height).grabBufferImage(url);
if (img != null) {
JavaImgConverter.saveImage(img, format, imgurl);
return true;
}
return false;
}
@Override
public String getImgBase64(String url, String format, Integer width, Integer height) throws IOException {
if (format == null) {
format =CurrentThreadData.DETAULT_FORMAT;
}
BufferedImage img = CurrentThreadData.grabber.get().setWidth(width).setHeight(height).grabBufferImage(url);
if (img!= null) {
String base64=JavaImgConverter.bufferedImage2Base64(img, format);
return base64;
}
return null;
}
@Override
public String shotAndGetBase64(String url, String imgurl, String format, Integer width, Integer height)
throws IOException {
if (format == null) { if (format == null) {
format = CurrentThreadData.DETAULT_FORMAT; format = CurrentThreadData.DETAULT_FORMAT;
} }
BufferedImage img =CurrentThreadData.grabber.get().grabBufferImage(url); BufferedImage img =CurrentThreadData.grabber.get().setWidth(width).setHeight(height).grabBufferImage(url);
if (img != null) { if (img != null) {
JavaImgConverter.saveImage(img, format, imgurl); JavaImgConverter.saveImage(img, format, imgurl);
return JavaImgConverter.bufferedImage2Base64(img, format); return JavaImgConverter.bufferedImage2Base64(img, format);
......
...@@ -11,22 +11,33 @@ import java.io.IOException; ...@@ -11,22 +11,33 @@ import java.io.IOException;
public interface Screenshot { public interface Screenshot {
/** /**
* 截图 * 截图并保存图片文件
*
* @param url -视频地址
* @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式)
* @throws IOException
*/
boolean shot(String url,String imgurl) throws IOException;
/**
* 截图并保存图片文件
* *
* @param url -视频地址 * @param url -视频地址
* @param imgurl -保存的图片地址(不带后缀) * @param imgurl -保存的图片地址(不带后缀)
* @param format 图片格式(图片后缀,如果为空默认jpg) * @param format 图片格式(图片后缀,如果为空默认jpg)
*/ */
boolean shot(String url, String imgurl,String format) throws IOException; boolean shot(String url, String imgurl,String format) throws IOException;
/** /**
* 截图 * 截图并保存图片文件
* *
* @param url -视频地址 * @param url -视频地址
* @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式) * @param imgurl -保存的图片地址(不带后缀)
* @throws IOException * @param format 图片格式(图片后缀,如果为空默认jpg)
* @param width -保存的图片宽度
* @param height -保存的图片高度
*/ */
boolean shot(String url,String imgurl) throws IOException; boolean shot(String url, String imgurl,String format,Integer width,Integer height) throws IOException;
/** /**
* 截图(只返回图像的base64编码,默认jpg格式) * 截图(只返回图像的base64编码,默认jpg格式)
...@@ -46,7 +57,18 @@ public interface Screenshot { ...@@ -46,7 +57,18 @@ public interface Screenshot {
String getImgBase64(String url, String fommat)throws IOException; String getImgBase64(String url, String fommat)throws IOException;
/** /**
* 截图并返回base64数据 * 截图(只返回图像的base64编码,默认jpg格式)
* @param url -视频地址
* @param format-图片格式(如果为空,默认jpg格式)
* @param width -图片宽度
* @param height -图片高度
* @return
* @throws IOException
*/
String getImgBase64(String url, String fommat,Integer width,Integer height)throws IOException;
/**
* 截图保存图片文件并返回base64数据
* @param url -视频地址 * @param url -视频地址
* @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式) * @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式)
* @return * @return
...@@ -54,13 +76,25 @@ public interface Screenshot { ...@@ -54,13 +76,25 @@ public interface Screenshot {
String shotAndGetBase64(String url,String imgurl) throws IOException; String shotAndGetBase64(String url,String imgurl) throws IOException;
/** /**
* 截图并返回base64数据 * 截图保存图片文件并返回base64数据
* @param url -视频地址 * @param url -视频地址
* @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式) * @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式)
* @param format-图片格式(如果为空,默认jpg格式) * @param format-图片格式(如果为空,默认jpg格式)
* @return * @return
* @throws IOException * @throws IOException
*/ */
String shotAndGetBase64(String url,String imgurl, String fommat) throws IOException ; String shotAndGetBase64(String url,String imgurl, String fommat) throws IOException;
/**
* 截图保存图片文件并返回base64数据
* @param url -视频地址
* @param imgurl-图片地址(带后缀,如果不带后缀默认jpg格式)
* @param format-图片格式(如果为空,默认jpg格式)
* @param width -图片宽度
* @param height -图片高度
* @return
* @throws IOException
*/
String shotAndGetBase64(String url,String imgurl, String fommat,Integer width,Integer height) throws IOException;
} }
...@@ -5,20 +5,16 @@ import java.awt.image.DataBuffer; ...@@ -5,20 +5,16 @@ import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte; import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
import java.awt.image.Raster; import java.awt.image.Raster;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
...@@ -99,7 +95,6 @@ public class JavaImgConverter { ...@@ -99,7 +95,6 @@ public class JavaImgConverter {
*/ */
public static void viewBGR(int width,int height,ByteBuffer src) { public static void viewBGR(int width,int height,ByteBuffer src) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
// image.setRGB(0, 0, width, height, rgbarr, 0,height);
Raster ra = image.getRaster(); Raster ra = image.getRaster();
DataBuffer out = ra.getDataBuffer(); DataBuffer out = ra.getDataBuffer();
DataBufferByte db=(DataBufferByte)out; DataBufferByte db=(DataBufferByte)out;
...@@ -128,10 +123,10 @@ public class JavaImgConverter { ...@@ -128,10 +123,10 @@ public class JavaImgConverter {
public static String bufferedImage2Base64(BufferedImage image, String format) throws IOException { public static String bufferedImage2Base64(BufferedImage image, String format) throws IOException {
Encoder encoder=CurrentThreadData.localEncoder.get(); Encoder encoder=CurrentThreadData.localEncoder.get();
ByteArrayOutputStreamPlus baos = CurrentThreadData.localbaos.get(); ByteArrayOutputStreamPlus baos = CurrentThreadData.localbaos.get();
long last=System.currentTimeMillis(); // long last=System.currentTimeMillis();
ImageIO.write(image, format, baos);// 写出到字节流,这个耗时比较长 ImageIO.write(image, format, baos);// 写出到字节流,这个耗时比较长
long now=System.currentTimeMillis(); // long now=System.currentTimeMillis();
System.err.println("图像转换为字节流耗时:"+(now-last)); // System.err.println("图像转换为字节流耗时:"+(now-last));
// byte[] bytes=baos.toByteArray(); // byte[] bytes=baos.toByteArray();
// 编码成base64 // 编码成base64
String jpg_base64 = encoder.encodeToString(baos); String jpg_base64 = encoder.encodeToString(baos);
......
...@@ -19,6 +19,16 @@ import cc.eguid.cv.corelib.videoimageshot.core.JavaImgConverter; ...@@ -19,6 +19,16 @@ import cc.eguid.cv.corelib.videoimageshot.core.JavaImgConverter;
*/ */
public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGrabber,BufferedImageGrabber{ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGrabber,BufferedImageGrabber{
public FFmpegVideoImageGrabber setWidth(Integer width) {
this.width = width;
return this;
}
public FFmpegVideoImageGrabber setHeight(Integer height) {
this.height = height;
return this;
}
@Override @Override
protected ByteBuffer saveFrame(AVFrame pFrame, int width, int height){ protected ByteBuffer saveFrame(AVFrame pFrame, int width, int height){
BytePointer data = pFrame.data(0); BytePointer data = pFrame.data(0);
...@@ -75,10 +85,10 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra ...@@ -75,10 +85,10 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra
@Override @Override
public BufferedImage grabBufferImage(String url, Integer fmt) throws IOException { public BufferedImage grabBufferImage(String url, Integer fmt) throws IOException {
BufferedImage image=null; BufferedImage image=null;
long now =System.currentTimeMillis(); // long now =System.currentTimeMillis();
ByteBuffer buf=grabBuffer(url,fmt); ByteBuffer buf=grabBuffer(url,fmt);
long cu=System.currentTimeMillis(); // long cu=System.currentTimeMillis();
System.err.println("截图耗时:"+(cu-now)); // System.err.println("截图耗时:"+(cu-now));
image= JavaImgConverter.BGR2BufferedImage(buf,width,height); image= JavaImgConverter.BGR2BufferedImage(buf,width,height);
return image; return image;
} }
...@@ -87,6 +97,7 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra ...@@ -87,6 +97,7 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra
private Integer fmt;//图像数据结构 private Integer fmt;//图像数据结构
public FFmpegVideoImageGrabber() { public FFmpegVideoImageGrabber() {
} }
public FFmpegVideoImageGrabber(String url) { public FFmpegVideoImageGrabber(String url) {
...@@ -98,6 +109,12 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra ...@@ -98,6 +109,12 @@ public class FFmpegVideoImageGrabber extends GrabberTmplate implements BufferGra
this.url = url; this.url = url;
this.fmt = fmt; this.fmt = fmt;
} }
public FFmpegVideoImageGrabber(String url, Integer fmt,Integer width,Integer height) {
super(width,height);
this.url = url;
this.fmt = fmt;
}
public String getUrl() { public String getUrl() {
return url; return url;
......
...@@ -20,20 +20,40 @@ import cc.eguid.cv.corelib.videoimageshot.exception.StreamNotFoundException; ...@@ -20,20 +20,40 @@ import cc.eguid.cv.corelib.videoimageshot.exception.StreamNotFoundException;
public abstract class GrabberTmplate { public abstract class GrabberTmplate {
/*
* Register all formats and codecs
*/
static { static {
// Register all formats and codecs
av_register_all(); av_register_all();
avformat_network_init(); avformat_network_init();
av_log_set_level(AV_LOG_ERROR);
} }
protected int width;//宽度 protected Integer width;//宽度
protected int height;//高度 protected Integer height;//高度
private final static int PROBESIZE=500*1024; // private final static int PROBESIZE=1920*1080;
private final static int MAX_ANALYZE_DURATION=3 * AV_TIME_BASE; // private final static int MAX_ANALYZE_DURATION=10 * AV_TIME_BASE;
private AVDictionary options = new AVDictionary(); private AVDictionary options = new AVDictionary();
public GrabberTmplate() {
super();
}
public GrabberTmplate(Integer width, Integer height) {
super();
this.width = width;
this.height = height;
}
public Integer getWidth() {
return width;
}
public Integer getHeight() {
return height;
}
/** /**
* 打开视频流 * 打开视频流
* @param url -url * @param url -url
...@@ -61,16 +81,39 @@ public abstract class GrabberTmplate { ...@@ -61,16 +81,39 @@ public abstract class GrabberTmplate {
} }
/** /**
* 获取第一帧视频位置 * 获取视频通道
* @param pFormatCtx * @param pFormatCtx
* @return * @return
*/ */
protected int findVideoStreamIndex(AVFormatContext pFormatCtx) { protected int findVideoStreamIndex(AVFormatContext pFormatCtx) {
int i = 0; int size=pFormatCtx.nb_streams();
for (i = 0; i < pFormatCtx.nb_streams(); i++) { // System.err.println("流数量:"+size);
for (int i = 0; i < size; i++) {
AVStream stream=pFormatCtx.streams(i);
AVCodecContext codec=stream.codec();
int type=codec.codec_type();
// System.err.println("类型:"+type);
if (type == AVMEDIA_TYPE_VIDEO) {
return i;
}
}
return -1;
}
/**
* 获取音频通道
* @param pFormatCtx
* @return
*/
protected int findAudioStreamIndex(AVFormatContext pFormatCtx) {
int size=pFormatCtx.nb_streams();
// System.err.println("流数量:"+size);
for (int i = 0; i < size; i++) {
AVStream stream=pFormatCtx.streams(i); AVStream stream=pFormatCtx.streams(i);
AVCodecContext codec=stream.codec(); AVCodecContext codec=stream.codec();
if (codec.codec_type() == AVMEDIA_TYPE_VIDEO) { int type=codec.codec_type();
// System.err.println("类型:"+type);
if (type == AVMEDIA_TYPE_AUDIO) {
return i; return i;
} }
} }
...@@ -83,13 +126,14 @@ public abstract class GrabberTmplate { ...@@ -83,13 +126,14 @@ public abstract class GrabberTmplate {
* @param videoStream * @param videoStream
* @return * @return
*/ */
protected AVCodecContext findVideoStream(AVFormatContext pFormatCtx ,int videoStream)throws StreamNotFoundException { protected AVCodecContext findVideoStream(AVFormatContext pFormatCtx ,int videoStreamIndex)throws StreamNotFoundException {
if(videoStream >=0) { if(videoStreamIndex >=0) {
// Get a pointer to the codec context for the video stream // Get a pointer to the codec context for the video stream
AVStream stream=pFormatCtx.streams(videoStream); AVStream stream=pFormatCtx.streams(videoStreamIndex);
AVCodecContext pCodecCtx = stream.codec(); AVCodecContext pCodecCtx = stream.codec();
return pCodecCtx; return pCodecCtx;
} }
//如果没找到视频流,抛出异常
throw new StreamNotFoundException("Didn't open video file"); throw new StreamNotFoundException("Didn't open video file");
} }
...@@ -101,14 +145,14 @@ public abstract class GrabberTmplate { ...@@ -101,14 +145,14 @@ public abstract class GrabberTmplate {
// Find the decoder for the video stream // Find the decoder for the video stream
AVCodec pCodec = avcodec_find_decoder(pCodecCtx.codec_id()); AVCodec pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
if (pCodec == null) { if (pCodec == null) {
System.err.println("Codec not found"); System.err.println("Codec not found!");
throw new CodecNotFoundExpception("Codec not found"); throw new CodecNotFoundExpception("Codec not found!");
} }
AVDictionary optionsDict = null; AVDictionary optionsDict = null;
// Open codec // Open codec
if (avcodec_open2(pCodecCtx, pCodec, optionsDict) < 0) { if (avcodec_open2(pCodecCtx, pCodec, optionsDict) < 0) {
System.err.println("Could not open codec"); System.err.println("Could not open codec!");
throw new CodecNotFoundExpception("Could not open codec"); // Could not open codec throw new CodecNotFoundExpception("Could not open codec!"); // Could not open codec
} }
return pCodecCtx; return pCodecCtx;
} }
...@@ -125,12 +169,13 @@ public abstract class GrabberTmplate { ...@@ -125,12 +169,13 @@ public abstract class GrabberTmplate {
// Open video file // Open video file
AVFormatContext pFormatCtx=openInput(url); AVFormatContext pFormatCtx=openInput(url);
//不再使用减少缓存和检索时长方法,该方法导致高清/高清视频无法获取到i帧的问题
// if(url.indexOf("rtmp")>=0) { // if(url.indexOf("rtmp")>=0) {
//解决rtmp检索时间过长问题 //解决rtmp检索时间过长问题
//限制最大读取缓存 //限制最大读取缓存
pFormatCtx.probesize(PROBESIZE);//设置500k能保证高清视频也能读取到关键帧 // pFormatCtx.probesize(PROBESIZE);//设置500k能保证高清视频也能读取到关键帧
//限制avformat_find_stream_info最大持续时长,设置成3秒 //限制avformat_find_stream_info最大持续时长,设置成3秒
pFormatCtx.max_analyze_duration(MAX_ANALYZE_DURATION); // pFormatCtx.max_analyze_duration(MAX_ANALYZE_DURATION);
// } // }
// Retrieve stream information // Retrieve stream information
pFormatCtx=findStreamInfo(pFormatCtx); pFormatCtx=findStreamInfo(pFormatCtx);
...@@ -138,7 +183,8 @@ public abstract class GrabberTmplate { ...@@ -138,7 +183,8 @@ public abstract class GrabberTmplate {
//av_dump_format(pFormatCtx, 0, url, 0); //av_dump_format(pFormatCtx, 0, url, 0);
//Find a video stream //Find a video stream
int videoStream=findVideoStreamIndex(pFormatCtx); final int videoStream=findVideoStreamIndex(pFormatCtx);
AVCodecContext pCodecCtx =findVideoStream(pFormatCtx,videoStream); AVCodecContext pCodecCtx =findVideoStream(pFormatCtx,videoStream);
// Find the decoder for the video stream // Find the decoder for the video stream
...@@ -148,8 +194,13 @@ public abstract class GrabberTmplate { ...@@ -148,8 +194,13 @@ public abstract class GrabberTmplate {
//Allocate an AVFrame structure //Allocate an AVFrame structure
AVFrame pFrameRGB = av_frame_alloc(); AVFrame pFrameRGB = av_frame_alloc();
width = pCodecCtx.width(); int srcWidth = pCodecCtx.width();
height = pCodecCtx.height(); int srcHeight = pCodecCtx.height();
//如果分辨率为空,则保持图片尺寸保持不变
if(width==null||height==null) {
width=srcWidth;
height=srcHeight;
}
pFrameRGB.width(width); pFrameRGB.width(width);
pFrameRGB.height(height); pFrameRGB.height(height);
pFrameRGB.format(fmt); pFrameRGB.format(fmt);
...@@ -157,7 +208,7 @@ public abstract class GrabberTmplate { ...@@ -157,7 +208,7 @@ public abstract class GrabberTmplate {
// Determine required buffer size and allocate buffer // Determine required buffer size and allocate buffer
int numBytes = avpicture_get_size(fmt, width, height); int numBytes = avpicture_get_size(fmt, width, height);
DoublePointer param=null; DoublePointer param=null;
SwsContext sws_ctx = sws_getContext(width, height, pCodecCtx.pix_fmt(), width, height,fmt, SWS_FAST_BILINEAR, null, null, param); SwsContext sws_ctx = sws_getContext(srcWidth, srcHeight, pCodecCtx.pix_fmt(), width, height,fmt, SWS_FAST_BILINEAR, null, null, param);
BytePointer buffer = new BytePointer(av_malloc(numBytes)); BytePointer buffer = new BytePointer(av_malloc(numBytes));
// Assign appropriate parts of buffer to image planes in pFrameRGB // Assign appropriate parts of buffer to image planes in pFrameRGB
...@@ -170,14 +221,17 @@ public abstract class GrabberTmplate { ...@@ -170,14 +221,17 @@ public abstract class GrabberTmplate {
while (av_read_frame(pFormatCtx, packet) >= 0) { while (av_read_frame(pFormatCtx, packet) >= 0) {
// Is this a packet from the video stream? // Is this a packet from the video stream?
if (packet.stream_index() == videoStream) { if (packet.stream_index() == videoStream) {
// Decode video frame //Is i frame?
avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, packet); if(packet.flags()==AV_PKT_FLAG_KEY) {
// Did we get a video frame? // Decode video frame
if (frameFinished[0] >= 0) { avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, packet);
// Convert the image from its native format to BGR // Did we get a video frame?
sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(), 0, height, pFrameRGB.data(),pFrameRGB.linesize()); if (frameFinished[0] >= 0) {
//Convert BGR to ByteBuffer // Convert the image from its native format to BGR
return saveFrame(pFrameRGB, width, height); sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(), 0, srcHeight, pFrameRGB.data(),pFrameRGB.linesize());
//Convert BGR to ByteBuffer
return saveFrame(pFrameRGB, width, height);
}
} }
} }
// Free the packet that was allocated by av_read_frame // Free the packet that was allocated by av_read_frame
......
package cc.eguid.cv.corelib.videoimageshot;
import java.io.IOException;
/**
* 测试截图
* @author eguid
*
*/
public class FFmpegScreenshotTest {
static Screenshot shoter=new FFmpegScreenshot();
/**
* 测试截图保存jpg
* @throws IOException
*/
public static void shotJpg() throws IOException {
shoter.shot("rtmp://media3.sinovision.net:1935/live/livestream", "test.jpg");
}
/**
* 测试截图保存png
* @throws IOException
*/
public static void shotPng() throws IOException {
shoter.shot("rtmp://media3.sinovision.net:1935/live/livestream","test.png" );
}
/**
* 测试截图保存jpeg
* @throws IOException
*/
public static void shotJpeg() throws IOException {
shoter.shot("rtmp://media3.sinovision.net:1935/live/livestream", "test.jpeg");
}
/**
* 测试截图保存bmp
* @throws IOException
*/
public static void shotBmp() throws IOException {
shoter.shot("rtmp://media3.sinovision.net:1935/live/livestream", "rtmp.bmp");
}
/**
* 测试截图保存gif
* @throws IOException
*/
public static void shotGif() throws IOException {
shoter.shot("rtmp://media3.sinovision.net:1935/live/livestream", "test.gif");
}
/**
* 测试截图并返回base64编码
* @throws IOException
*/
public static void shotGetBase64() throws IOException {
String base64=shoter.getImgBase64("rtmp://media3.sinovision.net:1935/live/livestream");
System.err.println(base64);
}
/**
* 测试截图保存文件并返回base64编码
* @throws IOException
*/
public static void shotAndGetBase64() throws IOException {
String base64=shoter.shotAndGetBase64("rtmp://media3.sinovision.net:1935/live/livestream", "shot.jpg");
System.err.println(base64);
}
/**
* 测试截图缩放保存文件并返回base64编码
* @throws IOException
*/
public static void shotAndGetBase64Scale() throws IOException {
String base64=shoter.shotAndGetBase64("rtmp://media3.sinovision.net:1935/live/livestream", "scale.png","png",300,200);
System.err.println(base64);
}
public static void main(String[] args) throws IOException {
// shotJpg();
// shotPng();
// shotJpeg();
// shotBmp();
// shotGif();
// shotGetBase64();
// shotAndGetBase64();
shotAndGetBase64Scale();
}
}
package cc.eguid.cv.corelib.videoimageshot;
import java.awt.image.BufferedImage;
import java.io.IOException;
import cc.eguid.cv.corelib.videoimageshot.core.JavaImgConverter;
import cc.eguid.cv.corelib.videoimageshot.grabber.FFmpegVideoImageGrabber;
public class GrabberShotAndViewTest {
/**
* 截图并显示
* @throws IOException
*/
public static void shotAndView() throws IOException {
BufferedImage image=new FFmpegVideoImageGrabber("rtmp://10.23.49.12:1935/live/100100015").grabBufferImage();
JavaImgConverter.viewImage(image);
}
/**
* 截图缩放图像并显示
* @throws IOException
*/
public static void shotScaleAndView() throws IOException {
BufferedImage image=new FFmpegVideoImageGrabber("rtmp://10.23.49.12:1935/live/100100015").setWidth(800).setHeight(600).grabBufferImage();
JavaImgConverter.viewImage(image);
}
/**
* 测试
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// shotAndView();
// shotScaleAndView();
}
}
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:29 CST 2019
version=1.1.0-2019.3.25
groupId=cc.eguid.cv.corelib
m2e.projectName=videoimageshot
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\corelib\\videoimageshot
artifactId=videoimageshot
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>corelib</artifactId>
<version>1.0</version>
</parent>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoimageshot</artifactId>
<version>1.1.0-2019.3.25</version>
<name>videoimageshot</name>
<url>https://blog.eguid.cc</url>
<description>
版本更新
2018.9.21
1、多项性能优化
①base64转换性能优化
②ByteBuffer转BufferedImage性能优化
③ffmpeg截图性能优化
④提供线程安全的API
2、修复rtsp截图崩溃的bug
2018.9.18
1、修复流媒体视频检索时间过长问题
2018.9.14
1、支持rtsp/rtmp/hls/媒体流和视频文件截图
2、支持bmp/jpg/png/jpeg/gif等多种格式图片保存
3、支持base64图像数据返回
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- ffmpeg -->
<!-- javacv -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
</dependency>
<!-- javacpp -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
</dependency>
<!-- ffmpeg -->
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
...@@ -13,20 +13,24 @@ ...@@ -13,20 +13,24 @@
<version>1.1</version> <version>1.1</version>
<description>web-service</description> <description>web-service</description>
<properties>
<videoRecorder.version>2019.3.25</videoRecorder.version>
<videoimageshot.version>2019.4.18</videoimageshot.version>
</properties>
<modules> <modules>
<!-- --> <!--
<module>videoimageshot-web</module> <module>videoimageshot-web</module>
<module>videorecorder-web</module> <module>videorecorder-web</module>
<module>videorecorder-postgre</module> <module>videorecorder-postgre</module> -->
<module>videoshot-postgre</module> <module>videoshot-postgre</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>cc.eguid.cv.corelib</groupId> <groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoimageshot</artifactId> <artifactId>videoimageshot</artifactId>
<version>1.1.0-2019.3.25</version> <version>${videoimageshot.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
...@@ -38,7 +42,7 @@ ...@@ -38,7 +42,7 @@
<dependency> <dependency>
<groupId>cc.eguid.cv.corelib</groupId> <groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoRecorder</artifactId> <artifactId>videoRecorder</artifactId>
<version>1.1.0-2019-3.25</version> <version>${videoRecorder.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
......
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:30 CST 2019
version=1.0
groupId=cc.eguid.cv.web.examples
m2e.projectName=videoimageshot-web
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\examples\\videoimageshot-web
artifactId=videoimageshot-web
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv</groupId>
<artifactId>examples</artifactId>
<version>1.1</version>
</parent>
<groupId>cc.eguid.cv.web.examples</groupId>
<artifactId>videoimageshot-web</artifactId>
<version>1.0</version>
<name>videoimageshot-web</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoimageshot</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<!-- Package as an executable jar -->
</plugin>
</plugins>
</build>
</project>
#配置程序端口,默认为8080
server.port=8081
#thymeleaf缓存(开发时)取消,取消缓存-false
spring.thymeleaf.cache=true
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}">world</h1>
<table>
<tr>
<th>截图ID</th>
<th>截图名称</th>
<th>源视频</th>
<th>格式</th>
<th>保存位置</th>
<th>操作</th>
</tr>
<tr th:each="his : ${hislist}">
<td th:text="${his.id}">0</td>
<td th:text="${his.name}">zhansan</td>
<td th:text="${his.src}">1988-06-01</td>
<td th:text="${his.fmt}">12345</td>
<td th:text="${his.output}">1988-06-01</td>
<td><a th:href="'view?id='+${his.id}" target="_blank">查看截图</a></td>
</tr>
</table>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}"></h1>
<div>
<label for="src">流媒体视频源(rtsp/rtmp/hls/http-flv地址):</label>
<input type="url" autofocus="autofocus" value="" name="src" id="src">
<label for="fmt">图片格式:</label>
<select id="fmt" name="fmt">
<option value="png">png</option>
<option value="jpg" selected="selected">jpg</option>
<option value="jpeg">jpeg</option>
<option value="gif">gif</option>
<option value="bmp">bmp</option>
</select>
<button id="btn" type="button" name="截图" title="截图" onclick="submit();">截图</button>
</div>
<div>
<p>显示截图<a id="download" target="_blank"></a></p>
<img id="img" alt="截图" style="margin:0;padding:0;width:500;height:500;">
</div>
</body>
<script type="text/javascript">
function isEmpty(s){
return s==undefined||s==null||s.length<1||s=='';
}
function getDom(id){
return document.getElementById(id);
}
function req(reqmethod,url,ret){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
if(typeof ret === "function"){
ret(xmlhttp.responseText);
}
}
};
xmlhttp.open(reqmethod,url,true);
xmlhttp.send();
}
function submit(){
getDom("btn").disabled=true;
var fmt=getDom("fmt").value;
var src=getDom("src").value;
if(isEmpty(src)){
alert("请填写一个视频源");
}
req("GET","shot?src="+src+"&fmt="+fmt,function(res){
getDom("btn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(res)){
alert(data.msg);
return;
}
viewImg(data.base64);
}else{
alert("请求失败");
}
});
}
function viewImg(data){
var fmt=getDom("fmt").value;
var s='data:image/'+fmt+';base64,'+data;
getDom("img").src=s;
var d=getDom("download");
d.href=s;
d.text="(点击这里下载本截图)";
d.download="截图."+fmt;
}
</script>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
<img th:src="'data:image/jpeg;base64,'+${his.base64}" alt="截图预览" style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
</body>
</html>
\ No newline at end of file
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:31 CST 2019
version=1.1
groupId=cc.eguid.cv.examples
m2e.projectName=videorecorder-postgre
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\examples\\videorecorder-postgre
artifactId=videorecorder-postgre
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv</groupId>
<artifactId>examples</artifactId>
<version>1.1</version>
</parent>
<groupId>cc.eguid.cv.examples</groupId>
<artifactId>videorecorder-postgre</artifactId>
<name>videorecorder-postgre</name>
<description>2019年3月18日
使用postgre数据库示例</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoRecorder</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<!-- Package as an executable jar -->
</plugin>
</plugins>
</build>
</project>
#配置程序端口,默认为8080
server.port=8082
#thymeleaf缓存(开发时)取消,取消缓存-false
spring.thymeleaf.cache=true
#最大工作线程
record.maxsize=10
#录像文件存储路径(该路径应该放在http/ftp服务中,方便取用/播放)
record.dir=F://nginx-1.15.3/html/
#录像播放地址(该地址应当为http/ftp服务访问地址,方便取用/播放录像)
play.url=http://localhost:81/
#postgre
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=postgres
spring.datasource.password=123456
spring.datasource.max-active=5
spring.datasource.min-idle=5
spring.datasource.initial-size=1
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=select 1
# 使用druid数据源
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=cc.eguid.cv.videorecorder.web.dao
mybatis.mapperLocations: cc.eguid.cv.videorecorder.web.dao/*.xml
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.eguid.cv.videorecorder.web.dao.RecordInfoMapper">
<resultMap id="BaseResultMap" type="cc.eguid.cv.videorecorder.web.pojo.CameraRecordInfo">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="src" jdbcType="VARCHAR" property="src" />
<result column="out" jdbcType="VARCHAR" property="out" />
<result column="playurl" jdbcType="VARCHAR" property="playurl" />
<result column="starttime" javaType="java.util.Date" jdbcType="TIMESTAMP" property="starttime" />
<result column="endtime" javaType="java.util.Date" jdbcType="TIMESTAMP" property="endtime" />
<result column="cameraid" jdbcType="INTEGER" property="cameraid" />
</resultMap>
<sql id="Base_Column_List">
id,src,out,playurl,starttime,endtime,cameraid
</sql>
<delete id="delete" parameterType="cc.eguid.cv.videorecorder.web.pojo.CameraRecordInfo" >
delete from vnmp_recordinfo
where 1=0
<if test="id!=null">
OR id=#{id,jdbcType=INTEGER}
</if>
<if test="cameraid!=null">
OR cameraid=#{cameraid,jdbcType=INTEGER}
</if>
</delete>
<insert id="insert" parameterType="cc.eguid.cv.videorecorder.web.pojo.CameraRecordInfo" >
insert into vnmp_recordinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="src!= null and src!='' ">
src,
</if>
<if test="out!= null and out!=''">
out,
</if>
<if test="playurl!= null and playurl!= ''">
playurl,
</if>
<if test="starttime!= null">
starttime,
</if>
<if test="endtime!= null">
endtime,
</if>
<if test="cameraid!= null">
cameraid,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id!= null">
#{id,jdbcType=INTEGER},
</if>
<if test="src!= null and src!='' ">
#{src,jdbcType=VARCHAR},
</if>
<if test="out!= null and out!=''">
#{out,jdbcType=VARCHAR},
</if>
<if test="playurl!= null and playurl!= ''">
#{playurl,jdbcType=VARCHAR},
</if>
<if test="starttime != null">
#{starttime,jdbcType=DATE},
</if>
<if test="endtime != null">
#{endtime,jdbcType=DATE},
</if>
<if test="cameraid != null">
#{cameraid,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="update" parameterType="cc.eguid.cv.videorecorder.web.pojo.CameraRecordInfo" >
update vnmp_recordinfo
<trim prefix="set" suffixOverrides=",">
<if test="src != null and src!=''">
src = #{src,jdbcType=VARCHAR},
</if>
<if test="out != null and out !=''">
out = #{out,jdbcType=VARCHAR},
</if>
<if test="playurl != null and playurl!=''">
playurl = #{playurl,jdbcType=VARCHAR},
</if>
<if test="starttime != null">
starttime = #{starttime,jdbcType=DATE},
</if>
<if test="endtime != null">
endtime = #{endtime,jdbcType=DATE},
</if>
<if test="cameraid != null">
cameraid = #{cameraid,jdbcType=INTEGER},
</if>
</trim>
where id = #{id,jdbcType=INTEGER}
</update>
<select id="selectById" parameterType="int" resultMap="BaseResultMap" >
select id,src,out,playurl,starttime,endtime,cameraid
from vnmp_recordinfo
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectBySelective" parameterType="cc.eguid.cv.videorecorder.web.pojo.CameraRecordInfo" resultMap="BaseResultMap" >
select <include refid="Base_Column_List" />
from vnmp_recordinfo
<trim prefix="where" prefixOverrides="and|or">
<if test="id != null">
and id = #{id,jdbcType=INTEGER}
</if>
<if test="src != null">
and src = #{src,jdbcType=VARCHAR}
</if>
<if test="out != null">
and out = #{out,jdbcType=VARCHAR}
</if>
<if test="playurl != null">
and playurl = #{playurl,jdbcType=VARCHAR}
</if>
<if test="starttime != null">
and starttime = #{starttime,jdbcType=DATE}
</if>
<if test="endtime != null">
and endtime = #{endtime,jdbcType=DATE}
</if>
<if test="cameraid != null">
and cameraid = #{cameraid,jdbcType=INTEGER}
</if>
</trim>
</select>
</mapper>
\ No newline at end of file
#设置级别和目的地
log4j.rootLogger=info,stdout,fout
log4j.logger.me=debug
#mybatis logging
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
org.apache.ibatis.session.AutoMappingUnknownColumnBehavior=debug
#sql logging
log4j.logger.org.apache.commons=info
log4j.logger.java.sql.Connection=info
log4j.logger.java.sql.Statement=info
log4j.logger.java.sql.PreparedStatement=info
log4j.logger.java.sql.ResultSet=info
# Console output
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Encoding=UTF-8
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss},%6.6r]%-5p[%t]%x(%F:%L) - %m%n
#File output
log4j.appender.fout=org.apache.log4j.DailyRollingFileAppender
log4j.appender.fout.Threshold =info
log4j.appender.fout.file=logs/info.log
log4j.appender.fout.Encoding=UTF-8
log4j.appender.fout.layout=org.apache.log4j.PatternLayout
log4j.appender.fout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss},%6.6r]%-5p[%t]%x(%F:%L) - %m%n
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}">world</h1>
<table>
<tr>
<th>录像ID</th>
<th>源视频</th>
<th>录像保存位置</th>
<th>录像播放地址</th>
<th>操作</th>
</tr>
<tr th:each="record: ${list}">
<td th:text="${record.id}">0</td>
<td th:text="${record.src}">视频源地址为空</td>
<td th:text="${record.out}">保存位置为空</td>
<td th:text="${record.playurl}">播放地址为空</td>
<td><a th:href="'play?id='+${record.id}" target="_blank">播放录像(点播)</a></td>
</tr>
</table>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
<link href="https://vjs.zencdn.net/7.2/video-js.min.css" rel="stylesheet">
<script src="https://vjs.zencdn.net/7.2/video.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/videojs-flash@2/dist/videojs-flash.min.js"></script>
</head>
<body style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
<video id="my-player" class="video-js" controls poster="http://eguid.cc/eguid-s.png" data-setup='{"controls": true, "autoplay": true, "preload": "true"}'>
<source th:src="${record.playurl}">
</video>
</body>
<script type="text/javascript">
var options = {};
var player = videojs('my-player', options, function onPlayerReady() {
videojs.log('Your player is ready!');
this.play();
this.on('ended', function() {
videojs.log('Awww...over so soon?!');
});
});
</script>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
<link href="https://vjs.zencdn.net/7.2/video-js.min.css" rel="stylesheet">
<script src="https://vjs.zencdn.net/7.2/video.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/videojs-flash@2/dist/videojs-flash.min.js"></script>
</head>
<body>
<h1 th:text="${hello}"></h1>
<p>操作须知:先点击录像按钮后停止按钮点亮,说明开始录像;点击停止按钮成功后播放录像按钮点亮;点击播放录像按钮开始点播录像视频。虽然录像可以支持较多视频格式,但是播放器只支持mp4,flv这些</p>
<div>
<label for="src">流媒体视频源(rtsp/rtmp/hls/http-flv地址):</label>
<input type="url" autofocus="autofocus" name="src" id="src">
<label for="out">录像文件名称:</label>
<input type="text" name="out" id="out">
<button id="btn" type="button" name="录像" title="录像" onclick="submit();">录像</button>
<button id="stopbtn" type="button" name="停止录像" title="停止录像" onclick="stop();">停止</button>
</div>
<div>
<p>播放录像(本地测试)</p>
<button id="playbtn" type="button" name="播放当前录像" title="播放当前录像" onclick="play();">播放录像</button>
<input type="text" name="id" id="id" hidden="hidden"/>
<video id="my-player" class="video-js" controls poster="http://eguid.cc/eguid-s.png" data-setup='{"controls": true, "autoplay": false, "preload": "false"}'>
</video>
<div>
<p>状态</p>
<b id="stat"></b>
</div>
</div>
</body>
<script type="text/javascript">
var options = {};
var player = videojs('my-player', options, function onPlayerReady() {
videojs.log('Your player is ready!');
this.play();
this.on('ended', function() {
videojs.log('Awww...over so soon?!');
});
});
function isEmpty(s){
return s==undefined||s==null||s.length<1||s=='';
}
function getDom(id){
return document.getElementById(id);
}
function req(reqmethod,url,ret){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
if(typeof ret === "function"){
ret(xmlhttp.responseText);
}
}
};
xmlhttp.open(reqmethod,url,true);
xmlhttp.send();
}
//修改状态
function state(msg){
getDom("stat").innerHTML=msg;
}
function submit(){
getDom("btn").disabled=true;
var out=getDom("out").value;
var src=getDom("src").value;
if(isEmpty(src)||isEmpty(out)){
alert("视频源和录制文件名不能为空");
return;
}
state("准备录像");
req("GET","record?src="+src+"&out="+out,function(res){
getDom("stopbtn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code!='1'){
state("录像操作失败");
alert(data.msg);
return;
}
getDom("id").value=data.msg;
state("正在录像中...(可点击停止按钮结束录像)");
}else{
state("请求失败");
}
});
}
getDom("playbtn").disabled=true;
getDom("stopbtn").disabled=true;
function play(){
var id=getDom("id").value;
if(isEmpty(id)){
alert("尚未开始录像");
}
state("正在查找录像...");
req("GET","get?id="+id,function(res){
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code=='0'){
alert(data.msg);
return;
}
var url= data.playurl;
if(isEmpty(url)){
state("没有点播视频");
return;
}
player.src(url);
player.play();
state("正在播放录像...");
}else{
state("请求失败");
}
});
}
function stop(){
var id=getDom("id").value;
if(isEmpty(id)){
alert("尚未开始录像");
return;
}
getDom("stopbtn").disabled=true;
if(flag=false){
alert("已停止录像,请勿重复点击");
getDom("stopbtn").disabled=false;
return;
}
state("正在停止录像...");
req("GET","stop?id="+id,function(res){
getDom("stopbtn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code!='1'){
alert(data.msg);
return;
}
getDom("btn").disabled=false;
getDom("playbtn").disabled=false;
state("已停止录像...(可通过播放按钮播放录像)");
}else{
state("请求失败");
}
});
}
</script>
</html>
\ No newline at end of file
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:31 CST 2019
version=1.1
groupId=cc.eguid.cv.examples
m2e.projectName=videorecorder-web
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\examples\\videorecorder-web
artifactId=videorecorder-web
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv</groupId>
<artifactId>examples</artifactId>
<version>1.1</version>
</parent>
<groupId>cc.eguid.cv.examples</groupId>
<artifactId>videorecorder-web</artifactId>
<name>videorecorder-web</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoRecorder</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<!-- Package as an executable jar -->
</plugin>
</plugins>
</build>
<description>2018年10月18日
</description>
</project>
#配置程序端口,默认为8080
server.port=8082
#thymeleaf缓存(开发时)取消,取消缓存-false
spring.thymeleaf.cache=true
#最大工作线程
record.maxsize=10
#录像文件存储路径(该路径应该放在http/ftp服务中,方便取用/播放)
record.dir=F://nginx-1.15.3/html/
#录像播放地址(该地址应当为http/ftp服务访问地址,方便取用/播放录像)
play.url=http://localhost:81/
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}">world</h1>
<table>
<tr>
<th>录像ID</th>
<th>源视频</th>
<th>录像保存位置</th>
<th>录像播放地址</th>
<th>操作</th>
</tr>
<tr th:each="record: ${list}">
<td th:text="${record.id}">0</td>
<td th:text="${record.src}">视频源地址为空</td>
<td th:text="${record.out}">保存位置为空</td>
<td th:text="${record.playurl}">播放地址为空</td>
<td><a th:href="'play?id='+${record.id}" target="_blank">播放录像(点播)</a></td>
</tr>
</table>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
<link href="https://vjs.zencdn.net/7.2/video-js.min.css" rel="stylesheet">
<script src="https://vjs.zencdn.net/7.2/video.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/videojs-flash@2/dist/videojs-flash.min.js"></script>
</head>
<body style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
<video id="my-player" class="video-js" controls poster="http://eguid.cc/eguid-s.png" data-setup='{"controls": true, "autoplay": true, "preload": "true"}'>
<source th:src="${record.playurl}">
</video>
</body>
<script type="text/javascript">
var options = {};
var player = videojs('my-player', options, function onPlayerReady() {
videojs.log('Your player is ready!');
this.play();
this.on('ended', function() {
videojs.log('Awww...over so soon?!');
});
});
</script>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
<link href="https://vjs.zencdn.net/7.2/video-js.min.css" rel="stylesheet">
<script src="https://vjs.zencdn.net/7.2/video.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/videojs-flash@2/dist/videojs-flash.min.js"></script>
</head>
<body>
<h1 th:text="${hello}"></h1>
<p>操作须知:先点击录像按钮后停止按钮点亮,说明开始录像;点击停止按钮成功后播放录像按钮点亮;点击播放录像按钮开始点播录像视频。虽然录像可以支持较多视频格式,但是播放器只支持mp4,flv这些</p>
<div>
<label for="src">流媒体视频源(rtsp/rtmp/hls/http-flv地址):</label>
<input type="url" autofocus="autofocus" name="src" id="src">
<label for="out">录像文件名称:</label>
<input type="text" name="out" id="out">
<button id="btn" type="button" name="录像" title="录像" onclick="submit();">录像</button>
<button id="stopbtn" type="button" name="停止录像" title="停止录像" onclick="stop();">停止</button>
</div>
<div>
<p>播放录像(本地测试)</p>
<button id="playbtn" type="button" name="播放当前录像" title="播放当前录像" onclick="play();">播放录像</button>
<input type="text" name="id" id="id" hidden="hidden"/>
<video id="my-player" class="video-js" controls poster="http://eguid.cc/eguid-s.png" data-setup='{"controls": true, "autoplay": false, "preload": "false"}'>
</video>
<div>
<p>状态</p>
<b id="stat"></b>
</div>
</div>
</body>
<script type="text/javascript">
var options = {};
var player = videojs('my-player', options, function onPlayerReady() {
videojs.log('Your player is ready!');
this.play();
this.on('ended', function() {
videojs.log('Awww...over so soon?!');
});
});
function isEmpty(s){
return s==undefined||s==null||s.length<1||s=='';
}
function getDom(id){
return document.getElementById(id);
}
function req(reqmethod,url,ret){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
if(typeof ret === "function"){
ret(xmlhttp.responseText);
}
}
};
xmlhttp.open(reqmethod,url,true);
xmlhttp.send();
}
//修改状态
function state(msg){
getDom("stat").innerHTML=msg;
}
function submit(){
getDom("btn").disabled=true;
var out=getDom("out").value;
var src=getDom("src").value;
if(isEmpty(src)||isEmpty(out)){
alert("视频源和录制文件名不能为空");
return;
}
state("准备录像");
req("GET","record?src="+src+"&out="+out,function(res){
getDom("stopbtn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code!='1'){
state("录像操作失败");
alert(data.msg);
return;
}
getDom("id").value=data.msg;
state("正在录像中...(可点击停止按钮结束录像)");
}else{
state("请求失败");
}
});
}
getDom("playbtn").disabled=true;
getDom("stopbtn").disabled=true;
function play(){
var id=getDom("id").value;
if(isEmpty(id)){
alert("尚未开始录像");
}
state("正在查找录像...");
req("GET","get?id="+id,function(res){
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code=='0'){
alert(data.msg);
return;
}
var url= data.playurl;
if(isEmpty(url)){
state("没有点播视频");
return;
}
player.src(url);
player.play();
state("正在播放录像...");
}else{
state("请求失败");
}
});
}
function stop(){
var id=getDom("id").value;
if(isEmpty(id)){
alert("尚未开始录像");
return;
}
getDom("stopbtn").disabled=true;
if(flag=false){
alert("已停止录像,请勿重复点击");
getDom("stopbtn").disabled=false;
return;
}
state("正在停止录像...");
req("GET","stop?id="+id,function(res){
getDom("stopbtn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(data)||data.code!='1'){
alert(data.msg);
return;
}
getDom("btn").disabled=false;
getDom("playbtn").disabled=false;
state("已停止录像...(可通过播放按钮播放录像)");
}else{
state("请求失败");
}
});
}
</script>
</html>
\ No newline at end of file
...@@ -4,7 +4,7 @@ server.port=8081 ...@@ -4,7 +4,7 @@ server.port=8081
spring.thymeleaf.cache=true spring.thymeleaf.cache=true
#截图保存路径 #截图保存路径
shot.dir=F://nginx-1.15.3/html/ shot.dir=F://nginx-1.15.3/html/
shot.url=http://localhost:81/images/ shot.url=http://localhost:81/
#postgre #postgre
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver
......
...@@ -22,6 +22,8 @@ public class VideoshotInfo extends BaseEntity{ ...@@ -22,6 +22,8 @@ public class VideoshotInfo extends BaseEntity{
private Date createtime; private Date createtime;
private Integer cameraid; private Integer cameraid;
private String base64;
public VideoshotInfo() { public VideoshotInfo() {
super(); super();
...@@ -137,4 +139,13 @@ public class VideoshotInfo extends BaseEntity{ ...@@ -137,4 +139,13 @@ public class VideoshotInfo extends BaseEntity{
public void setCameraid(Integer cameraid) { public void setCameraid(Integer cameraid) {
this.cameraid = cameraid; this.cameraid = cameraid;
} }
public String getBase64() {
return base64;
}
public void setBase64(String base64) {
this.base64 = base64;
}
} }
\ No newline at end of file
...@@ -37,6 +37,7 @@ public class VideoShotServiceImpl implements VideoShotService { ...@@ -37,6 +37,7 @@ public class VideoShotServiceImpl implements VideoShotService {
long now=System.currentTimeMillis(); long now=System.currentTimeMillis();
String src=param.getSrc(),out = param.getOutput(),fmt=param.getFmt(),name=param.getName(); String src=param.getSrc(),out = param.getOutput(),fmt=param.getFmt(),name=param.getName();
Integer needBase64=param.getNeedBase64(); Integer needBase64=param.getNeedBase64();
Integer width=param.getWidth(),height=param.getHeight();
String base64; String base64;
boolean isSuccess=false; boolean isSuccess=false;
if(out==null) { if(out==null) {
...@@ -49,17 +50,17 @@ public class VideoShotServiceImpl implements VideoShotService { ...@@ -49,17 +50,17 @@ public class VideoShotServiceImpl implements VideoShotService {
if(needBase64 == null||needBase64==0) { if(needBase64 == null||needBase64==0) {
//只保存文件 //只保存文件
String outfilepath=out+name+"."+fmt; String outfilepath=out+name+"."+fmt;
isSuccess=shoter.shot(src,outfilepath,fmt); isSuccess=shoter.shot(src,outfilepath,fmt,width,height);
info.setImgfile(out); info.setImgfile(out);
}else if (needBase64==1){ }else if (needBase64==1){
//只返回base64 //只返回base64
base64=shoter.getImgBase64(src, fmt); base64=shoter.getImgBase64(src, fmt,width,height);
isSuccess=(base64!=null); isSuccess=(base64!=null);
info.setBase64(base64); info.setBase64(base64);
}else if(needBase64==2) { }else if(needBase64==2) {
//既要保存文件也转换base64 //既要保存文件也转换base64
String outfilepath=out+name+"."+fmt; String outfilepath=out+name+"."+fmt;
base64=shoter.shotAndGetBase64(src, outfilepath, fmt); base64=shoter.shotAndGetBase64(src, outfilepath, fmt,width,height);
isSuccess=(base64!=null); isSuccess=(base64!=null);
info.setBase64(base64); info.setBase64(base64);
info.setImgfile(out); info.setImgfile(out);
...@@ -90,10 +91,13 @@ public class VideoShotServiceImpl implements VideoShotService { ...@@ -90,10 +91,13 @@ public class VideoShotServiceImpl implements VideoShotService {
if(shotMapper.insertSelective(videoshotinfo)==1) { if(shotMapper.insertSelective(videoshotinfo)==1) {
log.info("保存截图数据成功"); log.info("保存截图数据成功");
videoshotinfo.setBase64(info.getBase64());
return videoshotinfo; return videoshotinfo;
} }
log.error("保存截图数据失败"); log.error("保存截图数据失败");
return null;
info=null;param=null;
return videoshotinfo;
} }
@Override @Override
......
Manifest-Version: 1.0
Built-By: Administrator
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
#Generated by Maven Integration for Eclipse
#Tue Apr 16 15:11:37 CST 2019
version=0.1-snapshot
groupId=cc.eguid.cv.examples
m2e.projectName=videoshot-postgre
m2e.projectLocation=E\:\\workspace\\eclipse4.8\\easyCV\\examples\\videoshot-postgre
artifactId=videoshot-postgre
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.eguid.cv</groupId>
<artifactId>examples</artifactId>
<version>1.1</version>
</parent>
<groupId>cc.eguid.cv.examples</groupId>
<artifactId>videoshot-postgre</artifactId>
<version>0.1-snapshot</version>
<name>videoshot-postgre</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cc.eguid.cv.corelib</groupId>
<artifactId>videoimageshot</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<!-- Package as an executable jar -->
</plugin>
</plugins>
</build>
</project>
#配置程序端口,默认为8080
server.port=8081
#thymeleaf缓存(开发时)取消,取消缓存-false
spring.thymeleaf.cache=true
#截图保存路径
shot.dir=F://nginx-1.15.3/html/
shot.url=http://localhost:81/images/
#postgre
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/js-hais?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=postgres
spring.datasource.password=123456
spring.datasource.max-active=5
spring.datasource.min-idle=5
spring.datasource.initial-size=1
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=select 1
# 使用druid数据源
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=cc.eguid.cv.web.videoimageshotweb.dao
mybatis.mapperLocations=cc.eguid.cv.web.videoimageshotweb.dao/*.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.eguid.cv.web.videoimageshotweb.dao.VideoshotInfoMapper">
<resultMap id="BaseResultMap" type="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="src" jdbcType="VARCHAR" property="src" />
<result column="out" jdbcType="VARCHAR" property="out" />
<result column="url" jdbcType="VARCHAR" property="url" />
<result column="fmt" jdbcType="VARCHAR" property="fmt" />
<result column="width" jdbcType="INTEGER" property="width" />
<result column="height" jdbcType="INTEGER" property="height" />
<result column="createtime" jdbcType="TIMESTAMP" property="createtime" />
<result column="cameraid" jdbcType="INTEGER" property="cameraid" />
</resultMap>
<sql id="Base_Column_List">
id, src, out, url, fmt, width, height, createtime, cameraid
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from vnmp_videoshotinfo
where id = #{id,jdbcType=VARCHAR}
</select>
<select id="selectBySelective" parameterType="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from vnmp_videoshotinfo
<trim prefix="where" prefixOverrides="and|or">
<if test="src != null and src !=''">
and src = #{src,jdbcType=VARCHAR}
</if>
<if test="out != null and out !=''">
and out = #{out,jdbcType=VARCHAR}
</if>
<if test="url != null and url!=''">
and url = #{url,jdbcType=VARCHAR}
</if>
<if test="fmt != null and fmt!=''">
and fmt = #{fmt,jdbcType=VARCHAR}
</if>
<if test="width != null">
and width = #{width,jdbcType=INTEGER}
</if>
<if test="height != null">
and height = #{height,jdbcType=INTEGER}
</if>
<if test="createtime != null">
and createtime = #{createtime,jdbcType=TIMESTAMP}
</if>
<if test="cameraid != null">
and cameraid = #{cameraid,jdbcType=INTEGER}
</if>
</trim>
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from vnmp_videoshotinfo
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert" parameterType="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo">
insert into vnmp_videoshotinfo (id, src, out,
url, fmt, width, height,
createtime, cameraid)
values (#{id,jdbcType=VARCHAR}, #{src,jdbcType=VARCHAR}, #{out,jdbcType=VARCHAR},
#{url,jdbcType=VARCHAR}, #{fmt,jdbcType=VARCHAR}, #{width,jdbcType=INTEGER}, #{height,jdbcType=INTEGER},
#{createtime,jdbcType=TIMESTAMP}, #{cameraid,jdbcType=INTEGER})
</insert>
<insert id="insertSelective" parameterType="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo">
insert into vnmp_videoshotinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="src != null">
src,
</if>
<if test="out != null">
out,
</if>
<if test="url != null">
url,
</if>
<if test="fmt != null">
fmt,
</if>
<if test="width != null">
width,
</if>
<if test="height != null">
height,
</if>
<if test="createtime != null">
createtime,
</if>
<if test="cameraid != null">
cameraid,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="src != null">
#{src,jdbcType=VARCHAR},
</if>
<if test="out != null">
#{out,jdbcType=VARCHAR},
</if>
<if test="url != null">
#{url,jdbcType=VARCHAR},
</if>
<if test="fmt != null">
#{fmt,jdbcType=VARCHAR},
</if>
<if test="width != null">
#{width,jdbcType=INTEGER},
</if>
<if test="height != null">
#{height,jdbcType=INTEGER},
</if>
<if test="createtime != null">
#{createtime,jdbcType=TIMESTAMP},
</if>
<if test="cameraid != null">
#{cameraid,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo">
update vnmp_videoshotinfo
<set>
<if test="src != null">
src = #{src,jdbcType=VARCHAR},
</if>
<if test="out != null">
out = #{out,jdbcType=VARCHAR},
</if>
<if test="url != null">
url = #{url,jdbcType=VARCHAR},
</if>
<if test="fmt != null">
fmt = #{fmt,jdbcType=VARCHAR},
</if>
<if test="width != null">
width = #{width,jdbcType=INTEGER},
</if>
<if test="height != null">
height = #{height,jdbcType=INTEGER},
</if>
<if test="createtime != null">
createtime = #{createtime,jdbcType=TIMESTAMP},
</if>
<if test="cameraid != null">
cameraid = #{cameraid,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="cc.eguid.cv.web.videoimageshotweb.pojo.VideoshotInfo">
update vnmp_videoshotinfo
set src = #{src,jdbcType=VARCHAR},
out = #{out,jdbcType=VARCHAR},
url = #{url,jdbcType=VARCHAR},
fmt = #{fmt,jdbcType=VARCHAR},
width = #{width,jdbcType=INTEGER},
height = #{height,jdbcType=INTEGER},
createtime = #{createtime,jdbcType=TIMESTAMP},
cameraid = #{cameraid,jdbcType=INTEGER}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>
\ No newline at end of file
#设置级别和目的地
log4j.rootLogger=info,stdout,fout
log4j.logger.me=debug
#mybatis logging
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
org.apache.ibatis.session.AutoMappingUnknownColumnBehavior=debug
#sql logging
log4j.logger.org.apache.commons=info
log4j.logger.java.sql.Connection=info
log4j.logger.java.sql.Statement=info
log4j.logger.java.sql.PreparedStatement=info
log4j.logger.java.sql.ResultSet=info
# Console output
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Encoding=UTF-8
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss},%6.6r]%-5p[%t]%x(%F:%L) - %m%n
#File output
log4j.appender.fout=org.apache.log4j.DailyRollingFileAppender
log4j.appender.fout.Threshold =info
log4j.appender.fout.file=logs/info.log
log4j.appender.fout.Encoding=UTF-8
log4j.appender.fout.layout=org.apache.log4j.PatternLayout
log4j.appender.fout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss},%6.6r]%-5p[%t]%x(%F:%L) - %m%n
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}">world</h1>
<table>
<tr>
<th>截图ID</th>
<th>截图名称</th>
<th>源视频</th>
<th>格式</th>
<th>保存位置</th>
<th>操作</th>
</tr>
<tr th:each="his : ${hislist}">
<td th:text="${his.id}">0</td>
<td th:text="${his.cameraid}">zhansan</td>
<td th:text="${his.src}">1988-06-01</td>
<td th:text="${his.fmt}">12345</td>
<td th:text="${his.out}">1988-06-01</td>
<td><a th:href="'view?id='+${his.id}" target="_blank">查看截图</a></td>
</tr>
</table>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body>
<h1 th:text="${hello}"></h1>
<div>
<label for="src">流媒体视频源(rtsp/rtmp/hls/http-flv地址):</label>
<input type="url" autofocus="autofocus" value="" name="src" id="src">
<label for="fmt">图片格式:</label>
<select id="fmt" name="fmt">
<option value="png">png</option>
<option value="jpg" selected="selected">jpg</option>
<option value="jpeg">jpeg</option>
<option value="gif">gif</option>
<option value="bmp">bmp</option>
</select>
<button id="btn" type="button" name="截图" title="截图" onclick="submit();">截图</button>
</div>
<div>
<p>显示截图<a id="download" target="_blank"></a></p>
<img id="img" alt="截图" style="margin:0;padding:0;width:500;height:500;">
</div>
</body>
<script type="text/javascript">
function isEmpty(s){
return s==undefined||s==null||s.length<1||s=='';
}
function getDom(id){
return document.getElementById(id);
}
function req(reqmethod,url,ret){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
if(typeof ret === "function"){
ret(xmlhttp.responseText);
}
}
};
xmlhttp.open(reqmethod,url,true);
xmlhttp.send();
}
function submit(){
getDom("btn").disabled=true;
var fmt=getDom("fmt").value;
var src=getDom("src").value;
if(isEmpty(src)){
alert("请填写一个视频源");
}
req("GET","shot?src="+src+"&fmt="+fmt,function(res){
getDom("btn").disabled=false;
if(!isEmpty(res)){
var data=eval('('+res+')');
if(isEmpty(res)){
alert(data.msg);
return;
}
viewImg(data.base64);
}else{
alert("请求失败");
}
});
}
function viewImg(data){
var fmt=getDom("fmt").value;
var s='data:image/'+fmt+';base64,'+data;
getDom("img").src=s;
var d=getDom("download");
d.href=s;
d.text="(点击这里下载本截图)";
d.download="截图."+fmt;
}
</script>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${hello}">hello</title>
</head>
<body style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
<img th:src="${his.url!= null} ?${his.url}:'data:image/jpeg;base64,'+${his.base64}" alt="截图预览" style="margin:0;padding:0;width:100%;height:100%;position: absolute;">
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册