2016-10-29 43 views
21

我要調用一個函數,並建立一個視頻輸出圖像的列表,然後保存在本地設備上:如何從Android中的圖像數組創建視頻?

public void CreateAndSaveVideoFile(List<Bitmap> MyBitmapArray) 
{ 
    // .. 
} 

試驗:

當前 目錄創建所有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

不要幀的位圖先。並且不要將它們放在列表中,只要你將不在記憶中。只需將您的幀直接寫入文件即可。 – greenapps

+0

請比較幀大小/長度與位圖需要的內存大小和報告。如果你想使用列表,那麼最好把框架放在裏面。只需比較所需的記憶。 – greenapps

+0

@greenapps在按鈕上單擊RecordVideo,您希望我開始在設備上本地保存ReceivedImages。你會推薦這種方式來保存[保存和讀取位圖/圖像](http://stackoverflow.com/questions/17674634/saving-and-reading-bitmaps-images-from-internal-memory-in-android)?我繼續執行以保存單個圖像,然後向您報告它的大小。 –

回答

23

您可以使用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項目,你不必使用NDK不同的ffmpeg。

參考http://jcodec.org/示例代碼&下載。

+0

太棒了,看起來像我在找什麼。我會試一試,謝謝! –

1

阿布舍克五世權,更多有關jcodec SequenceEncoder: 看到Android make animated video from list of images

最近我已經建立了使用樹莓派和Android設備的實時視頻系統,遇到了同樣的問題你。我沒有保存圖像文件列表,而是使用了一些實時流協議,如RTP/RTCP將數據流傳輸給用戶。如果你的要求是這樣的話,也許你可以改變你的策略。

另一個建議是,您可以探索一些C/C++庫,使用NDK/JNI來打破Java的限制。

希望這些建議對你有意義:)

+0

是的,它對我有意義:)感謝您的建議和鏈接。我們是否可以去聊天室,也許我們可以更多地談論這個策略? –

+0

這個頁面上的內容幫了我很多,這個例子完全實用:[android-streaming-live-camera-video-to-web](http://www.androidhive.info/2014/06/android-流媒體,實時攝像機視頻到網頁/)。如果此策略符合您的要求,並且您有更多關於實施的問題,我們可能會繼續聊天 – JY0284

3

如果您的應用程序的Android SDK的最低版本是大於或等於16 (安卓4.1)視頻編碼的最佳方式是使用Android Media Codec API

Android 4.3 APIs

當編碼視頻,機器人4.1(SDK 16)要求你提供 媒體用的ByteBuffer陣列,而Android 4.3(SDK 18)現在允許 您使用表面作爲輸入到編碼器。例如,這個 允許您對來自現有視頻文件的輸入或使用從OpenGL ES生成的幀 進行編碼。

Media Muxer加入的Android 4.3(SDK 18),所以寫MP4文件與媒體複用器你應該有SDK> = 18的便捷方式。

使用媒體編解碼器API的方式,你會得到硬件加速編碼,你很容易編碼高達60幀/秒。

可以從1)How to encode Bitmaps into a video using MediaCodec? 啓動或使用2)Google Grafika3)Bigflake

從Grafika RecordFBOActivity.java開始。用您自己的包含位圖替換Choreographer事件進行編碼,刪除屏幕上的圖形,將您的位圖加載爲Open GL Texture並在媒體編解碼器輸入表面上繪製它。

+0

嗨,並感謝您的鏈接。你能幫忙寫出完整的代碼嗎? –

+0

請參閱http://stackoverflow.com/questions/20343534/illegalstateexception-at-mediacodec-dequeinputbuffer-dequeoutputbuffer中的代碼 – AndreyICE

1

你有位圖,你可以使用JCodec

這裏翻轉到視頻的樣本圖像序列編碼器:

您可以通過更換BufferedImage的修改你的目的位圖。

根據您的需要使用這些方法。

public static Picture fromBitmap(Bitmap src) { 
    Picture dst = Picture.create((int)src.getWidth(), (int)src.getHeight(), RGB); 
    fromBitmap(src, dst); 
    return dst; 
} 

public static void fromBitmap(Bitmap src, Picture dst) { 
    int[] dstData = dst.getPlaneData(0); 
    int[] packed = new int[src.getWidth() * src.getHeight()]; 

    src.getPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight()); 

    for (int i = 0, srcOff = 0, dstOff = 0; i < src.getHeight(); i++) { 
    for (int j = 0; j < src.getWidth(); j++, srcOff++, dstOff += 3) { 
     int rgb = packed[srcOff]; 
     dstData[dstOff]  = (rgb >> 16) & 0xff; 
     dstData[dstOff + 1] = (rgb >> 8) & 0xff; 
     dstData[dstOff + 2] = rgb & 0xff; 
    } 
    } 
} 

public static Bitmap toBitmap(Picture src) { 
    Bitmap dst = Bitmap.create(pic.getWidth(), pic.getHeight(), ARGB_8888); 
    toBitmap(src, dst); 
    return dst; 
} 

public static void toBitmap(Picture src, Bitmap dst) { 
    int[] srcData = src.getPlaneData(0); 
    int[] packed = new int[src.getWidth() * src.getHeight()]; 

    for (int i = 0, dstOff = 0, srcOff = 0; i < src.getHeight(); i++) { 
    for (int j = 0; j < src.getWidth(); j++, dstOff++, srcOff += 3) { 
     packed[dstOff] = (srcData[srcOff] << 16) | (srcData[srcOff + 1] << 8) | srcData[srcOff + 2]; 
    } 
    } 
    dst.setPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight()); 
} 
6

使用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();} 

我們您的位圖轉換爲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; 
    } 
} 
} 

原始question and answer here.