首页 > 编程语言 >【HarmonyOS】实现从视频提取音频并保存到pcm文件功能(API6 Java)

【HarmonyOS】实现从视频提取音频并保存到pcm文件功能(API6 Java)

时间:2023-07-25 14:22:59浏览次数:30  
标签:extractor Java format System HarmonyOS pcm println bufferInfo out

 【关键字】

视频提取类Extractor、视频编解码、保存pcm文件

 

【写在前面】

在使用API6开发HarmonyOS应用时,通常会开发一些音视频媒体功能,这里介绍如何从视频中提取音频保存到pcm文件功能,生成pcm音频文件后,就可使用音频播放类AudioRenderer进行播放了。这里主要介绍从视频提取音频并保存到pcm文件的开发步骤。

 

【开发步骤】

步骤1:对视频格式的文件进行提取音频文件,并通过解码器解码并监听获取到的buffer数据;直接使用Extractor从视频中提取出来的音频数据不能直接作为类似pcm数据源进行播放,需要使用解码器解码之后得到的原始数据才可AudioRenderer进行播放。新建VideoDecoder类,在里面封装相关功能代码。使用Extractor从视频提取音频数据并使用解码器解码,代码如下:

// 可创建VideoDecoder类,实现相关功能           
  private Format format;
  private Codec decoder;
  private Extractor extractor;

  public void createDecoder() {
      decoder = Codec.createDecoder(); // 创建解码器
      extractor = new Extractor(); // 创建Extractor解封装类
      boolean ret = extractor.setSource(new Source("/data/data/com.harmonyospro.myapplication/vedio_audio_test.mp4")); // 设置数据源,com.harmonyospro.myapplication为应用包名;也可设置为网络视频数据源
      System.out.println("setSource ret = " + ret);
      int trackCount = extractor.getTotalStreams();//获取轨道
      for (int i = 0; i < trackCount; i++) {
          format = extractor.getStreamFormat(i);
          if (format.getStringValue("mime").contains("audio")) { // 视频video,audio音频
              /**
               * @tc.steps: step2.set codec format for decoder
               * @tc.expected: step2.the return value is true
               */
              ret = decoder.setCodecFormat(format);
              System.out.println("setCodecFormat ret = " + ret);
              ret = extractor.specifyStream(i);
              System.out.println("specifyStream ret = " + ret);
              System.out.println("format.toString() = " + format.toString());
              System.out.println("format.getStringValue(mine) = "+format.getStringValue("mime"));
              System.out.println("format.getStringValue(width) = "+format.getIntValue("width"));
              System.out.println("format.getStringValue(height) = "+format.getIntValue("height"));
              break;
          }
      }
      decoder.registerCodecListener(listener);
  }

  Codec.ICodecListener listener = new Codec.ICodecListener() {
      @Override
      public void onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i) {
          Format fmt = decoder.getBufferFormat(byteBuffer);
          System.out.println("fmt.toString() = " + fmt.toString());
          // 写入文件
          writeFile(byteBuffer,bufferInfo,i);
          System.out.println("onReadBuffer == " + bufferInfo.toString());
      }

      @Override
      public void one rror(int errorCode, int act, int trackId) {
          throw new RuntimeException();
      }
  };

  /**
   * 调用 start()方法开始解码
   */
  public void start(){
      boolean start = decoder.start();
      System.out.println("start = " + start);
  }

  /**
   * 调用getAvailableBuffer取到一个可用的ByteBuffer,把数据填入ByteBuffer里,然后再调用writeBuffer把ByteBuffer写入解码器实例
   */
  public void framebuffer(){
      int i = 1;
      boolean reachEnd = false;
      while (!reachEnd){
          extractor.next();//下一帧
          ByteBuffer dstBuf = null;
          dstBuf = decoder.getAvailableBuffer(100000);

          if (dstBuf == null) {
              try {
                  Thread.sleep(200);
              } catch (InterruptedException e) {
                  System.out.println("InterruptedException");
              }
              continue;
          }
          System.out.println("02b dstBuf.toString() = " + dstBuf.toString());
          BufferInfo bufferInfo = new BufferInfo();
          bufferInfo.offset = 0;
          bufferInfo.size = extractor.readBuffer(dstBuf, 0);
          bufferInfo.timeStamp = extractor.getFrameTimestamp();
          bufferInfo.bufferType = extractor.getFrameType();
          System.out.println("bufferInfo bufferInfo = " + bufferInfo.timeStamp);
          reachEnd =  extractor.getStreamId() == -1;
          System.out.println("reachEnd = " + reachEnd);
          if(reachEnd){
              bufferInfo.bufferType = bufferInfo.BUFFER_TYPE_END_OF_STREAM;
          }
          boolean ret = decoder.writeBuffer(dstBuf, bufferInfo);
          System.out.println("writeBuffer ret = " + ret);

          try {
              Thread.sleep(200);
          } catch (InterruptedException e) {
              System.out.println("InterruptedException");
          }
      }
  } 

  /**
   * 停止解码,释放资源
   */
  public void stopAndRelease(){
      System.out.println("VedioDecoder stopAndRelease");
      decoder.stop();
      decoder.release();
  } 

