首页 > 技术文章 > 解决linux AMR转MP3出现转码成功却无法播放的问题

fennudexiaofan 2017-09-22 23:25 原文

 

根据帖子:http://blog.csdn.net/z313731418/article/details/50218341  的提示,在linux安装ffmpeg,确实在linux下使用命令可以将amr转成mp3,并且可以进行播放,不过使用编译的jave-1.0.2.jar进行转化的时候,目录下生成了mp3文件并没有变大反而变小(windows下转码会变大,不过代码会报错,不过转化的mp3是可以播放的),发现mp3不能正常播放。项目代码抛出异常如下:

经过项目组的老大的指引:

       使用反编译软件查看了该jar的源代码,查找  “ Stream mapping:(linux下出现的)” 和 “Duration: N/A, bitrate: N/A(windows下出现)”对应的源代码如下:

 

原因分析:

可以确定的一点最后都是拼接成命令:/tmp/jave-1/ffmpeg -i 源文件路径 -f mp3 -y 输出路径   让amr变成mp3

都抛出异常,不过windows下的文件确实转化成功,并且可以播放。根据转化之后的mp3文件的大小判断,可能是异常处理导致写入文件操作被迫中断了。

综合这些分析之后,决定对源代码的异常处理进行修改,重新修改jar包再进行打包。

解决方案:

将  public void encode(File source, File target, EncodingAttributes attributes, EncoderProgressListener listener){}这个方法里面的try的处理读取的异常处理代码进行重写

  public void encode(File source, File target, EncodingAttributes attributes, EncoderProgressListener listener)
    throws IllegalArgumentException, InputFormatException, EncoderException
  {
    String formatAttribute = attributes.getFormat();
    Float offsetAttribute = attributes.getOffset();
    Float durationAttribute = attributes.getDuration();
    AudioAttributes audioAttributes = attributes.getAudioAttributes();
    VideoAttributes videoAttributes = attributes.getVideoAttributes();
    if ((audioAttributes == null) && (videoAttributes == null)) {
      throw new IllegalArgumentException(
        "Both audio and video attributes are null");
    }
    target = target.getAbsoluteFile();
    target.getParentFile().mkdirs();
    FFMPEGExecutor ffmpeg = this.locator.createExecutor();
    if (offsetAttribute != null) {
      ffmpeg.addArgument("-ss");
      ffmpeg.addArgument(String.valueOf(offsetAttribute.floatValue()));
    }
    ffmpeg.addArgument("-i");
    ffmpeg.addArgument(source.getAbsolutePath());
    if (durationAttribute != null) {
      ffmpeg.addArgument("-t");
      ffmpeg.addArgument(String.valueOf(durationAttribute.floatValue()));
    }
    if (videoAttributes == null) {
      ffmpeg.addArgument("-vn");
    } else {
      String codec = videoAttributes.getCodec();
      if (codec != null) {
        ffmpeg.addArgument("-vcodec");
        ffmpeg.addArgument(codec);
      }
      String tag = videoAttributes.getTag();
      if (tag != null) {
        ffmpeg.addArgument("-vtag");
        ffmpeg.addArgument(tag);
      }
      Integer bitRate = videoAttributes.getBitRate();
      if (bitRate != null) {
        ffmpeg.addArgument("-b");
        ffmpeg.addArgument(String.valueOf(bitRate.intValue()));
      }
      Integer frameRate = videoAttributes.getFrameRate();
      if (frameRate != null) {
        ffmpeg.addArgument("-r");
        ffmpeg.addArgument(String.valueOf(frameRate.intValue()));
      }
      VideoSize size = videoAttributes.getSize();
      if (size != null) {
        ffmpeg.addArgument("-s");
        ffmpeg.addArgument(String.valueOf(size.getWidth()) + "x" + 
          String.valueOf(size.getHeight()));
      }
    }
    if (audioAttributes == null) {
      ffmpeg.addArgument("-an");
    } else {
      String codec = audioAttributes.getCodec();
      if (codec != null) {
        ffmpeg.addArgument("-acodec");
        ffmpeg.addArgument(codec);
      }
      Integer bitRate = audioAttributes.getBitRate();
      if (bitRate != null) {
        ffmpeg.addArgument("-ab");
        ffmpeg.addArgument(String.valueOf(bitRate.intValue()));
      }
      Integer channels = audioAttributes.getChannels();
      if (channels != null) {
        ffmpeg.addArgument("-ac");
        ffmpeg.addArgument(String.valueOf(channels.intValue()));
      }
      Integer samplingRate = audioAttributes.getSamplingRate();
      if (samplingRate != null) {
        ffmpeg.addArgument("-ar");
        ffmpeg.addArgument(String.valueOf(samplingRate.intValue()));
      }
      Integer volume = audioAttributes.getVolume();
      if (volume != null) {
        ffmpeg.addArgument("-vol");
        ffmpeg.addArgument(String.valueOf(volume.intValue()));
      }
    }
    ffmpeg.addArgument("-f");
    ffmpeg.addArgument(formatAttribute);
    ffmpeg.addArgument("-y");
    ffmpeg.addArgument(target.getAbsolutePath());
    try {
      ffmpeg.execute();
    } catch (IOException e) {
      throw new EncoderException(e);
    }
    try {
      String lastWarning = null;

      long progress = 0L;
      RBufferedReader reader = null;
      reader = new RBufferedReader(new InputStreamReader(
        ffmpeg.getErrorStream()));
      MultimediaInfo info = parseMultimediaInfo(source, reader);
      long duration;
      long duration;
      if (durationAttribute != null) {
        duration = 
          Math.round(durationAttribute.floatValue() * 1000.0F);
      } else {
        duration = info.getDuration();
        if (offsetAttribute != null)
        {
          duration = duration - 
            Math.round(offsetAttribute.floatValue() * 1000.0F);
        }
      }
      if (listener != null) {
        listener.sourceInfo(info);
      }
      int step = 0;
      String line;
      while ((line = reader.readLine()) != null)
      {
        String line;
        if (step == 0) {
          if (line.startsWith("WARNING: ")) {
            if (listener != null)
              listener.message(line);
          } else {
            if (!line.startsWith("Output #0")) {
              throw new EncoderException(line);
            }
            step++;
          }
        } else if ((step == 1) && 
          (!line.startsWith("  "))) {
          step++;
        }

        if (step == 2) {
          if (!line.startsWith("Stream mapping:")) {
            throw new EncoderException(line);
          }
          step++;
        }
        else if ((step == 3) && 
          (!line.startsWith("  "))) {
          step++;
        }

        if (step == 4) {
          line = line.trim();
          if (line.length() > 0) {
            Hashtable table = parseProgressInfoLine(line);
            if (table == null) {
              if (listener != null) {
                listener.message(line);
              }
              lastWarning = line;
            } else {
              if (listener != null) {
                String time = (String)table.get("time");
                if (time != null) {
                  int dot = time.indexOf('.');
                  if ((dot > 0) && (dot == time.length() - 2) && 
                    (duration > 0L)) {
                    String p1 = time.substring(0, dot);
                    String p2 = time.substring(dot + 1);
                    try {
                      long i1 = Long.parseLong(p1);
                      long i2 = Long.parseLong(p2);
                      progress = i1 * 1000L + 
                        i2 * 100L;
                      int perm = 
                        (int)Math.round(progress * 1000L / 
                        duration);
                      if (perm > 1000) {
                        perm = 1000;
                      }
                      listener.progress(perm);
                    }
                    catch (NumberFormatException localNumberFormatException) {
                    }
                  }
                }
              }
              lastWarning = null;
            }
          }
        }
      }
      if ((lastWarning == null) || 
        (SUCCESS_PATTERN.matcher(lastWarning).matches())) break label1089;
      throw new EncoderException(lastWarning);
    }
    catch (IOException e)
    {
      throw new EncoderException(e); } finally {
      jsr 6; } localObject1 = 
      returnAddress;

    ffmpeg.destroy();
    ret; label1089: jsr -9;
  }
}

