我正在开发一个应用程序,我解码视频并替换某些帧并使用MediaMuxer
和重新编码MediaCodec
.如果我不替换任何帧(除了1080p视频,我将在下面解释),该应用程序可以工作,但是当我这样做时,替换后的帧会像素化,视频会不稳定.
此外,当我尝试使用1920x1080视频的应用程序时,我得到一个奇怪的输出,其中视频没有显示任何内容,直到我滚动到视频的开头,然后视频开始显示(但具有之前提到的相同问题)编辑后的像素化.
以下是我配置编码器的方法:
Video_format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, interval);
Video_format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
Video_format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
Video_format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
int color_format=MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
Video_format.setInteger(MediaFormat.KEY_COLOR_FORMAT, color_format);
encoder.configure(Video_format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Run Code Online (Sandbox Code Playgroud)
总而言之,我有两个问题:
1-修改帧后的像素化帧和不连贯的视频.
2-损坏的1920x1080视频,除非我滚动到开头.
编辑
这是一个未经编辑的1080p视频示例,当我在VLC上播放并在手机上播放不正确时会显示绿屏,除非我滚动开始,现在奇怪地在YouTube上正常工作,除了开始时的绿框
这是一个720p视频样本,在开始时也使用绿色框架进行编辑,并在编辑后清除像素化和滞后
这是我用来解码重新编码的代码:
do{
Bitmap b1;
if(edited_frames.containsKey(extractor.getSampleTime()))
b1=BitmapFactory.decodeFile(edited_frames.get(extractor.getSampleTime()));
else
b1=decode(extractor.getSampleTime(),Preview_width,Preview_Height);
if(b1==null) continue;
Bitmap b_scal=Bitmap.createScaledBitmap(b1, Preview_width, Preview_Height, false);
if(b_scal==null) continue;
encode(b_scal, encoder, muxer, videoTrackIndex);
lastTime=extractor.getSampleTime();
}while(extractor.advance());
Run Code Online (Sandbox Code Playgroud)
解码方法:
private Bitmap decode(final long time,final int width,final int height){
MediaFormat newFormat = codec.getOutputFormat();
Bitmap b = null;
final int TIMEOUT_USEC = 10000;
ByteBuffer[] …
Run Code Online (Sandbox Code Playgroud) 这是我的场景:
这可能是使用MediaMuxer实现的吗?很高兴收到有关此事的任何信息
我一直在寻找http://bigflake.com/mediacodec/#DecodeEditEncodeTest(谢谢@fadden),它说:
"解码帧并将其复制到ByteBuffer,
glReadPixels()
在Nexus 5上大约需要8ms,足够快,足以跟上30fps的输入速度,但将其作为PNG保存到磁盘所需的额外步骤是昂贵的(大约半秒钟) "
所以几乎1秒/帧是不可接受的.根据我的想法,一种方法是将每个帧保存为PNG,打开它,在其上添加位图叠加然后保存它.然而,这将花费大量时间来完成.
我想知道是否有办法做这样的事情:
在iOS上,我看到有一种方法可以将原始音频+原始视频+图像添加到容器中,然后对整个事物进行编码...
我应该切换到ffmpeg吗?ffmpeg的稳定性和兼容性如何?我是否冒着与Android 4.0+设备兼容的问题?有没有办法用ffmpeg来实现这个?我是这个领域的新手,还在做研究.
多年后编辑:
自问题以来已过去多年,并且ffmpeg在许可方面很难添加到商业软件中.这是如何演变的?较新版本的android在默认sdk上更有能力吗?
稍后编辑一段时间
我发布了一些负面投票信息作为答案,因此我将编辑原始问题.这是一个很棒的库,从我的测试中确实将水印应用于视频并使用进度回调进行处理,这使得向用户显示进度变得更加容易,并且还使用默认的android sdks.https://github.com/MasayukiSuda/Mp4Composer-android
该库使用Android MediaCodec API生成Mp4电影,并应用滤镜,缩放和旋转Mp4.
示例代码,可能如下所示:
new mp4Composer(sourcePath, destinationPath)
.filter(new GlWatermarkFilter(watermarkBitmap)
.listener(){
@Override
private void onProgress(double value){}
@Override
private void onCompleted(double value){
runOnUiThread( () ->{
showSneakbar
}
}
@Override
private void onCancelled(double value){}
@Override
private void onFailed(Exception e){}
}).start();
Run Code Online (Sandbox Code Playgroud)
在模拟器上进行测试,似乎在android 8+上正常工作,而在旧版本上生成黑色视频文件.但是,在真实设备上进行测试似乎有效.
在过去的几年里,我一直在做的视频修剪库的定制,更多更新的版本的工作,在这里(基于这个库)
虽然在大多数情况下,我已经成功地将其定制,甚至将所有文件转换为Kotlin,但它在修剪本身时遇到了一个重大问题.
它假定输入始终是文件,因此如果用户从返回Uri的应用选择器中选择一个项目,它就会崩溃.这样做的原因不仅仅是UI本身,还因为它用于修剪的库(mp4parser)假定只输入File(或文件路径)而不是Uri(在此处写过).我尝试了多种方法来让它获得一个Uri,但失败了.还在这里写了一下.
这就是为什么我使用我在StackOverflow(这里)找到的解决方案进行修剪的原因.关于它的好处是它很安静,只使用Android的框架本身.但是,似乎对于某些视频文件,它总是无法修剪它们.由于这些文件的一个例子,有一个原始库的储存库,在这里(问题报道这里).
看看异常,这就是我得到的:
E: Unsupported mime 'audio/ac3'
E: FATAL EXCEPTION: pool-1-thread-1
Process: life.knowledge4.videocroppersample, PID: 26274
java.lang.IllegalStateException: Failed to add the track to the muxer
at android.media.MediaMuxer.nativeAddTrack(Native Method)
at android.media.MediaMuxer.addTrack(MediaMuxer.java:626)
at life.knowledge4.videotrimmer.utils.TrimVideoUtils.genVideoUsingMuxer(TrimVideoUtils.kt:77)
at life.knowledge4.videotrimmer.utils.TrimVideoUtils.genVideoUsingMp4Parser(TrimVideoUtils.kt:144)
at life.knowledge4.videotrimmer.utils.TrimVideoUtils.startTrim(TrimVideoUtils.kt:47)
at life.knowledge4.videotrimmer.BaseVideoTrimmerView$initiateTrimming$1.execute(BaseVideoTrimmerView.kt:220)
at life.knowledge4.videotrimmer.utils.BackgroundExecutor$Task.run(BackgroundExecutor.java:210)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Run Code Online (Sandbox Code Playgroud)
我在Android上使用MediaExtractor编辑MP4来获取音频和视频曲目,然后使用MediaMuxer创建新文件.它工作正常.我可以在手机(和其他播放器)上播放新的MP4,但无法在网络上传输文件.当我停止MediaMuxer时,它会生成一条日志消息
"mp4文件不会流式传输."
我查看了底层的本机代码(MPEG4Writer.cpp),看起来编写器在计算所需的moov盒大小时遇到了麻烦.如果没有将比特率作为参数提供给作者,它会尝试使用某种启发式进行猜测.问题是MediaMuxer无法提供设置MPEG4Writer参数的功能.我是否遗漏了某些东西,或者我是不是在寻找其他一些生成文件(或标题)的方法?谢谢.
我正在寻找有效的方法来减少一些视频权重(作为File
上传),明显的答案是:让我们降低分辨率!(全高清或4K不需要,简单的高清对我来说已经足够了)我已经尝试了很多方法应该通过很多API(需要10),最好的方法是使用android-ffmpeg-java,但是...我的漂亮快速几乎当前的旗舰设备整个过程持续约length_of_video*4秒,此lib的重量是9 Mb,这个数量增加了我的应用程序大小...没有wai!(12 Mb到1 Mb是很好的结果,但仍然有太多的缺陷)
所以我决定使用原生的Android方式来实现这一点,MediaMuxer
并且MediaCodec
- 它们分别来自API18和API16(旧设备用户:抱歉;但他们也经常使用"低分辨率"相机).下面的方法几乎可以工作 - MediaMuxer
不尊重MediaFormat.KEY_WIDTH
和MediaFormat.KEY_HEIGHT
- 提取File
是"重新压缩",重量稍微小一点,但分辨率与原始视频相同File
...
那么,问题:如何使用MediaMuxer
和其他随附的类和方法压缩和重新调整/更改视频的分辨率?
public File getCompressedFile(String videoPath) throws IOException{
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(videoPath);
int trackCount = extractor.getTrackCount();
String filePath = videoPath.substring(0, videoPath.lastIndexOf(File.separator));
String[] splitByDot = videoPath.split("\\.");
String ext="";
if(splitByDot!=null && splitByDot.length>1)
ext = splitByDot[splitByDot.length-1];
String fileName = videoPath.substring(videoPath.lastIndexOf(File.separator)+1,
videoPath.length());
if(ext.length()>0)
fileName=fileName.replace("."+ext, "_out."+ext);
else
fileName=fileName.concat("_out");
final File outFile = new File(filePath, …
Run Code Online (Sandbox Code Playgroud) 我有一个从麦克风录制的3gp文件和一个mp4视频文件.我想将音频文件和视频文件复制到mp4文件并保存.我搜索了很多,但没有找到任何有助于使用Android的MediaMuxer api的东西. MediaMuxer api
更新:这是我的方法,mux两个文件,我有一个例外.原因是目标mp4文件没有任何轨道!someOne可以帮助我添加音频和视频轨道到多路复用器?
例外
java.lang.IllegalStateException: Failed to stop the muxer
Run Code Online (Sandbox Code Playgroud)
我的代码:
private void cloneMediaUsingMuxer( String dstMediaPath) throws IOException {
// Set up MediaExtractor to read from the source.
MediaExtractor soundExtractor = new MediaExtractor();
soundExtractor.setDataSource(audioFilePath);
MediaExtractor videoExtractor = new MediaExtractor();
AssetFileDescriptor afd2 = getAssets().openFd("Produce.MP4");
videoExtractor.setDataSource(afd2.getFileDescriptor() , afd2.getStartOffset(),afd2.getLength());
//PATH
//extractor.setDataSource();
int trackCount = soundExtractor.getTrackCount();
int trackCount2 = soundExtractor.getTrackCount();
//assertEquals("wrong number of tracks", expectedTrackCount, trackCount);
// Set up MediaMuxer for the destination.
MediaMuxer muxer;
muxer = new MediaMuxer(dstMediaPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// Set …
Run Code Online (Sandbox Code Playgroud) 我正在尝试调整ExtractDecodeEditEncodeMuxTest.java中的代码,以便从通过Cordova的device.capture.captureVideo录制的mp4中提取音频和视频,解码音频,编辑解码的音频样本,编码音频,并将音频复用回来用视频再次保存为mp4.
我的第一次尝试只是提取,解码,编码和复用音频而不尝试编辑任何音频样本 - 如果我能做到这一点,我相当确定我可以根据需要编辑解码的样本.我不需要编辑视频,因此我假设我可以简单地使用MediaExtractor来提取和复用视频轨道.
但是,我遇到的问题是我似乎无法正确地获得音频解码/编码过程.不断发生的事情是,复用器从提取的视频轨道和提取的 - >解码 - >编码的音频轨道创建mp4,但是当视频播放正常时,音频以短暂的噪声开始,然后看起来像是最后一个几秒钟的音频数据正常播放(但在视频的开头),然后静音播放视频的其余部分.
一些相关领域:
private MediaFormat audioFormat;
private MediaFormat videoFormat;
private int videoTrackIndex = -1;
private int audioTrackIndex = -1;
private static final int MAX_BUFFER_SIZE = 256 * 1024;
// parameters for the audio encoder
private static final String OUTPUT_AUDIO_MIME_TYPE = "audio/mp4a-latm"; // Advanced Audio Coding
private static final int OUTPUT_AUDIO_CHANNEL_COUNT = 2; // Must match the input stream. not using this, getting from input format
private static final int OUTPUT_AUDIO_BIT_RATE = …
Run Code Online (Sandbox Code Playgroud) 我写了一个演示来使用 MediaCodec 和 MediaMuxer 录制视频。
我用我的demo录制了一段视频,用ffprobe查看视频,结果如下:
Duration: 00:00:06.86, start: 0.000000, bitrate: 723 kb/s
Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 320x240, 619 kb/s, SAR 1:1 DAR 4:3, 30.02 fps, 30 tbr, 90k tbn, 180k tbc (default)
Metadata:
creation_time : 2015-06-05 13:19:24
handler_name : VideoHandle
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 96 kb/s (default)
Metadata:
creation_time : 2015-06-05 13:19:24
handler_name : SoundHandle
Run Code Online (Sandbox Code Playgroud)
它包含视频和音频信息,我发现音频属性与我在源代码中设置的相同,但视频属性不正确。我的视频设置源码如下:
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, 384000); …
Run Code Online (Sandbox Code Playgroud) 我正在尝试从 AudioRecord 对象中获取原始数据,并使用 MediaMuxer 和 MediaCodec 将其保存在文件中。
我启动编解码器,启动多路复用器,将数据加载到输入缓冲区中,但没有这样的运气。
从调试调查中,我发现问题发生在对dequeueInputBuffer()
. 看起来前几块数据成功了,但最终dequeueInputBuffer()
只是-1
不断返回。
有什么明显的东西我失踪了吗?这似乎是我正在填满输入缓冲区,但编解码器永远不会释放它们。
相关代码片段:
int numChunks = input.length / CHUNKSIZE;
mAudioEncoder.start();
for (int chunk = 0; chunk <= numChunks; chunk++) {
byte[] passMe = new byte[CHUNKSIZE];
int inputBufferIndex = -1;
Log.d("offerAudioEncoder","printing chunk #" + chunk + "of " + numChunks);
//Copy the data into the chunk array
if (chunk < input.length / CHUNKSIZE)
for (int i = 0; i < CHUNKSIZE; i++)
passMe[i] = input[chunk * …
Run Code Online (Sandbox Code Playgroud) 我正在开发一个具有与其他应用程序共享屏幕功能的应用程序。
我为此使用了媒体投影 API。我还使用 MediaMuxer 组合音频和视频输出以进行屏幕共享。
我知道媒体投影 API 用于屏幕录制,但我只想在录制时共享屏幕。
为此,我修改了 MediaMuxer 类的 writeSampleData 方法,以通过套接字将字节发送到网络上的其他设备。
下面是代码:
OutputStream outStream;
Run Code Online (Sandbox Code Playgroud)
outStream = ScreenRecordingActivity.getInstance().socket.getOutputStream();
void writeSampleData(final int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo) {
if (mStatredCount > 0) {
mMediaMuxer.writeSampleData(trackIndex, byteBuf, bufferInfo);
if (bufferInfo.size != 0) {
byteBuf.position(bufferInfo.offset);
byteBuf.limit(bufferInfo.offset + bufferInfo.size);
if (outStream != null) {
try {
byte[] bytes = new byte[byteBuf.remaining()];
byteBuf.get(bytes);
//Send the data
outStream.write(bytes);
outStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
字节通过套接字成功传输,我也能够在接收器端接收这些字节。
下面是接收端接收字节的代码:
private class …
Run Code Online (Sandbox Code Playgroud) android mediamuxer android-mediaprojection android-mediacodec
android ×10
mediamuxer ×10
mediacodec ×4
video ×4
audio ×2
bitrate ×1
encoding ×1
ffmpeg ×1
mp4 ×1
multiplexing ×1