【问题标题】:Android sdk cut/trim video fileAndroid sdk 剪切/修剪视频文件
【发布时间】:2021-06-21 00:41:10
【问题描述】:

有什么方法可以在 android 上剪切视频(mp4 或 3gp),比如只使用电影的最后 5 秒...在 iphone 上可以使用 AVAssetExportSession 但在 android 上我没有找到类似的,也许只是一些对 ffmpeg 库的引用,看起来很复杂。有没有更简单的方法?

【问题讨论】:

标签: android video media trim


【解决方案1】:

您可以使用我的 mp4parser 库来做到这一点。看看ShortenExample,它的功能与名称所暗示的完全一样。 由于库无法重新编码视频,它只能在 I 帧处剪切视频。因此,您可以进行切割的时间点非常粗糙。

在 Android 4.1 上,您可以通过 MediaCodec API 访问硬件编解码器,这可能是一个选项(但我还没有看到任何示例)

【讨论】:

  • 这仅适用于 mp4 文件还是适用于其他扩展?在哪里可以找到 ShortenExample 链接?
  • 3gp 文件基本上是 MP4 文件,有一些小的限制。我的 lib 也适用于 3gpp(和 quicktime)文件,但适用于 avi 或 mkv 等其他容器。
  • 我需要一个通用的库,它应该适用于 android 手机可以保存的所有视频格式,比如 mp4、3gp、mkv 可能:(
  • 我用mp4parserAppendExampleShortenExample制作了Android应用TestMp4parser
  • @prasanth 你可能会在阅读 googlecode 上的内容时遇到麻烦。表示项目已移至 github。使用谷歌也有帮助 - 尝试“mp4parser” -> 1st hit
【解决方案2】:

我们可以在 Android 中使用 ffmpeg 剪切视频。

为了在 android 中集成 FFmpeg,我们可以使用预编译库,例如 ffmpeg-android

要剪切带有重新编码的视频,我们可以使用以下命令-

String[] complexCommand = {"-ss", "" + startMs / 1000, "-y", "-i", inputFileAbsolutePath, "-t", "" + (endMs - startMs) / 1000, "-s", "320x240", "-r", "15", "-vcodec", "mpeg4", "-b:v", "2097152", "-b:a", "48000", "-ac", "2", "-ar", "22050", outputFileAbsolutePath};

这里,

-ss

寻找定位

-y

不询问就覆盖输出文件。

-我

ffmpeg 从 -i 选项指定的任意数量的输入“文件”中读取

-t

限制从输入文件读取数据的持续时间

-s

视频输出大小

-r

设置帧率

-vcodec

设置视频编解码器。

-b:v

设置视频码率

-b:a

设置音频比特率

-ac

设置音频通道数。

-ar

设置音频流的采样率(如果已编码)

startMs

从您要剪切的位置开始的视频开始时间(以毫秒为单位)

endMs

您要剪切的视频的结束时间(以毫秒为单位)

要在不重新编码的情况下剪切视频,我们可以使用以下命令-

String[] complexCommand = { "-y", "-i", inputFileAbsolutePath,"-ss", "" + startMs / 1000, "-t", "" + (endMs - startMs) / 1000, "-c","copy", outputFileAbsolutePath};

这里,

-c 复制 将视频、音频和比特流从输入复制到输出文件,无需重新编码。

我创建了一个使用 FFMpeg 编辑视频的示例 android 项目,其中包括剪切视频。检查一下-

https://github.com/bhuvnesh123/FFmpeg-Video-Editor-Android

及其教程在-

https://androidlearnersite.wordpress.com/2017/03/17/ffmpeg-video-editor/

【讨论】:

  • 感谢您的详细解答。您能否确认所有这些命令(即 -ar、-ac、-b:v、-b:a 等)是否都是修剪视频的必需命令?
  • 嗨,有没有办法更快地分割文件?拆分时间太长。
  • @Vivek FFmpeg 提供了某些预设,这些预设是为流程提供一定速度的选项的集合。按速度降序排列的预设是:超快、超快、非常快、更快、快、中、慢、慢、非常慢。默认预设为中等。Utrafast 预设在命令执行时间过长且您想加快进程的情况下特别有用。要使用预设,只需在命令中添加“-preset”、“ultrafast”即可。
  • @Ishaan 不,这些命令不是强制性的,用于重新编码。要在不重新编码的情况下剪切视频,您可以使用-{"-i", inputFileAbsolutePath,"-ss", "" + startMs / 1000, "-t", "" + (endMs - startMs) / 1000, "-c","copy", outputFileAbsolutePath}
  • @SiddarthG 当编辑操作失败时,这取决于你想做什么或向用户显示!
【解决方案3】:

试试这个

Intent trimVideoIntent = new Intent("com.android.camera.action.TRIM");

// The key for the extra has been discovered from com.android.gallery3d.app.PhotoPage.KEY_MEDIA_ITEM_PATH
trimVideoIntent.putExtra("media-item-path",FilePath);
trimVideoIntent.setData(videoUri);

// Check if the device can handle the Intent
List<ResolveInfo> list = getPackageManager().queryIntentActivities(trimVideoIntent, 0);
if (null != list && list.size() > 0) {
    startActivity(trimVideoIntent); // Fires TrimVideo activity into being active
}else {
    Toast.makeText(this, "not supported",Toast.LENGTH_SHORT).show();
}

它在安装了 Gallery2 包的设备上工作

【讨论】:

    【解决方案4】:

    你可以在android中使用MediaCodec API。

    import android.media.MediaCodec.BufferInfo
    import android.media.MediaExtractor
    import android.media.MediaFormat
    import android.media.MediaMetadataRetriever
    import android.media.MediaMuxer
    import java.io.IOException
    import java.nio.ByteBuffer
    import android.os.Handler
    import android.os.Looper
    
    
    class VideoUtils {
        companion object {
            /**
             * @param srcPath the path of source video file.
             * @param dstPath the path of destination video file.
             * @param startMs starting time in milliseconds for trimming. Set to
             * negative if starting from beginning.
             * @param endMs end time for trimming in milliseconds. Set to negative if
             * no trimming at the end.
             * @param useAudio true if keep the audio track from the source.
             * @param useVideo true if keep the video track from the source.
             * @throws IOException
             */
            @Throws(IOException::class)
            fun startTrim(
                srcPath: String, dstPath: String,
                startMs: Int, endMs: Int, useAudio: Boolean, useVideo: Boolean,
                listener: Listener
            ) {
                runOnUiThread {
                    listener.onStart()
                }
                // Set up MediaExtractor to read from the source.
                val extractor = MediaExtractor()
                extractor.setDataSource(srcPath)
                val trackCount = extractor.trackCount
                // Set up MediaMuxer for the destination.
                val muxer = MediaMuxer(dstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
                // Set up the tracks and retrieve the max buffer size for selected
                // tracks.
                val indexMap = HashMap<Int, Int>(trackCount)
                var bufferSize = -1
                for (i in 0 until trackCount) {
                    val format = extractor.getTrackFormat(i)
                    val mime = format.getString(MediaFormat.KEY_MIME)
                    var selectCurrentTrack = false
                    if (mime!!.startsWith("audio/") && useAudio) {
                        selectCurrentTrack = true
                    } else if (mime.startsWith("video/") && useVideo) {
                        selectCurrentTrack = true
                    }
                    if (selectCurrentTrack) {
                        extractor.selectTrack(i)
                        val dstIndex = muxer.addTrack(format)
                        indexMap[i] = dstIndex
                        if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                            val newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
                            bufferSize = if (newSize > bufferSize) newSize else bufferSize
                        }
                    }
                }
                if (bufferSize < 0) {
                    bufferSize = DEFAULT_BUFFER_SIZE
                }
                // Set up the orientation and starting time for extractor.
                val retrieverSrc = MediaMetadataRetriever()
                retrieverSrc.setDataSource(srcPath)
                val degreesString = retrieverSrc.extractMetadata(
                    MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION
                )
                if (degreesString != null) {
                    val degrees = degreesString.toInt()
                    if (degrees >= 0) {
                        muxer.setOrientationHint(degrees)
                    }
                }
                if (startMs > 0) {
                    extractor.seekTo((startMs * 1000).toLong(), MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                }
                // Copy the samples from MediaExtractor to MediaMuxer. We will loop
                // for copying each sample and stop when we get to the end of the source
                // file or exceed the end time of the trimming.
                val offset = 0
                var trackIndex: Int
                val dstBuf = ByteBuffer.allocate(bufferSize)
                val bufferInfo = BufferInfo()
                val totalTimeMs = endMs - startMs
                try {
                    muxer.start()
                    while (true) {
                        bufferInfo.offset = offset
                        bufferInfo.size = extractor.readSampleData(dstBuf, offset)
                        if (bufferInfo.size < 0) {
                            runOnUiThread {
                                listener.onComplete()
                            }
                            bufferInfo.size = 0
                            break
                        } else {
                            bufferInfo.presentationTimeUs = extractor.sampleTime
                            if (endMs > 0 && bufferInfo.presentationTimeUs > endMs * 1000) {
                                runOnUiThread {
                                    listener.onComplete()
                                }
                                break
                            } else {
                                bufferInfo.flags = extractor.sampleFlags
                                trackIndex = extractor.sampleTrackIndex
                                muxer.writeSampleData(
                                    indexMap[trackIndex]!!, dstBuf,
                                    bufferInfo
                                )
                                runOnUiThread {
                                    listener.onProgress((bufferInfo.presentationTimeUs / 1000 - startMs).toFloat() / totalTimeMs)
                                }
                                extractor.advance()
                            }
                        }
                    }
                    muxer.stop()
                } catch (e: IllegalStateException) {
                    runOnUiThread {
                        listener.onError("The source video file is malformed")
                    }
                } finally {
                    muxer.release()
                }
                return
            }
        }
    
        interface Listener {
            fun onStart()
            fun onProgress(value: Float)
            fun onComplete()
            fun onError(message: String)
        }
    }
    
    private val mHandler = Handler(Looper.getMainLooper())
    
    fun runOnUiThread(closure: () -> Unit) {
        mHandler.post {
            closure()
        }
    }
    

    参考:https://android.googlesource.com/platform/packages/apps/Gallery2/+/refs/heads/master/src/com/android/gallery3d/app/VideoUtils.java

    【讨论】:

      【解决方案5】:

      您可以尝试 INDE Media for Mobile - https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials

      它具有作为 MediaComposer 类的转码\remuxing 功能,并且可以为结果文件选择片段。由于它在内部使用 MediaCodec API,因此对电池非常友好并且工作速度尽可能快

      【讨论】:

        【解决方案6】:

        使用这个Android-video-trimmer android library。它使用 Exoplayer2 和 FFmpeg 进行视频修剪

        【讨论】:

          猜你喜欢
          • 2012-07-17
          • 2012-08-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-03-20
          • 2016-07-29
          相关资源
          最近更新 更多