Pregunta

Estoy editando un MP4 en Android utilizando MediaExtractor para obtener pistas de audio y video, luego creando un nuevo archivo usando MediaMuxer.Funciona bien.Puedo jugar el nuevo MP4 en el teléfono (y otros jugadores), pero no puedo transmitir el archivo en la web.Cuando detengo el MediaMuxer, genera un mensaje de registro

"El archivo MP4 no se transmbolará".

Miré el código nativo subyacente (MPEG4WRITER.CPP) y parece que el escritor tiene problemas para calcular el tamaño necesario de la caja de MOOV.Intenta adivinar el uso de algo heurístico si no se suministra una tasa de bits como un parámetro para el escritor.El problema es que el MediaMuxer no proveedorá la capacidad de establecer los parámetros de MPEG4Writer.¿Falta algo o estoy atascado buscando algún otro medio para generar el archivo (o encabezado)?Gracias.

¿Fue útil?

Solución

en MPEG4WRITER.CPP:

// The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
// where 1MB is the common file size limit for MMS application.
// The default MAX _MOOV_BOX_SIZE value is based on about 3
// minute video recording with a bit rate about 3 Mbps, because
// statistics also show that most of the video captured are going
// to be less than 3 minutes.

Esta es una mala suposición sobre cómo se puede usar MediaMuxer.Estamos grabando un máximo de 15 segundos de videos de res mayor y MIN_MOOV_BOX_SIZE es demasiado pequeño.Por lo tanto, para hacer que el archivo streamable tenga que reescribir el archivo para mover el encabezado MOOV antes de MDAT y parchear algunas compensaciones.Aquí está mi código.No es genial.Las rutas de error no se manejan correctamente y hace suposiciones sobre el orden de las casillas.

public void fastPlay(String srcFile, String dstFile)  {
    RandomAccessFile inFile = null;
    FileOutputStream outFile = null;
    try {
        inFile = new RandomAccessFile(new File(srcFile), "r");
        outFile = new FileOutputStream(new File(dstFile));
        int moovPos = 0;
        int mdatPos = 0;
        int moovSize = 0;
        int mdatSize = 0;
        byte[] boxSizeBuf = new byte[4];
        byte[] pathBuf = new byte[4];
        int boxSize;
        int dataSize;
        int bytesRead;
        int totalBytesRead = 0;
        int bytesWritten = 0;

        // First find the location and size of the moov and mdat boxes
        while (true) {
            try {
                boxSize = inFile.readInt();
                bytesRead = inFile.read(pathBuf);
                if (bytesRead != 4) {
                    Log.e(TAG, "Unexpected bytes read (path) " + bytesRead);
                    break;
                }
                String pathRead = new String(pathBuf, "UTF-8");
                dataSize = boxSize - 8;
                totalBytesRead += 8;
                if (pathRead.equals("moov")) {
                    moovPos = totalBytesRead - 8;
                    moovSize = boxSize;
                } else if (pathRead.equals("mdat")) {
                    mdatPos = totalBytesRead - 8;
                    mdatSize = boxSize;
                }
                totalBytesRead += inFile.skipBytes(dataSize);
            } catch (IOException e) {
                break;
            }
        }

        // Read the moov box into a buffer. This has to be patched up. Ug.
        inFile.seek(moovPos);
        byte[] moovBoxBuf = new byte[moovSize]; // This shouldn't be too big.
        bytesRead = inFile.read(moovBoxBuf);
        if (bytesRead != moovSize) {
            Log.e(TAG, "Couldn't read full moov box");
        }

        // Now locate the stco boxes (chunk offset box) inside the moov box and patch
        // them up. This ain't purdy.
        int pos = 0;
        while (pos < moovBoxBuf.length - 4) {
            if (moovBoxBuf[pos] == 0x73 && moovBoxBuf[pos + 1] == 0x74 &&
                    moovBoxBuf[pos + 2] == 0x63 && moovBoxBuf[pos + 3] == 0x6f) {
                int stcoPos = pos - 4;
                int stcoSize = byteArrayToInt(moovBoxBuf, stcoPos);
                patchStco(moovBoxBuf, stcoSize, stcoPos, moovSize);
            }
            pos++;
        }

        inFile.seek(0);
        byte[] buf = new byte[(int) mdatPos];
        // Write out everything before mdat
        inFile.read(buf);
        outFile.write(buf);

        // Write moov
        outFile.write(moovBoxBuf, 0, moovSize);

        // Write out mdat
        inFile.seek(mdatPos);
        bytesWritten = 0;
        while (bytesWritten < mdatSize) {
            int bytesRemaining = (int) mdatSize - bytesWritten;
            int bytesToRead = buf.length;
            if (bytesRemaining < bytesToRead) bytesToRead = bytesRemaining;
            bytesRead = inFile.read(buf, 0, bytesToRead);
            if (bytesRead > 0) {
                outFile.write(buf, 0, bytesRead);
                bytesWritten += bytesRead;
            } else {
                break;
            }
        }
    } catch (IOException e) {
        Log.e(TAG, e.getMessage());
    } finally {
        try {
            if (outFile != null) outFile.close();
            if (inFile != null) inFile.close();
        } catch (IOException e) {}
    }
}

private void patchStco(byte[] buf, int size, int pos, int moovSize) {
    Log.e(TAG, "stco " + pos + " size " + size);
    // We are inserting the moov box before the mdat box so all of
    // offsets in the stco box need to be increased by the size of the moov box. The stco
    // box is variable in length. 4 byte size, 4 byte path, 4 byte version, 4 byte flags
    // followed by a variable number of chunk offsets. So subtract off 16 from size then
    // divide result by 4 to get the number of chunk offsets to patch up.
    int chunkOffsetCount = (size - 16) / 4;
    int chunkPos = pos + 16;
    for (int i = 0; i < chunkOffsetCount; i++) {
        int chunkOffset = byteArrayToInt(buf, chunkPos);
        int newChunkOffset = chunkOffset + moovSize;
        intToByteArray(newChunkOffset, buf, chunkPos);
        chunkPos += 4;
    }
}

public static int byteArrayToInt(byte[] b, int offset)
{
    return   b[offset + 3] & 0xFF |
            (b[offset + 2] & 0xFF) << 8 |
            (b[offset + 1] & 0xFF) << 16 |
            (b[offset] & 0xFF) << 24;
}

public void intToByteArray(int a, byte[] buf, int offset)
{
    buf[offset] = (byte) ((a >> 24) & 0xFF);
    buf[offset + 1] = (byte) ((a >> 16) & 0xFF);
    buf[offset + 2] = (byte) ((a >> 8) & 0xFF);
    buf[offset + 3] = (byte) (a  & 0xFF);
}

Otros consejos

actualmente MediaMuxer no crea archivos MP4 seduckables

Puede probar Intel Inde en https://software.intel.com/en-EE.UU. / Intel-Inde y Paquete de medios para Android, que es parte de INDE, tutoriales en https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials .Tiene una muestra que muestra cómo usar el paquete de medios para crear y transmitir archivos a través de la red

Por ejemplo, para la transmisión de la cámara, tiene muestra CAMERASTREMERACTIVITY.JAVA

public void onCreate(Bundle icicle) {

    capture = new CameraCapture(new AndroidMediaObjectFactory(getApplicationContext()), progressListener);

    parameters = new StreamingParameters();
    parameters.Host = getString(R.string.streaming_server_default_ip);
    parameters.Port = Integer.parseInt(getString(R.string.streaming_server_default_port));
    parameters.ApplicationName = getString(R.string.streaming_server_default_app);
    parameters.StreamName = getString(R.string.streaming_server_default_stream);

    parameters.isToPublishAudio = false;
    parameters.isToPublishVideo = true;
}

public void startStreaming() {
    configureMediaStreamFormat();
    capture.setTargetVideoFormat(videoFormat);
    capture.setTargetAudioFormat(audioFormat);
    capture.setTargetConnection(prepareStreamingParams());
    capture.start();
}

Además, hay muestras simulares para la transmisión de archivos o el proceso de juego Captura y transmisión

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top