繁体   English   中英

在 Xamarin Android 中使用 FFmpeg 输出文件

[英]Output file using FFmpeg in Xamarin Android

我正在使用 Xamarin 构建一个 android 应用程序。 该应用程序的要求是从相机捕获视频并对视频进行编码以将其发送到服务器。

最初,我在服务器端使用编码器库对录制的视频进行编码,但事实证明它非常不可靠且效率低下,尤其是对于大型视频文件。 我已经在这里的另一个线程上发布了我的问题

然后我决定在客户端对视频进行编码,然后将其发送到服务器。 我发现编码有点复杂,并且没有太多关于如何做到这一点的信息。 因此,我搜索了我知道如何使用 FFmpeg 编解码器对视频进行编码的唯一方法。 我找到了一些解决方案。 GitHub 上有一个项目演示了如何在 Xamarin android 项目中使用 FFmpeg。 但是,运行该解决方案不会给出任何输出。 该项目有一个二进制 FFmpeg 文件,该文件使用以下代码安装到手机目录中:

_ffmpegBin = InstallBinary(XamarinAndroidFFmpeg.Resource.Raw.ffmpeg, "ffmpeg", false);

以下是将视频编码为不同输出集的示例代码:

_workingDirectory = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
var sourceMp4 = "cat1.mp4";
var destinationPathAndFilename = System.IO.Path.Combine (_workingDirectory, "cat1_out.mp4");
var destinationPathAndFilename2 = System.IO.Path.Combine (_workingDirectory, "cat1_out2.mp4");
var destinationPathAndFilename4 = System.IO.Path.Combine (_workingDirectory, "cat1_out4.wav");
if (File.Exists (destinationPathAndFilename))
    File.Delete (destinationPathAndFilename);
CreateSampleFile(Resource.Raw.cat1, _workingDirectory, sourceMp4);


var ffmpeg = new FFMpeg (this, _workingDirectory);

var sourceClip = new Clip (System.IO.Path.Combine(_workingDirectory, sourceMp4));

var result = ffmpeg.GetInfo (sourceClip);

var br = System.Environment.NewLine;

// There are callbacks based on Standard Output and Standard Error when ffmpeg binary is running as a process:

var onComplete = new MyCommand ((_) => {
    RunOnUiThread(() =>_logView.Append("DONE!" + br + br));
});

var onMessage = new MyCommand ((message) => {
    RunOnUiThread(() =>_logView.Append(message + br + br));
});

var callbacks = new FFMpegCallbacks (onComplete, onMessage);

// 1. The idea of this first test is to show that video editing is possible via FFmpeg:
// It results in a 150x150 movie that eventually zooms on a cat ear. This is desaturated, and there's a fade-in.

var filters = new List<VideoFilter> ();
filters.Add (new FadeVideoFilter ("in", 0, 100));
filters.Add(new CropVideoFilter("150","150","0","0"));
filters.Add(new ColorVideoFilter(1.0m, 1.0m, 0.0m, 0.5m, 1.0m, 1.0m, 1.0m, 1.0m));
var outputClip = new Clip (destinationPathAndFilename) { videoFilter = VideoFilter.Build (filters)  };
outputClip.H264_CRF = "18"; // It's the quality coefficient for H264 - Default is 28. I think 18 is pretty good.
ffmpeg.ProcessVideo(sourceClip, outputClip, true, new FFMpegCallbacks(onComplete, onMessage));

//2. This is a similar version in command line only:
string[] cmds = new string[] {
    "-y",
    "-i",
    sourceClip.path,
    "-strict",
    "-2",
    "-vf",
    "mp=eq2=1:1.68:0.3:1.25:1:0.96:1",
    destinationPathAndFilename2,
    "-acodec",
    "copy",
};
ffmpeg.Execute (cmds, callbacks);

// 3. This lists codecs:
string[] cmds3 = new string[] {
    "-codecs",
};
ffmpeg.Execute (cmds, callbacks);

