短视频文案提取的简单实现

       过春风十里,尽荠麦青青。春天总是让人舒坦,而今年的三月,也因为与媳妇结婚十年,显得格外不同。两人奢侈的请了一天假,瞒着孩子,重游西湖,去寻找13年前的冰棍店(给当时还是同事的她买了最贵的一个雪糕-8元),去寻找13年前卖红豆钥匙扣的大爷(她送我了一个绿豆的钥匙扣-纯洁的友谊),去坐一坐13年前坐过的那条凳子... 正当沉浸在浪漫的回忆中时,一个许久未曾联系的好友,突然来了消息,相约安吉大竹海。以前觉得老家的房前屋后都是竹子已是清幽之至,原来漫山遍野的竹子亦是别有一番风味。一群娃在草地上尽情的踢球,瞧,娃玩得多开心。

短视频文案提取的简单实现

 

 

闲聊之余,好友展示一个叫轻抖的小程序,里面一个视频文案提取的功能吸引了我。随便复制一条抖音,快手之类的短视频的链接就可以提取视频的文案。好奇心驱使之下,开始了一段探索之路。没曾想,开始容易,放下难。

经过一番简单的思索确定了大概流程,分三个步骤:

提取视频文件 -> 音频分离 -> 音频转文字。而后就兴高采烈的编码起来了。很快现实就给当头一棒,应验了那句伴随30年的四川老谚语:说得轻巧,是根灯草(四川话念来就有味儿了)。第一个难点就是:如何根据分享的链接下载视频,还能支持各种通用平台。尝试好一会儿后放弃了,毕竟”志不在此“嘛,后来偶然发现有不少这样的平台,专门提供根据url 下载视频的接口,就直接用三方的接口了。

有了视频链接,下载到本地就简单了(然则,简单的地方可能会有坑),直接上代码,返回文件生成的InputStream。

public InputStream run(MediaDownloadReq req) {         //根据url获取视频流         InputStream videoInputStream = null;         try {             String newName = "video-"+String.format("%s-%s", System.currentTimeMillis(), UUID.randomUUID().toString())+"."+req.getTargetFileSuffix();              File folder = new File(tempPath);             if (!folder.exists()) {                 folder.mkdir();             }             File file = HttpUtil.downloadFileFromUrl(req.getUrl(), new File(tempPath +"" + newName+""), new StreamProgress() {                 // 开始下载                 @Override                 public void start() {                     log.info("Start download file...");                 }                 // 每隔 10% 记录一次日志                 @Override                 public void progress(long total) {                     //log.info("Download file progress: {} ", total);                 }                 @Override                 public void finish() {                     log.info("Download file success!");                 }             });             videoInputStream = new FileInputStream(file);             file.delete();         } catch (Exception e) {             log.error("获取视频流失败  req ={}", req.getUrl(), e);             throw new BusinessException(ErrorCodeEnum.DOWNLOAD_VIDEO_ERROR.code(), "获取视频流失败");         }         return videoInputStream;     }

短视频文案提取的简单实现

然后使用javacv 分离音频,这个没什么特别的地方, 通过FFmpegFrameRecorder 搜集分离的音频。也直接上代码。

public ExtractAudioRes run(ExtractAudioReq req)  throws Exception {          long current = System.currentTimeMillis();         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();          //音频记录器,extractAudio:表示文件路径,2:表示两声道         FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, 2);          recorder.setAudioOption("crf", "0");         recorder.setAudioQuality(0);         //比特率         recorder.setAudioBitrate(256000);         //采样率         //recorder.setSampleRate(16000);         recorder.setSampleRate(8000);         recorder.setFormat(req.getAudioFormat());         //音频编解码         recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);         //开始记录         recorder.start();              //读取视频信息          FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(req.getVideoInputStream());         grabber.setSampleRate(8000);         //FFmpegLogCallback.set(); 调试日志         // 设置采集器构造超时时间(单位微秒,1秒=1000000微秒)         grabber.setOption("stimeout", String.valueOf(TimeUnit.MINUTES.toMicros(30L)));         grabber.start();         recorder.setAudioChannels(grabber.getAudioChannels());         Frame f;         Long audioTime = grabber.getLengthInTime() / 1000/ 1000;         current = System.currentTimeMillis();         //获取音频样本,并且用recorder记录         while ((f = grabber.grabSamples()) != null) {             recorder.record(f);         }         grabber.stop();         recorder.close();          ExtractAudioRes extractAudioRes = new ExtractAudioRes(outputStream.toByteArray(),  audioTime, outputStream.size() /1024);         extractAudioRes.setFormat(req.getAudioFormat());          return extractAudioRes;     }

短视频文案提取的简单实现

写到这里时,我以为胜利就如东方红霞之下呼之欲出的红日,已然无限接近,测试一个用例完美,二个用例完美,正当准备进行一个语音转文字的阶段时,最后一个单测失败。为此,开始了一轮旷日持久的调试路。

1, http下载保存文件-解析失败- avformat_find_stream_info() error : Could not find stream information;

2.浏览器保存文件也失败;

3, 迅雷下载解析也失败;

...

我已经开始怀疑三方接口返回的视频编码有问题了;当抖音保存文件解析成功时,更加印证了我的怀疑。但是使用微信小程序 saveVideoToPhotosAlbum 保存的文件居然可以解析成功...我开始怀疑自己了。于是各种参数开始胡乱一通调整。失败了无数次后,有了一个大胆的想法,我下载的你不能解析,那javaCV你自己下载的你总能解析了吧。 果然如此。上面的代码就修改了一行。

 //FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(req.getVideoInputStream()); // 直接传url  FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(req.getUrl());

短视频文案提取的简单实现

接下来就是根据提取的音频文件,调用腾讯云的ars 接口。之前使用Openai 的接口实现内部财务机器人时,有写过通过语音输入转文字的接口,直接拿过来放上就ok了。 一句话接口调用如下,如果是超过一分钟的,调用长语音接口就可以了。(注:一句话接口同步返回,长语音是异步回调)

    /**      * @param audioRecognitionReq      * @description: 语音转文字      * @author: jijunjian      * @date: 11/21/23 09:48      * @param: [bytes]      * @return: java.lang.String      */     @Override     public String run(AudioRecognitionReq audioRecognitionReq) {          log.info("一句话语音语音转文字开始");         AsrClient client = new AsrClient(cred,  "");         SentenceRecognitionRequest req = new SentenceRecognitionRequest();         req.setSourceType(1L);         req.setVoiceFormat(audioRecognitionReq.getFormat());         req.setEngSerViceType("16k_zh");         String base64Encrypted = BaseEncoding.base64().encode(audioRecognitionReq.getBytes());         req.setData(base64Encrypted);         req.setDataLen(Integer.valueOf(audioRecognitionReq.getBytes().length).longValue());          String text = "";         try {             SentenceRecognitionResponse resp = client.SentenceRecognition(req);             log.info("语音转文字结果:{}", JSONUtil.toJsonStr(resp));             text = resp.getResult();             if (Strings.isNotBlank(text)){                 return text;             }             return "无内容";         } catch (TencentCloudSDKException e) {             log.error("语音转文字失败:{}",e);             throw new BusinessException(AUDIO_RECOGNIZE_ERROR.code(), "语音转文字异常,请重试");         }     }

短视频文案提取的简单实现

长语音转文本也差不多。代码如下

    /**      * @param audioRecognitionReq      * @description: 语音转文字      * @author: jijunjian      * @date: 11/21/23 09:48      * @param: [bytes]      * @return: java.lang.String      */     @Override     public String run(AudioRecognitionReq audioRecognitionReq) {          log.info("极速语音转文字开始");         Credential credential = Credential.builder().secretId(AppConstant.Tencent.asrSecretId).secretKey(AppConstant.Tencent.asrSecretKey).build();         String text = "";         try {              FlashRecognizer recognizer = SpeechClient.newFlashRecognizer(AppConstant.Tencent.arsAppId, credential);             byte[] data = null;             if (audioRecognitionReq.getBytes() != null){                 data = audioRecognitionReq.getBytes();             }else {                 //根据文件路径获取识别语音数据 以后再实现             }              //传入识别语音数据同步获取结果             FlashRecognitionRequest recognitionRequest = FlashRecognitionRequest.initialize();             recognitionRequest.setEngineType("16k_zh");             recognitionRequest.setFirstChannelOnly(1);             recognitionRequest.setVoiceFormat(audioRecognitionReq.getFormat());             recognitionRequest.setSpeakerDiarization(0);             recognitionRequest.setFilterDirty(0);             recognitionRequest.setFilterModal(0);             recognitionRequest.setFilterPunc(0);             recognitionRequest.setConvertNumMode(1);             recognitionRequest.setWordInfo(1);             FlashRecognitionResponse response = recognizer.recognize(recognitionRequest, data);               if (SuccessCode.equals(response.getCode())){                 text = response.getFlashResult().get(0).getText();                 return text;             }             log.info("极速语音转文字失败:{}", JSONUtil.toJsonStr(response));             throw new BusinessException(AUDIO_RECOGNIZE_ERROR.code(), "极速语音转换失败,请重试");         } catch (Exception e) {             log.error("语音转文字失败:{}",e);             throw new BusinessException(AUDIO_RECOGNIZE_ERROR.code(), "极速语音转文字异常,请重试");         }     }      /**      * @param req      * @description: filter 根据参数选      * @author: jijunjian      * @date: 3/3/24 18:54      * @param:      * @return:      */     @Override     public Boolean filter(AudioRecognitionReq req) {         if (req.getAudioTime() == null || req.getAudioTime() >= AppConstant.Tencent.Max_Audio_Len || req.getAudioSize() >= AppConstant.Tencent.Max_Audio_Size){             return true;         }         return false;     }

短视频文案提取的简单实现

一开始只是凭着对文案提取好奇,没曾想,一写就停不下来;后端实现了,如果没有一个前端的呈现又感觉略有遗憾;于是又让媳妇帮忙搞了一套UI;又搞了一个简单的小程序...一顿操作之后,终于上线了。有兴趣的同学可以扫码体验下。

小程序名称 :智能配音实用工具;

小程序二维码 : 

短视频文案提取的简单实现

 

 

 

 

 

发表评论

评论已关闭。

相关文章

当前内容话题
  • 0