美文网首页
react-native-ffmpeg 将视频流保存为mp4

react-native-ffmpeg 将视频流保存为mp4

作者: 物联白菜 | 来源:发表于2020-12-07 16:38 被阅读0次

    https://www.npmjs.com/package/react-native-ffmpeg 注意这个库需要 IOS 更高的版本,可以到此修改,IOS 9.0的新版本不支持

    图片.png

    官网有很多包名,可以指定自己想要的包

    图片.png
    Android:
    安装
    1、yarn add react-native-ffmpeg
    react-native link react-native-ffmpeg

    2、添加权限
    <uses-sdk tools:overrideLibrary="com.arthenica.reactnative,com.arthenica.mobileffmpeg"/>


    图片.png

    3、android/build.gradle指定包名
    ext {
    reactNativeFFmpegPackage = "video"
    }


    图片.png

    IOS:
    安装
    1、yarn add react-native-ffmpeg (有安装过的不用再执行)
    2、在ios/Podfile中指定要安装的包为video(注意IOS的版本,不然会报错)

    pod 'react-native-ffmpeg/video', :podspec => '../node_modules/react-native-ffmpeg/react-native-ffmpeg.podspec'
    

    3、添加完之后进入cd ios =>pod install 或npx pod-install

    开始使用

    
    
    import {LogLevel, RNFFmpeg} from 'react-native-ffmpeg';
    
        //保存视频
        _saveVideo(){
            this.setState({
                isSave:true
            })
    
            let { saveVideo } = this.state
            console.warn('客户端发送保存视频指令===',saveVideo)
    
            clientFn.clientWrite(saveVideo,'保存视频0xB0')
    
            // Toast.show('正在保存视频',{position:Toast.positions.CENTER})
    
    
            var path = ''
            if (Platform.OS === 'android') {
                path = 'sdcard';
            }else{
                path = RNFS.LibraryDirectoryPath;
            }
    
    
            RNFS.exists('/sdcard/JGDYvideo').then((isExsit)=>{
                exchangeBasic.log('目录是否存在',isExsit)
                if(isExsit){
                    let now = moment().format('X')
                    RNFFmpeg.execute(`-i rtmp://58.200.131.2:1935/livetv/hunantv -acodec copy -vcodec copy -f mp4 /${path}/JGDYvideo/${this.props.deviceInfo[2]}_${now}.mp4`)
                        .then(
                            result => exchangeBasic.log("FFmpeg process exited with rc " + result.rc)
                        );
                }else{
                    RNFS.mkdir('/sdcard/JGDYvideo').then((ss)=>{
                        exchangeBasic.log('创建文件夹JGDYvideo成功,成功后保存视频',ss)
    
                        let now = moment().format('X')
                        RNFFmpeg.execute(`-i rtmp://58.200.131.2:1935/livetv/hunantv -acodec copy -vcodec copy -f mp4 /${path}/JGDYvideo/${this.props.deviceInfo[2]}_${now}.mp4`)
                            .then(
                                result => exchangeBasic.log("FFmpeg process exited with rc " + result.rc)
                            );
                    }).catch((ee)=>{
                        alert('保存失败',ee)
                    })
    
                }
            }).catch((e)=>{
                exchangeBasic.log('不存在这个目录',e)
                alert('手机不存在JGDYvideo这个目录')
            })
    
        }
    
    

    下面附上整个js页面的代码,因为后面又不需要这个库了,所以在里面我注释了,重新打开注释即可,RNFFmpeg.cancel() 为停止保存视频

    /*
    *开始打印
    * */
    import React, {Component} from 'react';
    import {
        View,
        Text,
        StyleSheet,
        Image,
        TouchableOpacity,
        ScrollView,
        TextInput,
        ImageBackground,
        Alert,
        PanResponder, NativeModules, NativeEventEmitter
    } from 'react-native'
    import Util from '../common/util'
    import Header from '../common/header'
    import Progress from '../common/progressBar'
    import storage from "../common/storage";
    import exchangeBasic from "../common/exchangeBasic";
    import clientFn from "../common/client";
    // import {LogLevel, RNFFmpeg} from 'react-native-ffmpeg';
    import Loading1 from "../common/loading";
    import Toast from "react-native-root-toast";
    import {RtmpView} from "react-native-rtmpview";
    
    import RNFS from "react-native-fs";
    import {connect} from "react-redux";
    import Orientation from "react-native-orientation";
    import {VlCPlayerView} from "react-native-vlc-media-player";
    
    const moment = require('moment');
    
    class printing extends Component {
        constructor(props) {
            super(props);
            this.player = null;
            this.vlcPlayer = React.createRef()
            this.state = {
                progress: 0,
                indeterminate: true,
                animated: false,
                username:'',
                password:'',
                startPrinting:'',
                stopPrinting:'',
                saveVideo:'',
                stopVideo:'',
                statusText:'开始打印',
                printingStatus:'',
                deviceName:'',
                isLoading:true,
                num:1,
                isSave:false
            };
    
            // const RNRtmpEventManager =
            //     NativeModules.RNRtmpEventManager;
            //
            // if (!(typeof RNRtmpEventManager === "undefined")) {
            //     const RNRtmpEventManager = new NativeEventEmitter(
            //         NativeModules.RNRtmpEventManager
            //     );
            //
            //     RNRtmpEventManager.addListener(
            //         "RNRtmpEvent",
            //         (data) => this.handleRNRtmpEvent(data)
            //     );
            //
            // }
    
    
        }
    
         componentDidMount() {
             // Orientation.lockToPortrait();
            this.getCurrentTime()
    
            let UserID = this.props.UserID
            let instruction = []
            let header = [exchangeBasic.tenTurntoHex(85)]
            let zero = exchangeBasic.tenTurntoHex(0)
            let chartLength = exchangeBasic.tenTurntoHex(1)
            let instructions1 = [exchangeBasic.tenTurntoHex(160)]   //设备状态0xa0
            let instructions = [exchangeBasic.tenTurntoHex(161)]   //查询打印状态0xa1
            let instructions2 = [exchangeBasic.tenTurntoHex(163)]   //开始打印0xa3
            let instructions3 = [exchangeBasic.tenTurntoHex(164)]     // 停止打印0xa4
            // let instructions4 = [exchangeBasic.tenTurntoHex(164)]     // 保存视频0xb0
            let instructions4 = [0xB0]     // 保存视频0xb0
            let instructions5 = [0xB1]     // 保存视频0xb0
            let check = [exchangeBasic.tenTurntoHex(226)]
            let end = [exchangeBasic.tenTurntoHex(170)]
    
            // <0x55><UserID><0xA2><CHK><0xAA> 预览打印
    
            let data1 = instruction.concat(header,UserID,zero,chartLength,instructions1,check,end)
            let deviceStatus = clientFn.clientWrite1(data1)
            let data2 = instruction.concat(header,UserID,zero,chartLength,instructions,check,end)
            let printingStatus = clientFn.clientWrite1(data2)
            let data3 = instruction.concat(header,UserID,zero,chartLength,instructions2,check,end)
            let startPrinting = clientFn.clientWrite1(data3)
            let data4 = instruction.concat(header,UserID,zero,chartLength,instructions3,check,end)
            let stopPrinting = clientFn.clientWrite1(data4)
            let data5 = instruction.concat(header,UserID,zero,chartLength,instructions4,check,end)
            let saveVideo = clientFn.clientWrite1(data5)
            let data6 = instruction.concat(header,UserID,zero,chartLength,instructions5,check,end)
            let stopVideo = clientFn.clientWrite1(data6)
            this.setState({
                printingStatus:printingStatus,
                startPrinting:startPrinting,
                stopPrinting:stopPrinting,
                saveVideo:saveVideo,
                stopVideo:stopVideo,
            })
    
            //     write([0x55, 0x07, 0xe4,0x00,0x01,0x20,0x00,0xAA])
            clientFn.clientWrite(deviceStatus,'设备状态0xa0')
    
        }
    
        componentWillUnmount() {
            // this.player.pause()
            this.timer && clearInterval(this.timer)
            this.timerSend && clearInterval(this.timerSend)
        }
    
        getCurrentTime() {
            let timestamp = new Date().getTime()
            let time = Util.transformToDate(timestamp)
            this.setState({
                time: time
            })
            this.timerInterval()
        }
    
        timerInterval() {
            let that = this
            this.timer = setInterval(function () {
                let timestamp = new Date().getTime()
                let time = Util.transformToDate(timestamp)
                that.setState({
                    time: time
                })
            }, 1000)
        }
    
    
        //每秒发送一次
        everySecond(printingStatus,stopVideo){
             storage.remove({
                 key: 'resData',
             });
    
              this.timerSend = setInterval(()=>{
                clientFn.clientWrite(printingStatus,'查询打印状态0xa1')
                storage.load({
                    key: 'resData',
                    autoSync: true,
                    syncInBackground: true,
                    syncParams: {
                        extraFetchOptions: {
                        },
                        someFlag: true
                    }
                }).then(res => {
                    exchangeBasic.log("读取到resData的缓存",res,res[8]/100);
                    let isSave = this.state.isSave
    
                    let progress = this.state.progress
                    // progress += 0.05;
                    if (progress < 1) {
                        this.setState({
                            progress: res[8]/100
                        })
                    } else {
                        this.setState({
                            animated: false,
                            progress: 1,
                            num:5,
                            isSave:false,
                            statusText:'重新打印'
                        })
    
                        if(isSave){
                            clientFn.clientWrite(stopVideo,'停止保存视频0xB1')
                            // RNFFmpeg.cancel();   //打印完毕自动保存视屏
                        }
    
    
                        Toast.show('打印完成',{
                            position: Toast.positions.CENTER,
                            backgroundColor:'#ccc',
                            textColor:'#000'
                        })
    
                        this.timerSend && clearInterval(this.timerSend)
                    }
    
                })
                    .catch(err => {
                        exchangeBasic.log(err.message);
                        switch (err.name) {
                            case 'NotFoundError':
                                break;
                            case 'ExpiredError':
                                break;
                        }
                    });
    
    
            },1000)
        }
    
    
    
        startPrint() {
    
            let {startPrinting,stopPrinting,animated,printingStatus,isSave,stopVideo} = this.state
    
            // console.warn('write to sever=================',printingStatus)
    
            let that = this
            this.timerSend && clearInterval(this.timerSend)
    
            //停止打印
            if(animated){
                let that = this
                that.timer && clearInterval(that.timer)
                that.timerSend && clearInterval(that.timerSend)
    
                that.setState({
                    statusText:'开始打印',
                    animated: false,
                    isSave:false
                })
    
                if(isSave){
                    clientFn.clientWrite(stopVideo,'停止保存视频0xB1')
                    // RNFFmpeg.cancel();
                }
    
                clientFn.clientWrite(stopPrinting,'停止打印0xa4')
    
            }
    
            //开始打印
            if(!animated){
                that.setState({
                    animated: true,
                    progress: 0,
                    statusText:'停止打印',
                })
    
    
                clientFn.clientWrite(startPrinting,'开始打印0xa3')
    
                this.everySecond(printingStatus,stopVideo)
    
            }
        }
    
        //保存视频
        _saveVideo(){
            this.setState({
                isSave:true
            })
    
            let { saveVideo } = this.state
            console.warn('客户端发送保存视频指令===',saveVideo)
    
            clientFn.clientWrite(saveVideo,'保存视频0xB0')
    
            // Toast.show('正在保存视频',{position:Toast.positions.CENTER})
    
    
            // var path = ''
            // if (Platform.OS === 'android') {
            //     path = 'sdcard';
            // }else{
            //     path = RNFS.LibraryDirectoryPath;
            // }
            //
            //
            // RNFS.exists('/sdcard/JGDYvideo').then((isExsit)=>{
            //     exchangeBasic.log('目录是否存在',isExsit)
            //     if(isExsit){
            //         let now = moment().format('X')
            //         RNFFmpeg.execute(`-i rtmp://58.200.131.2:1935/livetv/hunantv -acodec copy -vcodec copy -f mp4 /${path}/JGDYvideo/${this.props.deviceInfo[2]}_${now}.mp4`)
            //             .then(
            //                 result => exchangeBasic.log("FFmpeg process exited with rc " + result.rc)
            //             );
            //     }else{
            //         RNFS.mkdir('/sdcard/JGDYvideo').then((ss)=>{
            //             exchangeBasic.log('创建文件夹JGDYvideo成功,成功后保存视频',ss)
            //
            //             let now = moment().format('X')
            //             RNFFmpeg.execute(`-i rtmp://58.200.131.2:1935/livetv/hunantv -acodec copy -vcodec copy -f mp4 /${path}/JGDYvideo/${this.props.deviceInfo[2]}_${now}.mp4`)
            //                 .then(
            //                     result => exchangeBasic.log("FFmpeg process exited with rc " + result.rc)
            //                 );
            //         }).catch((ee)=>{
            //             alert('保存失败',ee)
            //         })
            //
            //     }
            // }).catch((e)=>{
            //     exchangeBasic.log('不存在这个目录',e)
            //     alert('手机不存在JGDYvideo这个目录')
            // })
    
    
    
        }
    
    
        render() {
            let progress = this.state.progress
            let isSave = this.state.isSave
            let left = progress * (Util.size.width - 80) - 25
            return (
                <View style={{flex: 1, backgroundColor: "#fff"}}>
                    <Header navigation={this.props.navigation} bgColor={"#fe2500"} fontColor={'#fff'} title={'打印'} num={this.state.num}/>
                    <View style={{padding: 15, flex: 1, justifyContent: 'space-between'}}>
                        <View style={{flex: 1, alignItems: 'center'}}>
                            <View style={styles.monitor_view}>
    
                                <Loading1 isLoading={this.state.isLoading} loadingText={'直播加载中…'}/>
    
                                {/*zhibo shikou */}
    
                                <VlCPlayerView
                                    autoplay={true}               //视屏播放结束时调用this.vlcPlayer.resume(false)方法 这个参数需要true
                                    ref={ref => (this.vlcPlayer = ref)}
                                    url={`rtmp://${this.props.deviceInfo[0]}:2022/live`}           //视频url
                                    Orientation={Orientation}
                                    //BackHandle={BackHandle}
                                    ggUrl=""                      // 广告url
                                    showGG={false}                 // 是否显示广告
                                    showTitle={true}              // 是否显示标题
                                    title=""                      // 标题
                                    showBack={false}               // 是否显示返回按钮
                                    paused={false}
                                    onLeftPress={()=>{}}          // 返回按钮点击事件
                                    // onOpen={this.onOpen.bind(this)}
                                    // onPlaying={this.onPlaying.bind(this)}
                                    // onProgress={this.onProgress.bind(this)}
                                    // onPaused={this.onPaused.bind(this)}
                                    // onStopped={this.onStopped.bind(this)}
                                    // onIsPlaying={this.onIsPlaying.bind(this)}
                                    // onBuffering={this.onBuffering.bind(this)}
                                    // onEnded={this.onEnded.bind(this)}
                                    // onError={this.onError.bind(this)}
                                    startFullScreen={() => {
                                        this.setState({
                                            isFull: true,
                                        });
                                    }}
                                    closeFullScreen={() => {
                                        this.setState({
                                            isFull: false,
                                        });
                                    }}
                                />
    
    
                                {/*<RtmpView*/}
                                {/*    style={styles.player}*/}
                                {/*    shouldMute={false}*/}
                                {/*    playOnResume={false}*/}
                                {/*    pauseOnStop={true}*/}
                                {/*    onLoadState={(data) => {*/}
                                {/*        // this.handleLoadState(data);*/}
                                {/*        exchangeBasic.log("直播加载视频",data)*/}
                                {/*        exchangeBasic.log(*/}
                                {/*            "React Native Received LoadState " + data.nativeEvent["state"]*/}
                                {/*        );*/}
                                {/*    }}*/}
    
                                {/*    onFirstVideoFrameRendered={(data) => {*/}
                                {/*        this.setState({*/}
                                {/*            isLoading:false*/}
                                {/*        })*/}
                                {/*    }}*/}
    
                                {/*    ref={e => { this.player = e; }}*/}
                                {/*    url={`rtmp://${this.props.deviceInfo[0]}:2022/live`} />*/}
    
                                <View style={styles.txt_fixed}>
                                    {/*<Text style={{fontSize: 12, color: "#fff"}}>局域网在线</Text>*/}
                                    <Text style={{fontSize:12,color:"#fff"}}>{`直播地址为:rtmp://${this.props.deviceInfo[0]}:2022/live`}</Text>
                                    <Text style={{fontSize: 12, color: "#fff"}}>{this.state.time}</Text>
                                </View>
                            </View>
    
                            {
                                isSave?
                                <View style={{flex:1}}>
                                    {Util.loading('正在保存视频…')}
                                </View>
                                    :
                                <View style={{paddingVertical: 50}}>
                                    <Text style={{fontSize: 14, color: "#333"}}>准备打印</Text>
                                </View>
    
    
                            }
                            <View style={styles.radio_view}>
                                <View style={[styles.percent_view, {left: left}]}>
                                    <View style={styles.txt_value}>
                                        <Text style={{
                                            fontSize: 12,
                                            color: "#fff",
                                            fontWeight: 'bold'
                                        }}>{Math.round(this.state.progress * 100)}%</Text>
                                    </View>
                                    <Image
                                        source={require('../images/triangle.png')}
                                        resizeMode={'cover'}
                                        style={{width: 7.5, height: 5}}
                                    />
                                </View>
                                <Progress
                                    animated={false}
                                    width={Util.size.width - 80}
                                    height={10}
                                    borderRadius={5}
                                    progress={this.state.progress}
                                    borderWidth={0}
                                    unfilledColor={"#eeeeee"}
                                />
                            </View>
    
    
                            <View style={styles.opera_view}>
                                <View style={styles.opera_item}>
                                    <TouchableOpacity
                                        onPress={() => this.startPrint()}>
                                        <Image
                                            source={require('../images/start_print.png')}
                                            resizeMode={'cover'}
                                            style={{width: 70, height: 70}}
                                        />
                                        <Text style={{fontSize: 14, color: "#696969"}}>{this.state.statusText}</Text>
                                    </TouchableOpacity>
                                </View>
    
                                <View style={styles.opera_item}>
                                    <TouchableOpacity
                                        onPress={() => this._saveVideo()}>
                                        <Image
                                            source={require('../images/save_video.png')}
                                            resizeMode={'cover'}
                                            style={{width: 70, height: 70}}
                                        />
                                        <Text style={{fontSize: 14, color: "#696969"}}>保存视频</Text>
                                    </TouchableOpacity>
                                </View>
    
                            </View>
                        </View>
    
                    </View>
                </View>
            )
        }
    }
    var styles = StyleSheet.create({
        player: {
            position:'absolute',
            width: '100%',
            height: '100%',
        },
        monitor_view: {
            width: Util.size.width - 30,
            height: 300,
            borderRadius: 10,
            // backgroundColor: "#353535",
            marginBottom: 15
        },
        txt_fixed: {
            position: 'absolute',
            top: 15,
            width: Util.size.width - 30,
            paddingHorizontal: 15,
            // flexDirection: 'row',
            alignItems: 'center',
            justifyContent: "space-between"
        },
        opera_view: {
            backgroundColor: "#fff",
            borderRadius: 10,
            flexDirection: "row",
            justifyContent: 'space-around',
            alignItems: 'flex-start',
            paddingBottom: 40,
        },
        btn_style: {
            width: 100,
            height: 35,
            borderRadius: 5,
            borderWidth: 1,
            borderColor: "#e0e0e0",
            alignItems: "center",
            justifyContent: 'center'
        },
        btn_style2: {
            width: 100,
            height: 35,
            borderRadius: 5,
            borderWidth: 1,
            borderColor: "#fe2500",
            backgroundColor: "#fe2500",
            alignItems: "center",
            justifyContent: 'center'
        },
        radio_view: {
            // paddingVertical: 30,
            alignItems: 'center',
            justifyContent: 'center',
            width: Util.size.width - 80,
            paddingBottom: 20,
        },
        percent_view: {
            width: 50,
            height: 30,
            alignItems: 'center',
            justifyContent: 'center',
            top: -30,
            position: 'absolute'
        },
        txt_value: {
            width: 50,
            height: 25,
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: "#fe2500",
            borderRadius: 3
        },
        opera_item: {
            flex: 1,
            alignItems: 'center',
            justifyContent: 'center',
        }
    })
    
    const mapStateToStore = (state) => {
        return {
            sever_DataArr: state.sever_DataArr,
            isLoading: state.isLoading,
            UserID: state.UserID,
            deviceInfo: state.deviceInfo,
            usernameText: state.usernameText,
            passwordText: state.passwordText,
        }
    }
    
    const dispatchToProps = (dispatch) => {
        return {
            login() {
                const action = {
                    type: "sever_DataArr",
                    sever_DataArr: [1,2,3,4]
                }
                dispatch(action);
            },
            handlePutIntodate() {
                const action = {
                    type: "add_num",
                    value: 1
                }
                dispatch(action);
            }
        }
    }
    
    export default connect(mapStateToStore, dispatchToProps)(printing)
    

    相关文章

      网友评论

          本文标题:react-native-ffmpeg 将视频流保存为mp4

          本文链接:https://www.haomeiwen.com/subject/yprtgktx.html