用Mencoder进行任意视频格式转换成flv

Mencoder简介:

     Mencoder是Mplayer自带的编码工具(Mplayer是Linux下的播放器,开源,支持几乎所有视频格式的播放,现在有windows和Mac版本)。

Mplayer的获得与配置:

     Mplayer windows版本下载列表:http://www5.mplayerhq.hu/MPlayer/releases/win32/

     列表中会有版本发布日期,可以挑选最新的版本,也可以选择old/去下载老的版本,笔者用的是6月份的版本。下载后解压到某个文件夹中即可。

     Codecs下载列表:http://www5.mplayerhq.hu/MPlayer/releases/codecs/

     选择windows-all-********.zip(*表示年月日)下载,将zip包中的文件夹下所有文件,包括*.dll、*.acm、*.ax等等全部copy到Mplayer根目录下的codecs文件夹中。

     此时最好把Mplayer.exe所在路径,同时也是Mencoder.exe所在路径添加到环境变量path中。

     现在可以试试用Mplayer播放视频,比如有个视频位于D:/music/APerfectMatch.wmv,那么可以打开一个cmd窗口,输入mplayer “D:/music/APerfectMatch.wmv”,感受一下来自Linux的播放器吧,可以通过键盘来操纵。

Mencoder转换视频格式:

     以将各种格式转换为flv格式为例(flv格式是flash支持的视频格式):

     mencoder "E:/test.m2p" -o "E:/output.flv" -of lavf  -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -oac mp3lame -lameopts abr:br=56 -ovc lavc -lavcopts vcodec=flv:vbitrate=500:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1 -vf scale=512:-3 -ofps 12 -srate 22050

     在命令行中输入这行代码(注意:windows的命令行是不支持换行的),按回车运行,一段时间之后就可以得到一个.flv文件,播放一下看看品质如何(可以直接用Mplayer播放)。

     下图是我这边转换的效果对比,左边是原视频,右边是转换后的视频:


 
    所有人都会觉得,转换后品质下降了很多,确实是这样,同时可以发现转换后的文件由原来的13M变成了1M,如果要提高品质,可以将vbitrate=500改为vbitrate=5000。

    转换后的效果对比,左边是原视频,右边是转换后的视频:


 
     品质几乎一样了,但同时,转换文件变成了6M。关于命令中的一些参数,解释一下:

     -o "E:/output.flv":是输出文件路径;

     -of lavf:是输出文件格式,这里不是flv而是lavf,是因为flv属于libavformat;

     -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames:-lavfopts表示对lavf进行一些设置,设置的内容就是i_certify_that_my_video_stream_does_not_use_b_frames,翻译成中文就是:我确定,我的视频不用B frames,对于转换flv格式,最好加上这个参数,否则可能会报错,如图:


     -oac mp3lame:oac=output audio codec,输出音频编码器,这里用的mp3lame;

     -lameopts abr:br=56:lame options,是专门针对mp3lame的参数设置,abr:br=56,是设置音频比特率为56(比特率:每秒钟输出的音频大小,单位kb/s);

-ovc lavc:ovc=output video codec,输出视频编码器,lavc表示one of libavcodec’s video codec,输出格式为libavformat之一,编码器当然也是libavcodec之一啦,至于libavodec里都有哪些编码器,可以查看Mplayer的官方文档;

 

    -lavcoptsvcodec=flv:vbitrate=500:mbd=2:mv0:v4mv:trell:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1:对lavc进行一些设置,详细介绍如下:

     vcodec=flv:指定视频编码器为flv;

     vbitrate=500:指定视频比特率为500,这个参数很重要,vbitrate大了可以让视频品质增加,但会让文件变的很大(可以参考flash8自带的转换工具的参数:低品质为150kb/s,中等品质为400kb/s,高品质为700kb/s)。

     mbd:宏模块选择算法,值为0~2默认为0,值越大转换越缓慢,但在品质和文件大小方面有好处;

     mv0:编译每个宏模块并选择最好的一个,当mbd=0时无效;

     v4mv:会稍微增加品质,mbd>0时效果更明显;

     trell:量子化网格搜索,对每8×8block找到最优化编码;

     cbp:只能和trell一期使用,评估失真的图像块编码;

     last_pred=3:与上一帧相比的移动数量预测,值为0~99,1~3比较合适,大于3时对品质的提高已经无关紧要,但却会降低速度;

     dia=4:移动搜索范围,值为-99~6,对于快速转换,-1是个不错的值,如果不是很重视速度,可以考虑2~4;

     cmp=6:值为1~2000,默认为0,设置用于全象素移动预算的比较函数

     vb_strategy=1:对动作很大的场景会有帮助,对有些视频,设置vmax_b_frames会有损品质,加上vb_strategy=1会好点。

     以上是对-lavcopts的详细解释,接下来继续说明mencoder的参数:

     -vf scale=512:-3:-vf表示视频格式,scale是缩放,512:-3表示强制将宽度设置为512,高度写为-3表示保持高宽比,也可以设置为-1或-2,具体表示什么,有兴趣的可以尝试一下。如果要强制转化为统一大小,可以直接写-vf scale=640:480,但笔者个人建议用-3来保持高宽比。-vf里还有expand和crop参数,例如:-vf scale=512:384,expand=512:384:::1,crop=512:384:0:0,expand表示膨胀,crop表示裁剪;

     -ofps 12:输出视频的帧频,一般,用于flash播放的视频帧频高了没有没有意义,反而会增加视频文件大小,但如果ofps设置的不合适,比如源视频帧频不是ofps的整数倍,可能会导致转换后的视频、音频不同步,似乎可以将这一参数改为-noskip来解决这一问题;   

    -srate 22050:音频采样率一般为22050或44100。

    对于转换flv格式,lavsopts的设置比较重要,还有很多参数本文没有涉及到,在笔者参考的文献中会有具体的说明,感兴趣的可以去看一下。我对于Mencoder的认识主要也是来源于下面2篇文章,实践的比较少,如果有理解和翻译的失误,再次欢迎与我交流。

