Android 3分钟一个库搞定视频替换音频 视频合成 视频裁剪(高仿剪映)

2 篇文章 0 订阅
1 篇文章 0 订阅

 

几种框架的比较:

 

 

方法一(Fail)

利用MediaMux实现音视频的合成。

效果:可以实现音视频的合并,利用Android原生的VideoView和SurfaceView播放正常,大部分的播放器也播放正常,但是,但是,在上传Youtube就会出现问题:音频不连续,分析主要是上传Youtube时会被再次的压缩,可能在压缩的过程中出现音频的帧率出现问题。

 

方法二

利用mp4parser实现

mp4parser是一个视频处理的开源工具箱,由于mp4parser里的方法都依靠工具箱里的一些内容,所以需要将这些内容打包成jar包,放到自己的工程里,才能对mp4parser的方法进行调用。

 

 

方法三

利用FFmpeg大法

FFmpeg 由于其丰富的 codec 插件,详细的文档说明,并且与其调试复杂量大的编解码代码(是的,用 MediaCodec 实现起来十分啰嗦和繁琐)还是不如调试一行 ffmpeg 命令来的简单。

 

实现步骤:
1.添加依赖
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
2.视频裁剪
/**
 * 裁剪视频
 * @param srcPath 需要裁剪的原视频路径
 * @param outPath 裁剪后的视频输出路径
 * @param startTimeMs 裁剪的起始时间
 * @param endTimeMs 裁剪的结束时间
 */
public static void clip(String srcPath, String outPath, double startTimeMs, double endTimeMs) throws IOException, IllegalArgumentException {
    if (TextUtils.isEmpty(srcPath) || TextUtils.isEmpty(outPath)) {
        throw new IllegalArgumentException("file path can't be null!!!!");
    }
    if (!(new File(srcPath).exists())) {
        throw new IllegalArgumentException("the source file is not exist!!!!");
    }
    if (startTimeMs >= endTimeMs) {
        throw new IllegalArgumentException("the startTimeMs is larger than endTimeMs!!!!");
    }
    Movie movie = MovieCreator.build(srcPath);
    List<Track> tracks = movie.getTracks();
    //移除旧的track
    movie.setTracks(new LinkedList<Track>());
    //处理的时间以秒为单位
    double startTime = startTimeMs/1000;
    double endTime = endTimeMs/1000;
    YDLog.logDebeg(TAG, "--->>>>startTimeMs = " + startTimeMs + "\n endTimeMs = " + endTimeMs + "\n tracks.size = " + tracks.size());
    //计算剪切时间,视频的采样间隔大,以视频为准
    for (Track track : tracks) {
        if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
            startTime = correctTimeToSyncSample(track, startTime, false);
            endTime = correctTimeToSyncSample(track, endTime, true);
            if (track.getHandler().equals("vide")) {
                break;
            }
        }
    }
    YDLog.logDebeg(TAG, "--->>>>startTime = " + startTime + "\n endTime = " + endTime);

    long currentSample;
    double currentTime;
    double lastTime;
    long startSample1;
    long endSample1;
    long delta;

    for (Track track : tracks) {
        currentSample = 0;
        currentTime = 0;
        lastTime = -1;
        startSample1 = -1;
        endSample1 = -1;

        //根据起始时间和截止时间获取起始sample和截止sample的位置
        for (int i = 0; i < track.getSampleDurations().length; i++) {
            delta = track.getSampleDurations()[i];
            if (currentTime > lastTime && currentTime <= startTime) {
                startSample1 = currentSample;
            }
            if (currentTime > lastTime && currentTime <= endTime) {
                endSample1 = currentSample;
            }
            lastTime = currentTime;
            currentTime += (double)delta / (double)track.getTrackMetaData().getTimescale();
            currentSample++;
        }
        Log.d(TAG, "track.getHandler() = " + track.getHandler() + "\n startSample1 = " + startSample1 + "\n endSample1 = " + endSample1);
        if (startSample1 <= 0 && endSample1 <= 0) {
            throw new RuntimeException("clip failed !!");
        }
        movie.addTrack(new CroppedTrack(track, startSample1, endSample1));// 添加截取的track
    }

    //合成视频mp4
    Container out = new DefaultMp4Builder().build(movie);
    FileOutputStream fos = new FileOutputStream(outPath);
    FileChannel fco = fos.getChannel();
    out.writeContainer(fco);
    fco.close();
    fos.close();
}
3.视频合成

/**
 * 将 AAC 和 MP4 进行混合[替换了视频的音轨]
 *
 * @param aacPath .aac
 * @param mp4Path .mp4
 * @param outPath .mp4
 */
