Das Extrahieren einer kurzen Videokopie erweist sich als ganz einfach

Nach zehn Jahren Ehe reist sie an den Westsee

       Zehn Meilen nach der Frühlingsbrise sind alle Hirten und der Weizen grün. Im Frühling fühlen sich die Menschen immer wohl, und dieser März ist besonders anders, weil ich seit zehn Jahren mit meiner Frau verheiratet bin. Die beiden nahmen sich einen luxuriösen Tag frei, versteckten ihre Kinder und besuchten den Westsee erneut, um den Eis am Stiel-Laden von vor 13 Jahren zu finden (sie kauften ihr das teuerste Eis – 8 Yuan – für ihre damalige Kollegin). ), und um die Eisdiele zu finden, die vor 13 Jahren verkauft wurde (sie schenkte mir einen Mungbohnen-Schlüsselanhänger – reine Freundschaft), setzte sich auf denselben Stuhl, auf dem ich vor 13 Jahren saß. Gerade als ich in romantische Erinnerungen versunken war, kam eine Person, die ich schon lange nicht mehr kontaktiert hatte, zu mir. Ein Freund von mir erhielt plötzlich die Nachricht, dass wir uns in Dazhuhai, Anji, treffen würden. Früher dachte ich, dass es sehr friedlich sei, Bambus vor und hinter meinem Haus in meiner Heimatstadt zu haben. Es stellte sich heraus, dass die Bambusse überall in den Bergen und Ebenen auch einen einzigartigen Geschmack haben. Eine Gruppe Kinder spielt Fußball auf dem Rasen. Die Kinder haben so viel Spaß.

Von nun an beginnt die Neugier

Während des Chats zeigte mir ein Freund ein kleines Programm namens Qingdou. Die Funktion zum Extrahieren von Videokopien hat mich angezogen. Kopieren Sie einfach einen Link zu einem kurzen Video wie Douyin oder Kuaishou, um die Videokopie zu extrahieren. Angetrieben von der Neugier begannen wir eine Entdeckungsreise. Ich hätte nie gedacht, dass es leicht sein würde, anzufangen, aber schwer, loszulassen.

Nach einigen einfachen Überlegungen wurde der allgemeine Prozess festgelegt, der in drei Schritte unterteilt ist:

Videodateien extrahieren -> Audiotrennung -> Audio in Text umwandeln. Dann begann ich glücklich zu programmieren. Die Realität traf mich bald hart, und sie erfüllte das alte Sichuan-Sprichwort, das mich seit 30 Jahren begleitet: Um es vorsichtig auszudrücken: Es ist wie ein Lichtstrahl (der Sichuan-Dialekt klingt interessant, wenn man ihn liest). Die erste Schwierigkeit besteht darin, Videos basierend auf freigegebenen Links herunterzuladen und verschiedene gängige Plattformen zu unterstützen. Nachdem ich es eine Weile versucht hatte, gab ich auf. Schließlich habe ich „das nicht tun wollen“. Schnittstelle eines Drittanbieters.

Mit dem Videolink ist es einfach, es lokal herunterzuladen (an einfachen Stellen kann es jedoch zu Fallstricken kommen), den Code direkt einzugeben und den von der Datei generierten InputStream zurückzugeben.

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;
    }

Verwenden Sie dann javacv, um das Audio zu trennen. Das getrennte Audio wird über FFmpegFrameRecorder erfasst. Laden Sie den Code auch direkt hoch.

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;
    }

Sturm vor Sonnenaufgang

Als ich das schrieb, dachte ich, der Sieg sei wie die rote Sonne, die im Osten unter den roten Wolken aufgehen würde. Ein Anwendungsfall war bereits perfekt, und der zweite Anwendungsfall war gerade perfekt bereitete sich auf eine Speech-to-Text-Phase vor, der letzte Einzeltest schlug fehl. Zu diesem Zweck begann eine langwierige Debugging-Runde.

