模仿微视视频录制、支持按下录制抬起暂停以及断点进度条(基于javacv)

apk下载视频录制apk下载

项目源码地址为https://github.com/qdrzwd/VideoRecorder  

补充:感谢雷军辉提供的文档

wiki: http://www.elesos.com/index.php?title=%E5%AF%B9android%E5%BD%95%E5%88%B6%E7%9A%84%E8%A7%86%E9%A2%91%E6%95%B0%E6%8D%AENV21%E8%BF%9B%E8%A1%8C%E6%97%8B%E8%BD%AC%E4%B8%8E%E5%89%AA%E5%88%87

 这里面有解决旋转办法,另外githup上面的源码已更新。

  

现在应该已经有很多人知道Vine和Instagram。前者做短视频分享起家后被Twitter收购,后者做照片分享起家后被Facebook收购,随后也迅速添加了视频分享功能。可以说短视频分享是社交网络适应移动互联网发展的大趋势。短视频分享应用已经如雨后春笋般崛起(算上最近新浪在推的“秒拍”,国内数得上名字的短视频分享已经有微视、秒拍、微可拍、玩拍,等等)。目前网上的录制视频的资料都是基于android自带的MediaRecorder类,但是这个类实在是个鸡肋,在实际项目中基本没什么用处。下面的项目中,录制视频使用javacv自带的api, 录制声音使用的是android的AudioRecord。


用过微视或vine的人都知道,他们的视频录制体验都是非常好的。微视虽然是模仿vine,

但是视频录制这一块我认为不比vine差。自己一直想做一个类似的视频录制app。

反编译微视和vine之后,发现微视使用的是自己编译的ffmpeg库,vine使用的是javacv库。

网上查资料后发现javacv已经提供了视频录制功能,并且包含有视频编辑等图像处理功能。

但是这之前从来没有听说过javacv,在上网查各种资料,纠结了几天之后,

偶然在githup上面发现了这个项目https://github.com/sourab-sharma/TouchToRecord

本文以下视频录制功能,主要就是基于这个项目。在这里再次感谢原作者。


视频录制存在的问题

1.如何获取摄像头的数据

2.如何把获取到的数据保存到视频文件中

3.如何录制音频,并和视频合并

4.录制视频时如何实现暂停功能

5.android摄像头支持的分辨率可能不符合需求,需要转换分辨率

6.android手机录制出来的视频是旋转了90度的,如何实现旋转(转换到前置摄像头又如何处理)

先上几张最后的成果图


就单纯这几张图来看已经很像微视了,有木有,实现了断点录制视频、断点进度条、音视频混合、录制完成后分辨率为480*480。

但是还存在很多问题,视频旋转、利用图片合成视频、使用本地视频剪辑、后期视频特效添加等等。

下面开始代码展示了

首先需要javacv和javacpp的jar包,需要javacv需要的so库文件





这些都是必须的库文件,并且都可以在javacv开源项目中找到https://code.google.com/p/javacv/

直接上代码

首先是FFmpegFrameRecorder类,这个类是javacv包中就有的,已经实现了视频录制和音频录制,我们只是需要做一些修改

代码中已经写了比较详细的注释,就不再详细介绍了。

/*
 * Copyright (C) 2009,2010,2011,2012,2013 Samuel Audet
 *
 * This file is part of JavaCV.
 *
 * JavaCV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version (subject to the "Classpath" exception
 * as provided in the LICENSE.txt file that accompanied this code).
 *
 * JavaCV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JavaCV.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Based on the output-example.c file included in FFmpeg 0.6.5
 * as well as on the decoding_encoding.c file included in FFmpeg 0.11.1,
 * which are covered by the following copyright notice:
 *
 * Libavformat API example: Output a media file in any supported
 * libavformat format. The default codecs are used.
 *
 * Copyright (c) 2001,2003 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.qd.recorder;

import com.googlecode.javacpp.BytePointer;
import com.googlecode.javacpp.DoublePointer;
import com.googlecode.javacpp.FloatPointer;
import com.googlecode.javacpp.IntPointer;
import com.googlecode.javacpp.Loader;
import com.googlecode.javacpp.Pointer;
import com.googlecode.javacpp.PointerPointer;
import com.googlecode.javacpp.ShortPointer;
import com.googlecode.javacv.FrameRecorder;

import java.io.File;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Map.Entry;

import static com.googlecode.javacv.cpp.avcodec.*;
import static com.googlecode.javacv.cpp.avformat.*;
import static com.googlecode.javacv.cpp.avutil.*;
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.swresample.*;
import static com.googlecode.javacv.cpp.swscale.*;

/**
 *
 * @author Samuel Audet
 */
public class FFmpegFrameRecorder extends FrameRecorder {
    public static FFmpegFrameRecorder createDefault(File f, int w, int h)   throws Exception { return new FFmpegFrameRecorder(f, w, h); }
    public static FFmpegFrameRecorder createDefault(String f, int w, int h) throws Exception { return new FFmpegFrameRecorder(f, w, h); }

    private static Exception loadingException = null;
    public static void tryLoad() throws Exception {
        if (loadingException != null) {
            throw loadingException;
        } else {
            try {
                Loader.load(com.googlecode.javacv.cpp.avutil.class);
                Loader.load(com.googlecode.javacv.cpp.avcodec.class);
                Loader.load(com.googlecode.javacv.cpp.avformat.class);
                Loader.load(com.googlecode.javacv.cpp.swscale.class);
            } catch (Throwable t) {
                if (t instanceof Exception) {
                    throw loadingException = (Exception)t;
                } else {
                    throw loadingException = new Exception("Failed to load " + FFmpegFrameRecorder.class, t);
                }
            }
        }
    }

    static {
        /* initialize libavcodec, and register all codecs and formats */
        av_register_all();
        avformat_network_init();
    }

    public FFmpegFrameRecorder(File file, int audioChannels) {
        this(file, 0, 0, audioChannels);
    }
    public FFmpegFrameRecorder(String filename, int audioChannels) {
        this(filename, 0, 0, audioChannels);
    }
    public FFmpegFrameRecorder(File file, int imageWidth, int imageHeight) {
        this(file, imageWidth, imageHeight, 0);
    }
    public FFmpegFrameRecorder(String filename, int imageWidth, int imageHeight) {
        this(filename, imageWidth, imageHeight, 0);
    }
    public FFmpegFrameRecorder(File file, int imageWidth, int imageHeight, int audioChannels) {
        this(file.getAbsolutePath(), imageWidth, imageHeight);
    }
    public FFmpegFrameRecorder(String filename, int imageWidth, int imageHeight, int audioChannels) {
        this.filename      = filename;
        this.imageWidth    = imageWidth;
        this.imageHeight   = imageHeight;
        this.audioChannels = audioChannels;

        this.pixelFormat   = AV_PIX_FMT_NONE;
        this.videoCodec    = AV_CODEC_ID_NONE;
        this.videoBitrate  = 400000;
        this.frameRate     = 30;

        this.sampleFormat  = AV_SAMPLE_FMT_NONE;
        this.audioCodec    = AV_CODEC_ID_NONE;
        this.audioBitrate  = 64000;
        this.sampleRate    = 44100;

        this.interleaved = true;

        this.video_pkt = new AVPacket();
        this.audio_pkt = new AVPacket();
    }
    public void release() throws Exception {
        synchronized (com.googlecode.javacv.cpp.avcodec.class) {
            releaseUnsafe();
        }
    }
    public void releaseUnsafe() throws Exception {
        /* close each codec */
        if (video_c != null) {
            avcodec_close(video_c);
            video_c = null;
        }
        if (audio_c != null) {
            avcodec_close(audio_c);
            audio_c = null;
        }
        if (picture_buf != null) {
            av_free(picture_buf);
            picture_buf = null;
        }
        if (picture != null) {
            avcodec_free_frame(picture);
            picture = null;
        }
        if (tmp_picture != null) {
            avcodec_free_frame(tmp_picture);
            tmp_picture = null;
        }
        if (video_outbuf != null) {
            av_free(video_outbuf);
            video_outbuf = null;
        }
        if (frame != null) {
            avcodec_free_frame(frame);
            frame = null;
        }
        if (samples_out != null) {
            for (int i = 0; i < samples_out.length; i++) {
                av_free(samples_out[i].position(0));
            }
            samples_out = null;
        }
        if (audio_outbuf != null) {
            av_free(audio_outbuf);
            audio_outbuf = null;
        }
        video_st = null;
        audio_st = null;

        if (oc != null && !oc.isNull()) {
            if ((oformat.flags() & AVFMT_NOFILE) == 0) {
                /* close the output file */
                avio_close(oc.pb());
            }

            /* free the streams */
            int nb_streams = oc.nb_streams();
            for(int i = 0; i < nb_streams; i++) {
                av_free(oc.streams(i).codec());
                av_free(oc.streams(i));
            }

            /* free the stream */
            av_free(oc);
            oc = null;
        }

        if (img_convert_ctx != null) {
            sws_freeContext(img_convert_ctx);
            img_convert_ctx = null;
        }

        if (samples_convert_ctx != null) {
            swr_free(samples_convert_ctx);
            samples_convert_ctx = null;
        }
    }
    @Override protected void finalize() throws Throwable {
        super.finalize();
        release();
    }

    private String filename;
    private AVFrame picture, tmp_picture;
    private BytePointer picture_buf;
    private BytePointer video_outbuf;
    private int video_outbuf_size;
    private AVFrame frame;
    private Pointer[] samples_in;
    private BytePointer[] samples_out;
    private PointerPointer samples_in_ptr;
    private PointerPointer samples_out_ptr;
    private BytePointer audio_outbuf;
    private int audio_outbuf_size;
    private int audio_input_frame_size;
    private AVOutputFormat oformat;
    private AVFormatContext oc;
    private AVCodec video_codec, audio_
摄像头重启时,javacv 录制视频分段失效的问题可能是由于摄像头重启导致的连接中断或状态丢失引起的。 在录制视频过程中,如果摄像头突然断电或者重启,就会导致正在进行的录制操作被中断。这时候,javacv 可能无法正确处理这种中断,导致无法实现视频分段的功能。 另外,摄像头重启时,会导致摄像头的状态发生变化,可能会丢失某些参数设置或连接信息。如果 javacv 没有正确处理状态变化,就会导致无法继续录视频或者无法保存录制视频。这也会影响视频分段的功能。 要解决这个问题,可以尝试以下方法: 1. 检测摄像头的连接状态,如果连接断开,可以尝试重新建立连接。可以在摄像头重启后自动重连,避免录制中断。 2. 在摄像头重启后,重新配置摄像头的参数和状态,确保与 javacv录制参数一致。确保配置的参数正确,包括帧率、分辨率等设置。 3. 在进行视频分段时,需要检测录制视频文件是否已经达到预设的大小或时间长度,如果达到了,则进行分段并创建新的视频文件。 4. 如果出现错误或者中断,需要做好异常处理,确保错误能够及时捕获并处理,避免影响录制过程。 总之,要解决摄像头重启时 javacv 录制视频分段失效的问题,需要注意处理连接中断、状态丢失以及合理的异常处理,以确保录制过程能够稳定进行并实现视频分段的功能。
评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值