// 4. This convers to WAV
// Note that the cat movie just has some silent house noise.
ffmpeg.ConvertToWaveAudio(sourceClip, destinationPathAndFilename4, 44100, 2, callbacks, true);

我尝试了不同的命令,但没有生成输出文件。 我尝试使用这里找到的另一个项目但这个项目有同样的问题。 我没有收到任何错误,但没有生成输出文件。 我真的希望有人能帮助我找到一种方法,我可以设法在我的项目中使用 FFmpeg,或者某种方法来压缩视频以将其传输到服务器。

如果有人能指出我正确的方向,我将不胜感激。

试试这个项目, https://github.com/neurospeech/xamarin-android-ffmpeg

Install-Package Xamarin.Android.FFmpeg

将此用作模板,这使您可以记录输出并计算进度。

您可以查看源代码,这个下载 ffmpeg 并在第一次使用时验证 sha1 哈希值。

public class VideoConverter 
{

    public VideoConverter()
    {

    }

    public File ConvertFile(Context contex,
        File inputFile, 
        Action<string> logger = null, 
        Action<int,int> onProgress = null)
    {
        File ouputFile = new File(inputFile.CanonicalPath + ".mpg");

        ouputFile.DeleteOnExit();

        List<string> cmd = new List<string>();
        cmd.Add("-y");
        cmd.Add("-i");
        cmd.Add(inputFile.CanonicalPath);

        MediaMetadataRetriever m = new MediaMetadataRetriever();
        m.SetDataSource(inputFile.CanonicalPath);

        string rotate = m.ExtractMetadata(Android.Media.MetadataKey.VideoRotation);

        int r = 0;

        if (!string.IsNullOrWhiteSpace(rotate)) {
            r = int.Parse(rotate);
        }

        cmd.Add("-b:v");
        cmd.Add("1M");

        cmd.Add("-b:a");
        cmd.Add("128k");


        switch (r)
        {
            case 270:
                cmd.Add("-vf scale=-1:480,transpose=cclock");
                break;
            case 180:
                cmd.Add("-vf scale=-1:480,transpose=cclock,transpose=cclock");
                break;
            case 90:
                cmd.Add("-vf scale=480:-1,transpose=clock");
                break;
            case 0:
                cmd.Add("-vf scale=-1:480");
                break;
            default:

                break;
        }

        cmd.Add("-f");
        cmd.Add("mpeg");

        cmd.Add(ouputFile.CanonicalPath);

        string cmdParams = string.Join(" ", cmd);

        int total = 0;
        int current = 0;

        await FFMpeg.Xamarin.FFMpegLibrary.Run(
            context,
             cmdParams 
            , (s) => {
                logger?.Invoke(s);
                int n = Extract(s, "Duration:", ",");
                if (n != -1) {
                    total = n;
                }
                n = Extract(s, "time=", " bitrate=");
                if (n != -1) {
                    current = n;
                    onProgress?.Invoke(current, total);
                }
            });

        return ouputFile;
    }

    int Extract(String text, String start, String end)
    {
        int i = text.IndexOf(start);
        if (i != -1)
        {
            text = text.Substring(i + start.Length);
            i = text.IndexOf(end);
            if (i != -1)
            {
                text = text.Substring(0, i);
                return parseTime(text);
            }
        }
        return -1;
    }

    public static int parseTime(String time)
    {
        time = time.Trim();
        String[] tokens = time.Split(':');
        int hours = int.Parse(tokens[0]);
        int minutes = int.Parse(tokens[1]);
        float seconds = float.Parse(tokens[2]);
        int s = (int)seconds * 100;
        return hours * 360000 + minutes * 60100 + s;
    }

}

只需弄清楚如何通过在 AndroidManifest 文件中添加权限来获取输出。

android.permission.WRITE_EXTERNAL_STORAG

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM