我想调用一个函数并从图像列表中构建一个视频,然后将其本地保存在设备上:
public void CreateAndSaveVideoFile(List<Bitmap> MyBitmapArray)
{
// ..
}
试验:
关注java/xuggle - encode array of images into a movie,答案中的链接是一个死链接
关注How to encode images into a video file in Java through programming?,接受答案中建议的库不支持Android。
上面的下一个答案有针对Android用户的方法,但我不清楚该功能的输入和输出(他在哪里提供图像?他在哪里获得视频?) - 我留下了一个问题评论
上面的下一个答案提供了一个完整的类,但是要包含的所需库有一个损坏的文件(当我尝试从提供的链接下载它时) - 我留下了一个问题评论
关注Java: How do I create a movie from an array of images?,顶部答案中建议的库使用我不熟悉的命令,我甚至不知道如何使用它们。像:
从当前的所有JPEG文件创建MPEG-4文件 目录:
mencoder mf://*.jpg -mf w=800:h=600:fps=25:type=jpg -ovc lavc \ -lavcopts vcodec=mpeg4:mbd=2:trell -oac copy -o output.avi
我不知道如何在Java / Android项目中使用上述内容。
任何人都可以帮助指导我或/并为我提供处理我的任务的方法吗?提前谢谢。
答案 0 :(得分:27)
您可以使用jcodec SequenceEncoder
将图像序列转换为MP4文件。
示例代码:
import org.jcodec.api.awt.SequenceEncoder;
...
SequenceEncoder enc = new SequenceEncoder(new File("filename"));
// GOP size will be supported in 0.2
// enc.getEncoder().setKeyInterval(25);
for(...) {
BufferedImage image = ... // Obtain an image to encode
enc.encodeImage(image);
}
enc.finish();
它是一个java库,所以很容易将它导入到Android项目中,你不必像ffmpeg那样使用NDK。
请参阅http://jcodec.org/了解示例代码&amp;下载。
答案 1 :(得分:13)
使用JCodec
public static void main(String[] args) throws IOException {
SequenceEncoder encoder = new SequenceEncoder(new File("video.mp4"));
for (int i = 1; i < 100; i++) {
BufferedImage bi = ImageIO.read(new File(String.format("img%08d.png", i)));
encoder.encodeImage(bi);
}
encoder.finish();}
现在要将Bitmap转换为BufferedImage,您可以使用此类:
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.io.IOException;
import java.io.InputStream;
/**
* Utility class for loading windows bitmap files
* <p>
* Based on code from author Abdul Bezrati and Pepijn Van Eeckhoudt
*/
public class BitmapLoader {
/**
* Static method to load a bitmap file based on the filename passed in.
* Based on the bit count, this method will either call the 8 or 24 bit
* bitmap reader methods
*
* @param file The name of the bitmap file to read
* @throws IOException
* @return A BufferedImage of the bitmap
*/
public static BufferedImage loadBitmap(String file) throws IOException {
BufferedImage image;
InputStream input = null;
try {
input = ResourceRetriever.getResourceAsStream(file);
int bitmapFileHeaderLength = 14;
int bitmapInfoHeaderLength = 40;
byte bitmapFileHeader[] = new byte[bitmapFileHeaderLength];
byte bitmapInfoHeader[] = new byte[bitmapInfoHeaderLength];
input.read(bitmapFileHeader, 0, bitmapFileHeaderLength);
input.read(bitmapInfoHeader, 0, bitmapInfoHeaderLength);
int nSize = bytesToInt(bitmapFileHeader, 2);
int nWidth = bytesToInt(bitmapInfoHeader, 4);
int nHeight = bytesToInt(bitmapInfoHeader, 8);
int nBiSize = bytesToInt(bitmapInfoHeader, 0);
int nPlanes = bytesToShort(bitmapInfoHeader, 12);
int nBitCount = bytesToShort(bitmapInfoHeader, 14);
int nSizeImage = bytesToInt(bitmapInfoHeader, 20);
int nCompression = bytesToInt(bitmapInfoHeader, 16);
int nColoursUsed = bytesToInt(bitmapInfoHeader, 32);
int nXPixelsMeter = bytesToInt(bitmapInfoHeader, 24);
int nYPixelsMeter = bytesToInt(bitmapInfoHeader, 28);
int nImportantColours = bytesToInt(bitmapInfoHeader, 36);
if (nBitCount == 24) {
image = read24BitBitmap(nSizeImage, nHeight, nWidth, input);
} else if (nBitCount == 8) {
image = read8BitBitmap(nColoursUsed, nBitCount, nSizeImage, nWidth, nHeight, input);
} else {
System.out.println("Not a 24-bit or 8-bit Windows Bitmap, aborting...");
image = null;
}
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
}
}
return image;
}
/**
* Static method to read a 8 bit bitmap
*
* @param nColoursUsed Number of colors used
* @param nBitCount The bit count
* @param nSizeImage The size of the image in bytes
* @param nWidth The width of the image
* @param input The input stream corresponding to the image
* @throws IOException
* @return A BufferedImage of the bitmap
*/
private static BufferedImage read8BitBitmap(int nColoursUsed, int nBitCount, int nSizeImage, int nWidth, int nHeight, InputStream input) throws IOException {
int nNumColors = (nColoursUsed > 0) ? nColoursUsed : (1 & 0xff) << nBitCount;
if (nSizeImage == 0) {
nSizeImage = ((((nWidth * nBitCount) + 31) & ~31) >> 3);
nSizeImage *= nHeight;
}
int npalette[] = new int[nNumColors];
byte bpalette[] = new byte[nNumColors * 4];
readBuffer(input, bpalette);
int nindex8 = 0;
for (int n = 0; n < nNumColors; n++) {
npalette[n] = (255 & 0xff) << 24 |
(bpalette[nindex8 + 2] & 0xff) << 16 |
(bpalette[nindex8 + 1] & 0xff) << 8 |
(bpalette[nindex8 + 0] & 0xff);
nindex8 += 4;
}
int npad8 = (nSizeImage / nHeight) - nWidth;
BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
DataBufferInt dataBufferByte = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer());
int[][] bankData = dataBufferByte.getBankData();
byte bdata[] = new byte[(nWidth + npad8) * nHeight];
readBuffer(input, bdata);
nindex8 = 0;
for (int j8 = nHeight - 1; j8 >= 0; j8--) {
for (int i8 = 0; i8 < nWidth; i8++) {
bankData[0][j8 * nWidth + i8] = npalette[((int) bdata[nindex8] & 0xff)];
nindex8++;
}
nindex8 += npad8;
}
return bufferedImage;
}
/**
* Static method to read a 24 bit bitmap
*
* @param nSizeImage size of the image in bytes
* @param nHeight The height of the image
* @param nWidth The width of the image
* @param input The input stream corresponding to the image
* @throws IOException
* @return A BufferedImage of the bitmap
*/
private static BufferedImage read24BitBitmap(int nSizeImage, int nHeight, int nWidth, InputStream input) throws IOException {
int npad = (nSizeImage / nHeight) - nWidth * 3;
if (npad == 4 || npad < 0)
npad = 0;
int nindex = 0;
BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_4BYTE_ABGR);
DataBufferByte dataBufferByte = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer());
byte[][] bankData = dataBufferByte.getBankData();
byte brgb[] = new byte[(nWidth + npad) * 3 * nHeight];
readBuffer(input, brgb);
for (int j = nHeight - 1; j >= 0; j--) {
for (int i = 0; i < nWidth; i++) {
int base = (j * nWidth + i) * 4;
bankData[0][base] = (byte) 255;
bankData[0][base + 1] = brgb[nindex];
bankData[0][base + 2] = brgb[nindex + 1];
bankData[0][base + 3] = brgb[nindex + 2];
nindex += 3;
}
nindex += npad;
}
return bufferedImage;
}
/**
* Converts bytes to an int
*
* @param bytes An array of bytes
* @param index
* @returns A int representation of the bytes
*/
private static int bytesToInt(byte[] bytes, int index) {
return (bytes[index + 3] & 0xff) << 24 |
(bytes[index + 2] & 0xff) << 16 |
(bytes[index + 1] & 0xff) << 8 |
bytes[index + 0] & 0xff;
}
/**
* Converts bytes to a short
*
* @param bytes An array of bytes
* @param index
* @returns A short representation of the bytes
*/
private static short bytesToShort(byte[] bytes, int index) {
return (short) (((bytes[index + 1] & 0xff) << 8) |
(bytes[index + 0] & 0xff));
}
/**
* Reads the buffer
*
* @param in An InputStream
* @param buffer An array of bytes
* @throws IOException
*/
private static void readBuffer(InputStream in, byte[] buffer) throws IOException {
int bytesRead = 0;
int bytesToRead = buffer.length;
while (bytesToRead > 0) {
int read = in.read(buffer, bytesRead, bytesToRead);
bytesRead += read;
bytesToRead -= read;
}
}
}
答案 2 :(得分:6)
如果您的应用Android SDK的最低版本大于或等于 16 (Android 4.1),则使用Android Media Codec API的最佳视频编码方式。
Android 4.3(SDK 18)中添加了对视频进行编码时,您需要提供Android 4.1(SDK 16) 具有ByteBuffer数组的媒体,但Android 4.3(SDK 18)现在允许 您可以使用Surface作为编码器的输入。例如,这个 允许您对现有视频文件或使用帧的输入进行编码 从OpenGL ES生成。
Media Muxer,因此,为了便于使用Media Muxer编写mp4文件,您应该 SDK&gt; = 18。
使用Media Codec API方式,您将获得硬件加速编码,并且您可轻松编码最高60 FPS。
您可以从 1)开始 How to encode Bitmaps into a video using MediaCodec? 或使用 2) Google Grafika或 3) Bigflake。
从格拉菲卡RecordFBOActivity.java开始。将Choreographer事件替换为您自己包含要编码的位图,删除屏幕上的绘图,将位图加载为Open GL Texture并将其绘制在Media Codec Input Surface上。
答案 3 :(得分:5)
jCodec已添加Android支持。
您需要将这些添加到您的gradle中......
compile 'org.jcodec:jcodec:0.2.3'
compile 'org.jcodec:jcodec-android:0.2.3'
...和
android {
...
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
}
}
我可以确认这是按预期工作的,但有警告。首先是我尝试了一些全尺寸图像并且文件已写入,但在播放时出错。当我按比例缩小时,如果图像的宽度或高度不均匀,我会得到一个错误,因为它需要YUV420J颜色空间的2的倍数。
另外值得注意的是,这会使你的包重,重。我的小项目通过添加这个并且需要启用multidex来超过dex限制。
FileChannelWrapper out = null;
File dir = what ever directory you use...
File file = new File(dir, "test.mp4");
try { out = NIOUtils.writableFileChannel(file.getAbsolutePath());
AndroidSequenceEncoder encoder = new AndroidSequenceEncoder(out, Rational.R(15, 1));
for (Bitmap bitmap : bitmaps) {
encoder.encodeImage(bitmap);
}
encoder.finish();
} finally {
NIOUtils.closeQuietly(out);
}
答案 4 :(得分:2)
您可以使用Bitmp4将图像序列转换为MP4文件。
示例代码:
...
val encoder = MP4Encoder()
encoder.setFrameDelay(50)
encoder.setOutputFilePath(exportedFile.path)
encoder.setOutputSize(width, width)
startExport()
stopExport()
addFrame(bitmap) //called intervally
这是一个Java库,因此很容易将其导入Android项目,而不必像ffmpeg一样使用NDK。
有关示例代码和下载,请参考https://github.com/dbof10/Bitmp4。
答案 5 :(得分:1)
Abhishek V是对的,有关 jcodec SequenceEncoder 的更多信息: 见Android make animated video from list of images
最近我使用覆盆子pi和Android设备构建了一个实时视频系统,遇到了和你一样的问题。我没有保存图像文件列表,而是使用了一些实时流媒体协议,如 RTP / RTCP ,将数据流传输给用户。如果你的要求是这样的,也许你可以改变你的策略。
另一个建议是你可以探索一些C / C ++库,使用NDK / JNI来打破Java的限制。
希望这些建议对你有意义:)
答案 6 :(得分:1)
您可以使用ffmpeg库从图像阵列制作视频。 FFMPEG库对于制作视频非常有用。 也许以下链接将帮助您。 http://osric.com/chris/accidental-developer/2012/04/using-ffmpeg-to-programmatically-slice-and-splice-video/ https://groups.google.com/forum/#!topic/android-ndk/sxDYlGYK-Xg
答案 7 :(得分:1)
你有位图,你可以使用JCodec将其翻转成视频
这是一个示例图像序列编码器:
您可以通过将BufferedImage替换为Bitmap来修改它。
根据您的需要使用这些方法。
layers {
name: "myLayer1"
type: CONVOLUTION
bottom: "data"
top: "myLayer1"
blobs_lr: 1
blobs_lr: 2
convolution_param {
num_output: 20
kernel_w: 2
kernel_h: 2
stride_w: 1
stride_h: 1
weight_filler {
type: "msra"
variance_norm: AVERAGE
}
bias_filler {
type: "constant"
}
}
}
答案 8 :(得分:0)
我创建了一个应该能够处理此项目的项目。代码简短而直接。