上述中的异常处理部分代码进行封装,保证不修改源代码。增加如下方法:

  protected void processErrorOutput(EncodingAttributes attributes, BufferedReader errorReader, File source, EncoderProgressListener listener) throws EncoderException, IOException {
    String lastWarning = null;

    long progress = 0L;
    Float offsetAttribute = attributes.getOffset();
    MultimediaInfo info = parseMultimediaInfo(source, (RBufferedReader)errorReader);
    Float durationAttribute = attributes.getDuration();
    long duration;
    long duration;
    if (durationAttribute != null) {
      duration = Math.round(durationAttribute.floatValue() * 1000.0F);
    }
    else {
      duration = info.getDuration();
      if (offsetAttribute != null) {
        duration -= Math.round(offsetAttribute.floatValue() * 1000.0F);
      }
    }

    if (listener != null) {
      listener.sourceInfo(info);
    }
    int step = 0;
    String line;
    while ((line = errorReader.readLine()) != null) {
      if (step == 0) {
        if (line.startsWith("WARNING: ")) {
          if (listener != null)
            listener.message(line);
        } else {
          if (!line.startsWith("Output #0")) {
            throw new EncoderException(line);
          }
          step++;
        }
      } else if ((step == 1) && 
        (!line.startsWith("  "))) {
        step++;
      }

      if (step == 2) {
        if (!line.startsWith("Stream mapping:")) {
          throw new EncoderException(line);
        }
        step++;
      }
      else if ((step == 3) && 
        (!line.startsWith("  "))) {
        step++;
      }

      if (step == 4) {
        line = line.trim();
        if (line.length() > 0) {
          Hashtable table = parseProgressInfoLine(line);
          if (table == null) {
            if (listener != null) {
              listener.message(line);
            }
            lastWarning = line;
          } else {
            if (listener != null) {
              String time = (String)table.get("time");
              if (time != null) {
                int dot = time.indexOf(46);
                if ((dot > 0) && (dot == time.length() - 2) && (duration > 0L))
                {
                  String p1 = time.substring(0, dot);
                  String p2 = time.substring(dot + 1);
                  try {
                    long i1 = Long.parseLong(p1);
                    long i2 = Long.parseLong(p2);
                    progress = i1 * 1000L + i2 * 100L;

                    int perm = (int)Math.round(progress * 1000L / duration);

                    if (perm > 1000) {
                      perm = 1000;
                    }
                    listener.progress(perm);
                  }
                  catch (NumberFormatException e) {
                  }
                }
              }
            }
            lastWarning = null;
          }
        }
      }
    }
    if ((lastWarning != null) && 
      (!SUCCESS_PATTERN.matcher(lastWarning).matches()))
      throw new EncoderException(lastWarning);
  }
}