public static boolean muxAacMp4(String aacPath, String mp4Path, String outPath) {
    try {
        AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(aacPath));
        Movie videoMovie = MovieCreator.build(mp4Path);
        Track videoTracks = null;// 获取视频的单纯视频部分
        for (Track videoMovieTrack : videoMovie.getTracks()) {
            if ("vide".equals(videoMovieTrack.getHandler())) {
                videoTracks = videoMovieTrack;
            }
        }

        Movie resultMovie = new Movie();
        resultMovie.addTrack(videoTracks);// 视频部分
        resultMovie.addTrack(aacTrack);// 音频部分

        Container out = new DefaultMp4Builder().build(resultMovie);
        FileOutputStream fos = new FileOutputStream(new File(outPath));
        out.writeContainer(fos.getChannel());
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }


    return true;
}
4.合成音视频长度不一样的问题
/***
 * 把视频裁剪15s。然后和音频合并。然后再裁剪15s
 * @param sdCardPath
 */
public static void getMergeVideo(String sdCardPath) {
    String srcMergePath = sdCardPath + "/4.mp4";//录屏文件
    String aacPath = sdCardPath + "/5.aac";
    Log.d("peng", "onClick:  aacPath" + aacPath);
    String outMergePath = sdCardPath + "/6.mp4";//得到临时的合成的视频文件
    Log.d("peng", "onClick:  outMergePath" + outMergePath);

    long startTime = 0;
    long endTime = 15 * 1000;

    boolean isMux =  VideoClipUtils.muxAacMp4(aacPath, srcMergePath, outMergePath);

    if (isMux) {
        String outMergeClipPath = sdCardPath + "/7.mp4";
        Log.d("peng", "onClick:  outMergeClipPath" + outMergeClipPath);

        try {
            VideoClipUtils.clip(outMergePath, outMergeClipPath, startTime, endTime);
            Log.d("peng", "OKKKKKKKK");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

遇到的问题:

1.音频和视频合成,他们的长度
     要以视频为准的
2.合成音视频的格式,是否支持,2种方案

 

方案一:
先全部合并,然后再裁剪15s
方案二:(没有声音)
把视频裁剪15s。然后和音频合并。然后再裁剪15s
方案三:(导致有视频不一定有声音,或者有声音不一定有视频)
视频剪切,音频剪切,音频和视频合并
 
 
方法一:
MediaMuxer文件生成:
官方文档:
=========================================================
mp4方案:
 
 
使用map4parser作为视频处理包,android studio引入 compile 'com.googlecode.mp4parser:isoparser:1.1.21'//视频处理
 

2017年4月10日更新:

我发现该框架还有诸多问题和BUG,比如无法合并不同格式(帧率,分辨率)的视频,最近已改用MediaCodec,如果需要做一些比较复杂的处理,还是推荐使用MediaCodec和FFmpeg,后面有时间我会写一写相关的内容。

 

Android 使用 mp4parser 做视频裁剪

 
 

mp4parser (所有的用法-----可以)

 
不错的库:
 
 
 
 

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个实现视频裁剪并适配屏幕大小的示例方法: ```java public static void cropAndFitToScreen(String videoPath, TextureView textureView) { MediaPlayer mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(videoPath); mediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); return; } int videoWidth = mediaPlayer.getVideoWidth(); int videoHeight = mediaPlayer.getVideoHeight(); float videoAspectRatio = (float) videoWidth / (float) videoHeight; int viewWidth = textureView.getWidth(); int viewHeight = textureView.getHeight(); float viewAspectRatio = (float) viewWidth / (float) viewHeight; // Calculate the crop width and height to maintain video aspect ratio int cropWidth, cropHeight; if (videoAspectRatio > viewAspectRatio) { cropWidth = (int) (videoHeight * viewAspectRatio); cropHeight = videoHeight; } else { cropWidth = videoWidth; cropHeight = (int) (videoWidth / viewAspectRatio); } // Calculate the crop position int cropX = (videoWidth - cropWidth) / 2; int cropY = (videoHeight - cropHeight) / 2; // Set up the texture view textureView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(videoWidth, videoHeight); Surface surface = new Surface(surfaceTexture); // Set up the media player with the cropped video surface mediaPlayer.setSurface(surface); mediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); mediaPlayer.setVideoCrop(cropX, cropY, cropWidth, cropHeight); // Start the video playback mediaPlayer.start(); } ``` 这个方法首先创建了一个MediaPlayer对象,并设置其数据源为视频文件路径。然后,它获取视频的宽度和高度,并计算出视频的宽高比。接下来,它获取TextureView的宽度和高度,并计算出TextureView的宽高比。然后,它根据视频和TextureView的宽高比,计算出裁剪后的视频宽度、高度和位置。最后,它设置TextureView的布局参数,创建一个SurfaceTexture对象,并将其作为渲染目标传递给MediaPlayer。 这个示例方法可以让你裁剪视频并适配到TextureView上,但你需要在调用这个方法之前确保TextureView已经被正确地添加到布局中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值