@ruboss

Как разбить часть видео на фреймы, не делая при этом лишних телодвижений с бубнами (Java, streams)?

Все привет!
Есть задачка, в которой нужно разбить видео на кадры с определенной частотой.
Причем не всегда нужно разбивать всё видео.
Например: мне нужно достать из видео "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);
	}
  • Вопрос задан
  • 1422 просмотра
Решения вопроса 1
@ruboss Автор вопроса
videoGrabber.setTimestamp(curts);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы