FFmpeg入门详解之82:FFmpeg转码器Java版之ava编码

创建数据库:db_webavtc

创建数据表:avcategory(素材类别)

id int primary key,

pid int ,

cname varchar(255),

cmemo varchar(1000)

CREATE TABLE `avcategory` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`pid` INT(11) NOT NULL,

`cname` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`cmemo` VARCHAR(1000) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

PRIMARY KEY (`id`) USING BTREE

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

;

创建数据表:avtemplate(转码模板)

Id int primary key auto_increment,

tcTemplateId  varchar(64),

tcTemplateName varchar(64),

tcFormatType  int,//0:m3u8, 1:mp4, 2:flv

threadsNum int,//转码线程数

hlsSegmentTime int, //m3u8的切片长度(秒)

isMultiOutput int,

hasWaterMark int,

Priority int,

removeAV int,//去除视频(0),去除音频(1)

waterMark varchar(2048),//水印json串

tcTemplateDetails varchar(4096)//转码模板的json串

CREATE TABLE `avtemplate` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`tcTemplateId` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTemplateName` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTemplateEnglish` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcFormatType` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`isDeleted` INT(11) NULL DEFAULT '0',

`state` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`threadsNum` INT(11) NULL DEFAULT '0',

`hlsSegmentTime` INT(11) NULL DEFAULT '10',

`tcFastMode` INT(11) NULL DEFAULT '0',

`isMultiOutput` INT(11) NULL DEFAULT '0',

`tcOutputFileCount` INT(11) NULL DEFAULT '0',

`hasWaterMark` INT(11) NULL DEFAULT '0',

`priority` INT(11) NULL DEFAULT '0',

`waterMark` VARCHAR(2048) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTemplateDetails` VARCHAR(4096) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`createTime` DATETIME NULL DEFAULT NULL,

`removeAV` INT(11) NULL DEFAULT '0',

`isSystemReserved` INT(11) NULL DEFAULT '0',

PRIMARY KEY (`id`) USING BTREE,

INDEX `idx_by_name` (`tcTemplateName`) USING BTREE,

INDEX `idx_by_tcid` (`tcTemplateId`) USING BTREE

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

ROW_FORMAT=COMPACT

AUTO_INCREMENT=43

;

创建数据表:avassets(素材)

Id int primary key auto_increment,

avAssetsGuid varchar(255),

avSrcName  varchar(500),

avTitle  varchar(255),

avMakeFullName  varchar(500),

avType int,

avCategoryId int,

avState  int,

avTag   varchar(255),

avPoster  varchar(255),

tcTemplateId  varchar(64)

创建数据表:avtranscodetask(转码任务)

CREATE TABLE `avtranscodetask` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`tcTaskGuid` VARCHAR(128) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`avAssetsGuid` VARCHAR(128) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTaskOutDir` VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTaskOutName` VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcTaskState` INT(11) NULL DEFAULT NULL,

`tcTaskPercent` VARCHAR(128) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`tcConsumeTime` INT(11) NULL DEFAULT NULL,

`tcTranscoderIP` VARCHAR(128) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

PRIMARY KEY (`id`) USING BTREE

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

;

bean

创建AVCategory

创建AVTemplate

创建AVAssets

创建AVTranscodeTask

dao

AVCategoryDaoImpl

AVTemplateDaoImpl

AVAssetsDaoImpl

AVTrancodeTaskDaoImpl

service

AVCategoryService

AVCategoryServiceImpl

AVTemplateService

AVTemplateServiceImpl

AVAssetsService

AVAssetsServiceImpl

AVTrancodeTaskService

AVTrancodeTaskServiceImpl

action

AVCategoryAction

AVTemplateAction

稍微复杂,一点一点改造

AVAssetsAction

AVTrancodeTaskAction

UI

avcategoryList.jsp

avtemplateList.jsp

avassetsMother.jsp

文件上传的组件:webuploader.js

avassetsEdit.jsp:添加素材

avassetsEdit2.jsp: 编辑素材

avassetsAddNew.action

avassetsList.jsp:素材列表

avtranscodetaskList.jsp:转码列表

配置文件

Struts.xml:            action

applicationContext.xml:   *.hbm.xml,  beans

创建数据表: ctype

CREATE TABLE `ctype` (

`ctid` INT(11) NOT NULL AUTO_INCREMENT,

`ctname` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`ctcode` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`ctno` INT(11) NULL DEFAULT NULL,

`ctdesc` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',

`csCount` INT(11) NULL DEFAULT NULL,

`pid` INT(11) NULL DEFAULT NULL,

PRIMARY KEY (`ctid`) USING BTREE

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

AUTO_INCREMENT=14

;

db_webavtc

左侧导航树

ztree

Java调用ffmpeg进行转码

import java.io.BufferedInputStream;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

public class JavaFFmpeg extends Thread{

private boolean stoped = false;

public boolean isStoped() {

return stoped;

}

public void setStoped(boolean stoped) {

this.stoped = stoped;

}

public void run() {  

        // 在run方法中编写需要执行的操作  

stoped = false;

 openFFmpegExe();

 stoped = true;

}  

public static String readToString(String fileName) {

        String encoding = "UTF-8";

        File file = new File(fileName);

        Long filelength = file.length();

        byte[] filecontent = new byte[filelength.intValue()];

        try {

            FileInputStream in = new FileInputStream(file);

            in.read(filecontent);

            in.close();

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }

        try {

            return new String(filecontent, encoding);

        } catch (UnsupportedEncodingException e) {

            System.err.println("The OS does not support " + encoding);

            e.printStackTrace();

            return null;

        }

    }

public static void main(String[] args) {

System.out.println("hello,ffmpeg......");

        

// 在main方法(线程)中,创建线程对象,并启动线程.  

JavaFFmpeg objTC = new JavaFFmpeg();  

objTC.start();  

String strFileContent;

        while( !objTC.isStoped() ){

         try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

        

         strFileContent = readToString("d:/_movies/__test/ande_302.mp4.txt");

//        frame=。

         int nIdxFrameStart = strFileContent.lastIndexOf("frame=");

         if(nIdxFrameStart >= 0){

          int nIdxFrameEnd = strFileContent.indexOf((char)0x0A, nIdxFrameStart);

          if(nIdxFrameEnd > 0){

           String strFrames = strFileContent.substring(nIdxFrameStart, nIdxFrameEnd);

           System.out.println(strFrames);// 打印输出信息

          

           /// out_time=00:00:02.066576

              int nIdxOutTimeStart = strFileContent.indexOf("out_time=", nIdxFrameEnd);

              if(nIdxOutTimeStart > 0){

               int nIdxOutTimeEnd = strFileContent.indexOf((char)0x0A, nIdxOutTimeStart);

               if(nIdxOutTimeEnd > 0){

                   String strOutTime = strFileContent.substring(nIdxOutTimeStart, nIdxOutTimeEnd);

                   System.out.println(strOutTime);// 打印输出信息

                  }

              }

              

          }          

         }

        

        }

        

        

        System.out.println("bye,ffmpeg......");

    }

//调用其他的可执行文件,例如:自己制作的exe,或是 下载 安装的软件.

    private void openFFmpegExe() {

    Runtime rn = Runtime.getRuntime();

    Process p = null;

    try {

     ///ffmpeg.exe  -re  -i d:/_movies/ande10.mp4  -vcodec libx264  -acodec aac  -f flv  rtmp://192.168.0.104/hls1/test

     p = rn.exec("ffmpeg431.exe -progress d:/_movies/__test/ande_302.mp4.txt  -i d:/_movies/__test/ande_302.mp4 -threads 1  -vcodec libx264 -acodec aac  -y d:/_movies/__test/ande_302_test2.mp4" );

      

     BufferedInputStream in = new BufferedInputStream(p.getErrorStream());

         BufferedReader inBr = new BufferedReader(new InputStreamReader(in));

         String lineStr;

         System.out.println("开始转码");

         while((lineStr = inBr.readLine())!=null){

        

          //获得命令执行后在控制台的输出信息

          //System.err.println("获得命令执行后在控制台的输出信息");

          /System.out.println(lineStr);// 打印输出信息

         }

         

         // 检查命令是否执行失败。

         if(p.waitFor()!=0){

         if(p.exitValue()==1)//p.exitValue()==0表示正常结束,1:非正常结束

          System.err.println("命令执行失败!");

         }

         inBr.close();in.close();

         

         

    } catch (Exception e) {

     System.out.println("Error exec:" + e.getMessage());

    }

    }

}

spring定时器

spring定时器一般有两种:

TimerTask、Quartz。

1.定时执行任务的类继承TimerTask:

Java代码

public class EmailReportTask extends TimerTask{     

    @Override     

    public void run() {     

        System.out.println(" EmailReportTask Run... ");  

    }       

}  

2.spring的配置文件:

Xml代码

   <!--  Bean  -->   

<bean id="emailReportTask" class="com.fyl.spring.timertask.EmailReportTask" />  

  

<!-- ScheduledTimerTask设置定时器属性 : period=定时器周期;delay=延迟多久启动   

     86400000代表24个小时;timerTask=执行定时任务的类对象  -->   

<bean id="emailReportScheduleReportTask"   

    class="org.springframework.scheduling.timer.ScheduledTimerTask">     

    <property name="timerTask" ref="emailReportTask" />     

    <property name="period" value="2000" />     

    <property name="delay" value="1000" />     

</bean>   

  

    <!-- Spring的TimerFactoryBean负责启动定时任务;   

        scheduledTimerTasks = 需要启动的定时器任务的列表-->  

    <bean class="org.springframework.scheduling.timer.TimerFactoryBean">     

    <property name="scheduledTimerTasks">     

        <list>  

            <ref bean="emailReportScheduleReportTask"/>    

        </list>    

    </property>     

</bean>   

<!-- ........................................ -->

<!-- ........................................ -->

<!-- ...................spring.timer..................... -->

<bean id="tcTimerTaskScheduler001" class="com.fyl.ssh.timer.TCTimerTaskScheduler">

</bean>

<bean id="scheduled_tcTimerTaskScheduler001" class="org.springframework.scheduling.timer.ScheduledTimerTask">

<property name="delay" value="1000" />

<property name="period" value="5000" />

<property name="timerTask" ref="tcTimerTaskScheduler001" />

</bean>

<bean class="org.springframework.scheduling.timer.TimerFactoryBean">

<property name="scheduledTimerTasks">

<list>

<ref bean="scheduled_tcTimerTaskScheduler001" />

</list>

</property>

</bean>

转码任务调度

查询“素材表”:等待状态

1.avassets: 

avType:  0视频,1音频, 2图片

avState:  <=100等待,101:转码中, 200:转码完成, 500:转码失败

2.avassets:素材绑定模板 

3.avassets:查询对应模板的详细内容,根据tcTemplateGuid

AVAssetsAction

需要添加一个属性:AVTemplateService,需要在spring配置文件中配置

4.解析模板信息

(1)String------>Class

com.alibaba.fastjson.JSON

strAVTemplateString

Class obj = JSON.parseObject(strAVTemplateString, AVTemplateDetail.class);

(2)String--->JsonArray

JSONArray templateDetailJsonArray = new JSONArray();

templateDetailJsonArray =

JSONArray.parseArray(avtTmp.getTcTemplateDetails());

for(int i = 0; i < templateDetailJsonArray.size(); i++){

AVTemplateDetail avtDetail =

JSON.parseObject(templateDetailJsonArray.getString(i) , AVTemplateDetail.class);

System.out.println(":::"+ avtDetail.getvCodec() );

}

5.准备转码

(1)先在数据表中插入一条记录

定时器,每5秒钟,执行一次

avAssets: 检测 avState==101,转码中。

如果有,跳过本地timer;否则,继续开启新转码

(2)构造转码命令,开始转码,并更新转码进度

final public static String THE_TC_FFMPEG_CMDLINE_SINGLE =

"ffmpeg431  -i %s -vcodec %s -s %dx%d -acodec %s   -f %s -y  %s";

6.获取音视频素材的媒体基本信息

基本信息、视频信息、音频信息 ...

ffprobe431 -v quiet -print_format json -show_format

 -show_streams  D:\_movies\__test\ande_302_test2.mp4

ffprobe431 -i D:\_movies\__test\ande_302_test2.mp4

7.更新转码进度

(1)获取totalDuration

(2)获取实时转码进度,并转成百分比,更新数据库

(3)刷新转码列表页面

8.多码流输出

性能很高:只需要一次解码,同时进行多路编码

9.m3u8转码

切片命令行:

ffmpeg431.exe -i ande_302.mp4   

-vbsf  h264_mp4toannexb -vcodec libx264 -acodec aac

-strict -2 -s 640x480  -r 30  -g 150  -start_number 0  -hls_time 5  -hls_list_size

0   -threads 1   -f hls   -y aaa.m3u8

-vbsf  h264_mp4toannexb -vcodec libx264 -acodec aac

-strict -2 -s 960x540  -r 30  -g 150  -start_number 0  -hls_time 5  -hls_list_size

0   -threads 1   -f hls   -y aaa.m3u8

-vbsf  h264_mp4toannexb -vcodec libx264 -acodec aac

-strict -2 -s 1280x720  -r 30  -g 150  -start_number 0  -hls_time 5  -hls_list_size

0   -threads 1   -f hls   -y aaa.m3u8

10.添加水印(图片、文字)

ffmpeg431.exe -i ande_302.mp4 -vf "drawtext=fontfile=simhei.ttf:text='hello, 您 好ffmpeg':x=(mod(2*n\,w+tw)-tw):y=10:fontcolor=#FF6600:fontsize=60" -f mp4 -y

ande_test_text3.mp4

11.水印:中文乱码

加上盘符后的表达式为

这个郁闷头疼: 

原始语法:-vf drawtext="fontfile='d\:/_movies/__test/simhei.ttf':text=’xxx’"

Java语法:

String strXXX = “-vf drawtext=\"fontfile='d\\://_movies/__test/simhei.ttf’:text=’xxx’\"”;

Fontconfig error: Cannot load default config file

[Parsed_drawtext_0 @ 0102d840] Using "C:/WINDOWS/fonts/mingliub.ttc"

ffmpeg431  -i f:/fzavms1/fxhttp_out//__avsrc//2020/10/16/2020101614355828787ef9e22-0131-4777-b7a8-d063bc8d0b4b.mp4  -vf  "drawtext=fontfile='d\:/_movies/__test/simhei.ttf':text='284378sxxxdqqq啊啊啊':x=(mod(2*n\,w+tw)-tw):y=10:fontcolor=#FF6600:fontsize=60"  -vcodec libx264 -s 640x360 -acodec aac -threads  1  -f mp4 -y  f:/fzavms1/fxhttp_out//__tasks//2020/10/16/2020101614355828787ef9e22-0131-4777-b7a8-d063bc8d0b4b.mp4/first.mp4

ffmpeg431  -i f:/fzavms1/fxhttp_out//__avsrc//2020/10/16/2020101614355828787ef9e22-0131-4777-b7a8-d063bc8d0b4b.mp4  -vf  "drawtext=fontfile='d\:/_movies/__test/simhei.ttf':text='284378sxxxdqqq啊啊啊':x=(mod(2*n\,w+tw)-tw):y=10:fontcolor=#FF6600:fontsize=60"  -vcodec libx264 -s 640x360 -acodec aac -threads  1  -f mp4 -y  f:/fzavms1/fxhttp_out//__tasks//2020/10/16/2020101614355828787ef9e22-0131-4777-b7a8-d063bc8d0b4b.mp4/first.mp4

fastjson

String strWMJsonStr = JSON.toJSONString(objWaterMark, true);

WaterMark avtWaterMarkDetail = JSON.parseObject(strxxx , WaterMark.class);

猜你喜欢

转载自blog.csdn.net/teachermei/article/details/127315635