Все привет!
Есть задачка, в которой нужно разбить видео на кадры с определенной частотой.
Причем не всегда нужно разбивать всё видео.
Например: мне нужно достать из видео "Movie1" кадры с 00:11:10 по 00:12:20 с частотой 2 кадра в секунду.
70 с = 140 кадров. Причем "Movie1" может быть как файл на сервере с программой, так и удаленный файл, который придется закачивать через интернет.
Т.е. Если мне нужно 70 секунд видео, то не оптимально будет качать для этого всю двухчасовую запись.
Суть проблемы:
1) Как можно сделать seek для удаленного файла? Если я знаю его высоту и ширину, но не знаю fps ?
2) Какие есть рабочие библиотеки для работы с видеофайлами на java? Поддержка разных форматов, отделение звука от видео и т.д?
Пытался сделать на FFmpeg из javacv, не могу понять как пропустить часть фреймов. Похоже что кроме grab() и grabFrame() никак не пропустить. Поэтому обработка видео 30 секунд занимает 28 секунд, что совсем не подходит
public void loadVideo() {
av_log_set_level(AV_LOG_QUIET); //off logs
FrameGrabber videoGrabber = new FFmpegFrameGrabber(fileName); //init
CanvasFrame canvas = new CanvasFrame("test1"); //canvas
try {
//videoGrabber.setFrameRate(48.0); //not working
videoGrabber.setFormat(format); // mp4 for example
videoGrabber.start(); //start
} catch (com.googlecode.javacv.FrameGrabber.Exception e) {
e.printStackTrace();
}
Frame vFrame = null;
double fps = videoGrabber.getFrameRate(); //get fps
System.out.println(fps);
int cnt = 0;
int each = (int) (Math.round(fps / framePerSec)); //count which frame w need to get
if (each > 1)
each--; // for example each 12`th if we have fps = 25
// that mean we have 2 image per second
do {
try {
vFrame = videoGrabber.grabFrame(); //grab
if (vFrame != null) {
IplImage img = vFrame.image;
if (img != null) {
canvas.showImage(img); //show image
cnt++;
if (cnt % each == 0) {
int seconds = (int) (cnt / fps);
list.add(cvCloneImage(img)); //add to some list of images for processing
timeList.add(seconds);
cvSaveImage("frames/f_"+seconds+".png",img); //save image
}
}
}
} catch (com.googlecode.javacv.FrameGrabber.Exception e) {
e.printStackTrace();
}
} while (vFrame != null);
}
Пытался также сделать через Xuggler - не работает seekKeyFrame:
public void videoSeparator() throws IOException{
String filename = fileName;
IContainer container = IContainer.make(); //создаем контейнер
if (container.open(filename, IContainer.Type.READ, null) < 0) //пытаемся открыть
throw new IllegalArgumentException("could not open file: "
+ filename);
int numStreams = container.getNumStreams(); //количество потоков
int videoStreamId = -1;
IStreamCoder videoCoder = null;
double fps = 24.0;
// нужно найти видео поток
for (int i = 0; i < numStreams; i++) {
IStream stream = container.getStream(i);
fps = stream.getFrameRate().getDouble(); //fps
IStreamCoder coder = stream.getStreamCoder();
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
videoStreamId = i;
videoCoder = coder;
break;
}
}
if (videoStreamId == -1)
//если не нашли
throw new RuntimeException(
"could not find video stream in container: " + filename);
//пытаемся открыть кодек
if (videoCoder.open() < 0)
throw new RuntimeException(
"could not open video decoder for container: " + filename);
IPacket packet = IPacket.make();
long start = 0;
long end = container.getDuration(); //длина видео в микросекундах
System.out.println("s = "+start+" e = "+end);
// микросекунды - пол секунды
long step = 1000*500;
int cnt = 0;
CanvasFrame canvas = new CanvasFrame("test1"); //холст для отображения
END: while (container.readNextPacket(packet) >= 0) { //читаем пакеты с контейнера
if (packet.getStreamIndex() == videoStreamId) { //если это видео
cnt++;
IVideoPicture picture = IVideoPicture.make(
videoCoder.getPixelType(), videoCoder.getWidth(),
videoCoder.getHeight());
int offset = 0;
while (offset < packet.getSize()) { //считываем в изображение
int bytesDecoded = videoCoder.decodeVideo(picture, packet,
offset);
// Если что-то пошло не так
if (bytesDecoded < 0)
throw new RuntimeException(
"got error decoding video in: " + filename);
offset += bytesDecoded;
if (picture.isComplete()) { // когда все считали
IVideoPicture newPic = picture;
// в микросекундах
long timestamp = picture.getTimeStamp();
BufferedImage javaImage = Utils
.videoPictureToImage(newPic);
canvas.showImage(javaImage); //выводим изображение
if (timestamp > end) { //выходим если установленое время вышло
break END;
}
}
}
//container.seekKeyFrame(videoStreamId, step, IContainer.SEEK_FLAG_BYTE); //пытаемся перскочить пол секунды - не работает
}
}
System.out.println("total count = "+cnt);
if (videoCoder != null) {
videoCoder.close();
videoCoder = null;
}
if (container != null) {
container.close();
container = null;
}
System.out.println("Count = " + count);
}