将encode方法的中相同的代码修改成调用processErrorOutput(attributes, reader, source, listener);   修改之后的源代码:

  public void encode(File source, File target, EncodingAttributes attributes, EncoderProgressListener listener)
    throws IllegalArgumentException, InputFormatException, EncoderException
  {
    FFMPEGExecutor ffmpeg = this.locator.createExecutor();
    setAttributes(attributes, ffmpeg, source, target);
    try {
      ffmpeg.execute();
    } catch (IOException e) {
      throw new EncoderException(e);
    }
    try {
      RBufferedReader reader = new RBufferedReader(new InputStreamReader(ffmpeg.getErrorStream()));
      processErrorOutput(attributes, reader, source, listener);
    } catch (IOException e) {
      throw new EncoderException(e);
    } finally {
      ffmpeg.destroy();
    }
  }

 

实际上jar包并没有修改啥,只是把异常处理的代码进行抽离成一个方法processErrorOutput而已。

最终实现代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import it.sauronsoftware.jave.*;


/**
 * 音频工具类
 */
public class AudioUtil {

    private static Logger logger = LoggerFactory.getLogger(AudioUtil.class);

    /**
     * Amr格式音频转为Mp3格式
     * 
     * @param sourcePath
     *            amr格式源路径
     *            
     * @param targetPath
     *            mp3格式输出路径
     *            
     * @example getAmrConversionMp3("D:\\test\\04296548.amr","D:\\test\\04296548.mp3");
     * 
     */
    public static void getAmrConversionMp3(String sourcePath, String targetPath) {
        File source = new File(sourcePath);
        File target = new File(targetPath);
        AudioAttributes audio = new AudioAttributes();
        AmrToMp3Encoder encoder = new AmrToMp3Encoder();
        audio.setCodec("libmp3lame");
        EncodingAttributes attrs = new EncodingAttributes();
        attrs.setFormat("mp3");
        attrs.setAudioAttributes(audio);
        try {
            encoder.encode(source, target, attrs);
        } catch (IllegalArgumentException e) {
            e.printStackTrace(); 
        } catch (InputFormatException e) {
            e.printStackTrace(); 
        } catch (EncoderException e) {
            e.printStackTrace(); 
        }
    }

    /**
     * 判断文件是不是amr格式
     *
     * @param  fileName  文件名
     * */
    public static boolean isAudioAmr(String fileName) {
        String tmpName = fileName.toLowerCase();
        return  tmpName.endsWith(".amr");
    }


    private static class AmrToMp3Encoder extends Encoder {
        protected void processErrorOutput(EncodingAttributes attributes, BufferedReader errorReader, File source, EncoderProgressListener listener) throws EncoderException, IOException {
            // 屏蔽默认的错误处理
            try {
                String line;
                while ((line = errorReader.readLine()) != null) {
                    logger.debug(line);
                }
            }
            catch (Exception exp) {
                logger.error("file convert error message process failed. ", exp);
            }
        }
    }
}

结论:

编写AmrToMp3Encoder类继承Encoder,并重写方法processErrorOutput,将默认的方法里面的异常方法全部屏蔽,通过单元测试发现,在linux下和windows下都不会抛出异常,

而且转化的mp3格式均可以播放。可能真的是这个jar包还存在着bug。该结果是在客观事实中进行求证,如有不对之处,请留下评论,大家一起探讨一下,而且ffmpeg这个文件需要进行替换处理,使用你所在linux环境的ffmpeg。修改jar包的源代码已经贴上了。

不想手动的同学可以直接下载:

链接: https://pan.baidu.com/s/1c2FAp8k    密码: fmx3

该帖子属于原创帖子:转载请贴原帖子的地址,谢谢!

 

推荐阅读