步骤2:封装writeFile方法,将获取到的buffer数据写入pcm文件中,此处com.harmonyospro.myapplication为工程bundleName,可替换为应用包名,代码如下:

private void writeFile(ByteBuffer outputBuffer, BufferInfo info, int trackId) {
    FileOutputStream fileOutputStream = null;
    File fd = new File("/data/data/com.harmonyospro.myapplication/1.pcm");
    try {
        fileOutputStream = new FileOutputStream(fd, true);
        final byte[] chunk = new byte[info.size];
        outputBuffer.get(chunk);
        fileOutputStream.write(chunk, 0, outputBuffer.limit());
        outputBuffer.clear();
    } catch (FileNotFoundException e) {
        System.out.println("02b FileNotFoundException");
    } catch (IOException e) {
        System.out.println("02b IOException");
    }finally {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            System.out.println("IOException");
        }
    }
}

步骤3:在需要调用视频提取音频的地方进行方法调用,代码如下:

VideoDecoder videoDecoder = new VideoDecoder();
videoDecoder.createDecoder();
videoDecoder.start();
videoDecoder.framebuffer();
//vedioDecoder.stopAndRelease(); // 需要停止的时候停止

这里就完成从视频获取音频并保存到pcm文件的功能了,获取到pcm文件,就可以使用AudioRenderer进行播放了。

 

【参考文档】

视频编解码文档:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-video-codec-0000000000031749

媒体提取开发指导:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-video-extractor-0000000000044202

音频播放开发指导:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-audio-playback-0000000000031734

标签:extractor,Java,format,System,HarmonyOS,pcm,println,bufferInfo,out
From: https://www.cnblogs.com/mayism123/p/17579770.html

相关文章

  • Java中抽象类和接口的区别
    一.抽象类定义上来看,被abstract关键字修饰的类称为抽象类。被abstract关键字修饰的方法称为抽象方法。当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类.例:[public|protected]abstractclassT{Stringname;intage;......
  • 【Java入门】小白快速入门 Java(一)
    ......
  • java~IDE工具技巧
    代码折叠操作:选中代码,按ctrl+alt+t,之后选择region代码环绕折叠后的效果spring代码格式化每个项目添加统一的依赖包<plugin><groupId>io.spring.javaformat</groupId><artifactId>spring-javaformat-maven-plugin</artifactId><version>0.0.35</version&g......
  • JavaScript 中的Promise学习
     代码示例:<scripttype="text/javascript">newPromise(function(resolve,reject){console.log(111);resolve(222);}).then(function(value){console.log(value);......
  • java多线程内存图
    多线程的例子例一:publicclassTest{publicstaticvoidmain(String[]args)throwsException{Threadx1=newThread(){@Overridepublicvoidrun(){for(inti=0;i<100;i++){Syst......
  • Java后端01(初识servlet)
    servlet添加依赖坐标(maven)<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency>小测试​ 所有请求都会固定携带一个参数(method)代表请求的方式(GET,POST,PUT,D......
  • 【JAVA】java日志框架 - slf4j
    1、配置依赖<!--slf4j日志门面--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.26</version></dependenc......
  • JavaWeb--环境搭建(idea,tomcat)到跑测试中我犯下的滔天大罪
    1.在网上copy时路径没有写对点击查看代码<?xmlversion="1.0"encoding="UTF-8"?><web-appxmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation......
  • Java开发笔记之mac的intellij idea在debug模式下卡住的问题
    0x00问题描述mac的Intellijidea在debug模式下放行时,程序会卡住无响应;即使在已经放行的情况下,后续代码也不运行,console内只显示Theapplicationisrunning或者等了很久程序才开始后续的运行。 0x01解决方案修改host中的配置。通过以下命令,打开hosts的编辑页面。sudovi......
  • int强制转换char在Java中的使用方法
    int强制转换char在Java中的使用方法在Java编程中,我们经常需要进行数据类型的转换,以便满足不同的需求。其中一个常见的转换就是将int类型强制转换为char类型。本文将围绕这一主题,介绍int强制转换char的使用方法,并探讨一些相关的注意事项。首先,让我们来看一下int和char的基本概念......