1. HTTP-Datei herunterladen und speichern – Parsen fehlgeschlagen – avformat_find_stream_info()-Fehler: Stream-Informationen konnten nicht gefunden werden;

2. Der Browser konnte die Datei auch nicht speichern.

3. Das Herunterladen und Parsen von Thunder schlug ebenfalls fehl.

...

Ich begann zu vermuten, dass es ein Problem mit der von der Drittanbieter-Schnittstelle zurückgegebenen Videokodierung gibt. Als die von Douyin gespeicherte Datei erfolgreich analysiert wurde, wurde mein Verdacht weiter bestätigt. Aber die mit dem WeChat-Applet saveVideoToPhotosAlbum gespeicherten Dateien können erfolgreich geparst werden ... Ich begann an mir selbst zu zweifeln. Also begannen verschiedene Parameter nach dem Zufallsprinzip angepasst zu werden. Nach unzähligen Fehlschlägen kam ich auf eine mutige Idee. Wenn Sie das heruntergeladene JavaCV nicht analysieren können, können Sie jederzeit das heruntergeladene JavaCV analysieren. Sicher genug. Der obige Code hat eine Zeile geändert.


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

Der nächste Schritt besteht darin, die ars-Schnittstelle von Tencent Cloud basierend auf den extrahierten Audiodateien aufzurufen. Als ich zuvor die Schnittstelle von Openai zur Implementierung eines internen Finanzroboters verwendet habe, habe ich eine Schnittstelle zum Konvertieren von Spracheingaben in Text geschrieben, die ich einfach eingefügt habe, und es war in Ordnung. Die Ein-Satz-Schnittstelle wird wie folgt aufgerufen. Wenn es länger als eine Minute dauert, rufen Sie einfach die lange Sprachschnittstelle auf. (Hinweis: Die Ein-Satz-Schnittstelle kehrt synchron zurück und die lange Stimme ist ein asynchroner Rückruf.)

    /**
     * @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(), "语音转文字异常,请重试");
        }
    }

Ähnlich verhält es sich mit langen Rede-to-Text-Texten. Code wird wie folgt angezeigt

    /**
     * @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;
    }

Ende ohne Ende

Zuerst war ich nur neugierig auf die Textextraktion, aber ich hätte nie gedacht, dass ich nicht aufhören könnte, es zu schreiben, und es war ein bisschen bedauerlich, wenn es keine Frontend-Präsentation gab Meine Frau half mir beim Erstellen einer Benutzeroberfläche und ich erstellte ein einfaches kleines Programm ... nach einer Weile war es endlich online. Interessierte Schüler können den Code scannen, um ihn zu erleben.

Name des Miniprogramms: Text-to-Speech-Dienstprogramm;

Miniprogramm-QR-Code:  Es ist kein QR-Code erlaubt. Hier ist ein Link für eine schnelle Erfahrung

 

 

 

Linus hat es sich zur Aufgabe gemacht, zu verhindern, dass Kernel-Entwickler Tabulatoren durch Leerzeichen ersetzen. Sein Vater ist einer der wenigen Führungskräfte, die Code schreiben können, sein zweiter Sohn ist Direktor der Open-Source-Technologieabteilung und sein jüngster Sohn ist ein Open-Source-Core Mitwirkender : Natürliche Sprache wird immer weiter hinter Huawei zurückfallen: Es wird 1 Jahr dauern, bis 5.000 häufig verwendete mobile Anwendungen vollständig auf Hongmeng migriert sind Der Rich - Text-Editor Quill 2.0 wurde mit einer deutlich verbesserten Erfahrung von Ma Huateng und „ Meta Llama 3 “ veröffentlicht Quelle von Laoxiangji ist nicht der Code, die Gründe dafür sind sehr herzerwärmend. Google hat eine groß angelegte Umstrukturierung angekündigt
{{o.name}}
{{m.name}}

Ich denke du magst

Origin my.oschina.net/jijunjian/blog/11054025
Empfohlen
Rangfolge