参考文献:

《Mplayer官方文档,英文版》http://www.mplayerhq.hu/DOCS/HTML-single/en/MPlayer.html

《Linux Reviews》http://linuxreviews.org/man/mencoder/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的例子,演示如何使用OpenGL ES和MediaCodec将RGB图像转换为MP4视频。 首先,我们需要使用OpenGL ES将RGB图像渲染到纹理上。以下是一个简单的渲染器类: ```cpp class Renderer { public: Renderer(); ~Renderer(); void init(); void render(const uint8_t* data, int width, int height); private: GLuint mTexture; GLuint mProgram; GLuint mVertexShader; GLuint mFragmentShader; GLint mPositionHandle; GLint mTexCoordHandle; GLint mTextureHandle; }; ``` 在Renderer的构造函数中,我们可以编译和链接顶点着色器和片段着色器: ```cpp Renderer::Renderer() { const char* vertexShaderSrc = "attribute vec4 position;\n" "attribute vec2 texCoord;\n" "varying vec2 vTexCoord;\n" "void main() {\n" " gl_Position = position;\n" " vTexCoord = texCoord;\n" "}"; const char* fragmentShaderSrc = "precision mediump float;\n" "varying vec2 vTexCoord;\n" "uniform sampler2D texture;\n" "void main() {\n" " gl_FragColor = texture2D(texture, vTexCoord);\n" "}"; mVertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderSrc); mFragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderSrc); mProgram = glCreateProgram(); glAttachShader(mProgram, mVertexShader); glAttachShader(mProgram, mFragmentShader); glBindAttribLocation(mProgram, ATTRIB_VERTEX, "position"); glBindAttribLocation(mProgram, ATTRIB_TEXTURE, "texCoord"); glLinkProgram(mProgram); mPositionHandle = glGetAttribLocation(mProgram, "position"); mTexCoordHandle = glGetAttribLocation(mProgram, "texCoord"); mTextureHandle = glGetUniformLocation(mProgram, "texture"); } ``` 在render函数中,我们将RGB数据上传到纹理中,并在渲染器中绘制纹理: ```cpp void Renderer::render(const uint8_t* data, int width, int height) { if (!data) { return; } if (!mTexture) { glGenTextures(1, &mTexture); glBindTexture(GL_TEXTURE_2D, mTexture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } glBindTexture(GL_TEXTURE_2D, mTexture); glUseProgram(mProgram); glEnableVertexAttribArray(mPositionHandle); glEnableVertexAttribArray(mTexCoordHandle); glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gVertices); glVertexAttribPointer(mTexCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, gTexCoords); glUniform1i(mTextureHandle, 0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableVertexAttribArray(mPositionHandle); glDisableVertexAttribArray(mTexCoordHandle); } ``` 接下来,我们使用MediaCodec将渲染的帧编码为MP4视频。以下是一个简单的编码器类: ```cpp class Encoder { public: Encoder(); ~Encoder(); void init(int width, int height); void encodeFrame(const uint8_t* data, int64_t pts); void flush(); private: AMediaCodec* mEncoder; AMediaMuxer* mMuxer; int mTrackIndex; int mFrameIndex; bool mIsStarted; }; ``` 在Encoder的构造函数中,我们可以创建MediaCodec和MediaMuxer: ```cpp Encoder::Encoder() { mEncoder = AMediaCodec_createEncoderByType("video/avc"); AMediaFormat* format = AMediaFormat_new(); AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "video/avc"); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 2000000); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, 30); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatSurface); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, 720); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, 1280); AMediaCodec_configure(mEncoder, format, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE); AMediaFormat_delete(format); mTrackIndex = -1; mFrameIndex = 0; mIsStarted = false; } ``` 在init函数中,我们将MediaCodec的输出Surface传递给MediaMuxer,并启动MediaCodec和MediaMuxer: ```cpp void Encoder::init(int width, int height) { if (mIsStarted) { return; } AMediaCodec_start(mEncoder); AMediaMuxer_start(mMuxer); ANativeWindow* window = AMediaCodec_createInputSurface(mEncoder); AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder)); AMediaMuxer_start(mMuxer); mTrackIndex = 0; mIsStarted = true; } ``` 在encodeFrame函数中,我们从输出Surface中获取编码的数据,并将其写入MediaMuxer: ```cpp void Encoder::encodeFrame(const uint8_t* data, int64_t pts) { if (!mIsStarted) { return; } ANativeWindow* window = AMediaCodec_createInputSurface(mEncoder); AMediaCodec_configure(mEncoder, nullptr, nullptr, nullptr, 0); AMediaCodec_start(mEncoder); AMediaSurface* surface = AMediaSurface_fromSurfaceTexture((ASurfaceTexture*) window); AMediaCodec_setOutputSurface(mEncoder, surface); AMediaSurface_release(surface); AMediaCodecBufferInfo bufferInfo; int outputBufferIndex = AMediaCodec_dequeueOutputBuffer(mEncoder, &bufferInfo, 0); if (outputBufferIndex >= 0) { AMediaCodec_releaseOutputBuffer(mEncoder, outputBufferIndex, true); } int inputBufferIndex = AMediaCodec_dequeueInputBuffer(mEncoder, 0); if (inputBufferIndex >= 0) { AMediaCodecInputBuffer* inputBuffer = AMediaCodec_getInputBuffer(mEncoder, inputBufferIndex); size_t bufferSize = AMediaCodec_getInputBufferSize(mEncoder); uint8_t* buffer = AMediaCodecInputBuffer_getBuffer(inputBuffer); memcpy(buffer, data, bufferSize); AMediaCodec_queueInputBuffer(mEncoder, inputBufferIndex, 0, bufferSize, pts, 0); } outputBufferIndex = AMediaCodec_dequeueOutputBuffer(mEncoder, &bufferInfo, 0); if (outputBufferIndex >= 0) { AMediaCodecBufferInfo bufferInfo; AMediaCodec_getOutputBufferInfo(mEncoder, outputBufferIndex, &bufferInfo); uint8_t* outputBuffer = AMediaCodec_getOutputBuffer(mEncoder, outputBufferIndex); AMediaMuxer_writeSampleData(mMuxer, mTrackIndex, outputBuffer, &bufferInfo); AMediaCodec_releaseOutputBuffer(mEncoder, outputBufferIndex, false); mFrameIndex++; } } ``` 在flush函数中,我们停止MediaCodec和MediaMuxer: ```cpp void Encoder::flush() { if (!mIsStarted) { return; } AMediaCodec_signalEndOfInputStream(mEncoder); AMediaCodecBufferInfo bufferInfo; while (true) { int outputBufferIndex = AMediaCodec_dequeueOutputBuffer(mEncoder, &bufferInfo, 0); if (outputBufferIndex >= 0) { AMediaCodec_releaseOutputBuffer(mEncoder, outputBufferIndex, true); } else if (outputBufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { AMediaFormat* format = AMediaCodec_getOutputFormat(mEncoder); AMediaMuxer_addTrack(mMuxer, format); mTrackIndex = AMediaMuxer_addTrack(mMuxer, format); AMediaMuxer_start(mMuxer); } else { break; } } AMediaCodec_stop(mEncoder); AMediaCodec_delete(mEncoder); AMediaMuxer_stop(mMuxer); AMediaMuxer_delete(mMuxer); mIsStarted = false; } ``` 在使用时,我们可以像这样使用渲染器和编码器: ```cpp Renderer renderer; Encoder encoder; renderer.init(); encoder.init(width, height); for (int i = 0; i < numFrames; i++) { renderer.render(data[i], width, height); encoder.encodeFrame(data[i], i * 1000000 / 30); } encoder.flush(); ``` 请注意,这只是一个非常简单的例子,实际上要实现一个完整的视频编码器需要更多的步骤和细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值