在opensuse上装vlc为什么mp4v2 vlc不能播放放mp4

视频播放的工作终于告一段落了,由于之前对ffmpeg, vlc,流媒体这些东西完全没有接触过,所以很辛苦的查找资料,艰难的理解一点点的概念,还好,总算有了一个可行的解决方法,算是最简单的,不用对解码库有深入的了解,算是入门的方法。
一、应用场景:
EPUB书中嵌入视频,要求点击图片时能够打开相应的视频进行播放,视频的格式为mp4
二、解决方法:
1.选用播放器,最开始采用github上的开源工程 havlenapetr/ffmpeg 使用cygwin + NDK r8编译通过,在真机和模拟器上播放都正常,但是这个库有几个缺陷,最致命的是它运行需要依赖android的libjniaudio.so和libjnivideo.so两个基础库,而这两个库是与系统版本有关的,这就意味着要针对不同的版本编译不同的基础库,这给将来的应用带来了很大麻烦,另一个缺陷是大屏幕播放图像倾斜的问题,但是这个问题在网上有人已经解决了,修改onVideoSizeChangedListener的实现即可解决,具体参照&这篇博文。
最后找到了vlc这个解码库,它比ffmpeg还要庞大,ffmpeg只是它的一部分而已,但是它的好处是各种android版本都支持的比较好,所以就采用它了。在ubuntu 10 + ruby 1.9.2 + NDK r5c下编译通过,播放本地视频一切正常。代码下载地址&
完成了这一步,整个工作就完成了一半,另外一半是如何播放EPUB书中的mp4视频? 这里边有两个问题:
1. 我们知道,EPUB其实就是一个zip文件,那么为了减少缓冲时间,最好的方式是把mp4不压缩存储在zip文件中,播放时直接把文件指针seek到mp4开始的位置开始加载,用播放本地文件的方式来解码,但是看了faplayer提供的接口后我放弃了,faplayer提供了类似android自带的MediaPlayer的接口,数据加载只有一个setDataSource(String path)的接口。完全没有办法加载文件指针,所以我们退一步,把mp4缓存到本地来播放,这样的话为了减少播放前的等待时间,我们不能等到Mp4加载全部完成后再来播放,只能边缓冲边播放,这就带来了第二个问题:
2.我们知道,mp4的结构信息存储在一个叫moov的box中,我们至少要等到moov这个box被加载后(当然还得有一部分真实的音视频数据也得被加载)后,才能正常进行播放,而真实的mp4中,有很多是吧moov放置在文件的尾部的,我猜测是便于生成数据,避免产生moov box对后面数据的偏移产生影响。这就需要预先对mp4进行调整(预先我指的是在制作EPUB书的时候),在参考上的一篇博文后,我找到了qt-faststart&这个工具,它可以把moov box调整到文件的前部,后来我找到了一个更好的工具,我们在后面再说。
调整完moov后还有一个问题:moov中记录了mp4有多少个track,track中记录的是真实的音视频数据存储的位置,而真实数据存储在mdat中,而且经常是音频,视频分开放置的,而且有可能相隔的比较远,这样的话就会出现一个问题:在播放的时候音频或是视频数据被丢掉了,要避免这个问题,我们需要对mp4做一个处理,这个处理就叫做"流化",经过搜索,我找到了一个很好的工具,那就是VLC Media Player 下载地址&&它提供了很好的流化视频的功能,在流化的时候会把moov提到文件前面去,所以它比qt-faststart这个工具要强很多,完成这个后我们的播放就基本上没有技术难题了
好了,说了这么多,放出示例代码,代码参照faplayer:
1 public class VideoPlayerActivity extends Activity implements
AbsMediaPlayer.OnBufferingUpdateListener,
AbsMediaPlayer.OnCompletionListener, AbsMediaPlayer.OnErrorListener,
AbsMediaPlayer.OnInfoListener, AbsMediaPlayer.OnPreparedListener,
AbsMediaPlayer.OnProgressUpdateListener,
AbsMediaPlayer.OnVideoSizeChangedListener, OnTouchListener,
OnClickListener, OnSeekBarChangeListener {
static final String LOGTAG = "DANMAKU-PlayerActivity";
private static final int SURFACE_NONE = 0;
private static final int SURFACE_FILL = 1;
private static final int SURFACE_ORIG = 2;
private static final int SURFACE_4_3 = 3;
private static final int SURFACE_16_9 = 4;
private static final int SURFACE_16_10 = 5;
private static final int SURFACE_MAX = 6;
private static final int MEDIA_PLAYER_BUFFERING_UPDATE = 0x4001;
private static final int MEDIA_PLAYER_COMPLETION = 0x4002;
private static final int MEDIA_PLAYER_ERROR = 0x4003;
private static final int MEDIA_PLAYER_INFO = 0x4004;
private static final int MEDIA_PLAYER_PREPARED = 0x4005;
private static final int MEDIA_PLAYER_PROGRESS_UPDATE = 0x4006;
private static final int MEDIA_PLAYER_VIDEO_SIZE_CHANGED = 0x4007;
private static final int VIDEO_STATE_UPDATE = 0x4008;
private static final int VIDEO_CACHE_READY = 0x4009;
private static final int VIDEO_CACHE_UPDATE = 0x400A;
private static final int VIDEO_CACHE_FINISH = 0x400B;
public static final String REMOTE_URL = "url";
private static final int READY_BUFFER_LENGTH = 0;
private static final int CACHE_BUFFER_LENGTH = 0;
/* the media player */
private AbsMediaPlayer mMediaPlayer = null;
/* GUI evnet handler */
private Handler mEventH
/* player misc */
private ProgressBar mProgressBarP
/* player controls */
private TextView mTextViewT
private SeekBar mSeekBarP
private TextView mTextViewL
private ImageButton mImageButtonToggleM
private ImageButton mImageButtonSwitchA
private ImageButton mImageButtonSwitchS
private ImageButton mImageButtonP
private ImageButton mImageButtonToggleP
private ImageButton mImageButtonN
private ImageButton mImageButtonSwitchAspectR
private LinearLayout mLinearLayoutControlB
/* player video */
private SurfaceView mSurfaceViewV
private SurfaceHolder mSurfaceHolderV
/* misc */
private boolean mMediaPlayerLoaded = false;
private boolean mMediaPlayerStarted = false;
/* misc */
private int mTime = -1;
private int mLength = -1;
private boolean mCanSeek = true;
private int mAspectRatio = 0;
private int mAudioTrackIndex = 0;
private int mAudioTrackCount = 0;
private int mSubtitleTrackIndex = 0;
private int mSubtitleTrackCount = 0;
private String localUri = null;
private String remoteUrl = null;
private long mediaLength = 0;
private int readS
private boolean isR
private int errorC
protected boolean isE
protected int curP
private String cacheFileP
protected void initializeEvents() {
mEventHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MEDIA_PLAYER_BUFFERING_UPDATE: {
if (mMediaPlayerLoaded) {
mProgressBarPreparing
.setVisibility(msg.arg1 & 100 ? View.VISIBLE
: View.GONE);
case MEDIA_PLAYER_COMPLETION: {
curPosition = 0;
mMediaPlayer.pause();
case MEDIA_PLAYER_ERROR: {
mMediaPlayerLoaded = true;
isError = true;
errorCount++;
mMediaPlayer.pause();
mProgressBarPreparing.setVisibility(View.VISIBLE);
case MEDIA_PLAYER_INFO: {
if (msg.arg1 == MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
mCanSeek = false;
case MEDIA_PLAYER_PREPARED: {
mProgressBarPreparing.setVisibility(View.GONE);
mMediaPlayerLoaded = true;
mMediaPlayer.seekTo(curPosition);
startMediaPlayer();
case MEDIA_PLAYER_PROGRESS_UPDATE: {
if (mMediaPlayer != null) {
int length = msg.arg2;
if (length &= 0) {
mTextViewLength.setText(SystemUtility
.getTimeString(mLength));
mSeekBarProgress.setMax(mLength);
int time = msg.arg1;
if (time &= 0) {
mTextViewTime.setText(SystemUtility
.getTimeString(mTime));
mSeekBarProgress.setProgress(mTime);
case MEDIA_PLAYER_VIDEO_SIZE_CHANGED: {
AbsMediaPlayer player = (AbsMediaPlayer) msg.
SurfaceView surface = mSurfaceViewV
int ar = mAspectR
changeSurfaceSize(player, surface, ar);
case VIDEO_STATE_UPDATE: // 缓冲启动
case VIDEO_CACHE_READY: { // 缓冲完成最低限度
isReady = true;
mMediaPlayer.setDataSource(localUri);
mMediaPlayer.prepareAsync();
case VIDEO_CACHE_UPDATE: { // 缓冲加载
if (isError) {
mMediaPlayer.setDataSource(localUri);
mMediaPlayer.prepareAsync();
isError = false;
case VIDEO_CACHE_FINISH: { // 缓冲结束
if (isError) {
mMediaPlayer.setDataSource(localUri);
mMediaPlayer.prepareAsync();
isError = false;
super.handleMessage(msg);
protected void initializeControls() {
/* SufaceView used by VLC is a normal surface */
mSurfaceViewVlc = (SurfaceView) findViewById(R.id.player_surface_vlc);
mSurfaceHolderVlc = mSurfaceViewVlc.getHolder();
mSurfaceHolderVlc.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
mSurfaceHolderVlc.addCallback(new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
createMediaPlayer(false, localUri, mSurfaceHolderVlc);
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
mMediaPlayer.setDisplay(holder);
public void surfaceDestroyed(SurfaceHolder holder) {
destroyMediaPlayer(false);
mSurfaceViewVlc.setOnTouchListener(this);
mTextViewTime = (TextView) findViewById(R.id.player_text_position);
mSeekBarProgress = (SeekBar) findViewById(R.id.player_seekbar_progress);
mSeekBarProgress.setOnSeekBarChangeListener(this);
mTextViewLength = (TextView) findViewById(R.id.player_text_length);
mImageButtonToggleMessage = (ImageButton) findViewById(R.id.player_button_toggle_message);
mImageButtonToggleMessage.setOnClickListener(this);
mImageButtonSwitchAudio = (ImageButton) findViewById(R.id.player_button_switch_audio);
mImageButtonSwitchAudio.setOnClickListener(this);
mImageButtonSwitchSubtitle = (ImageButton) findViewById(R.id.player_button_switch_subtitle);
mImageButtonSwitchSubtitle.setOnClickListener(this);
mImageButtonPrevious = (ImageButton) findViewById(R.id.player_button_previous);
mImageButtonPrevious.setOnClickListener(this);
mImageButtonTogglePlay = (ImageButton) findViewById(R.id.player_button_toggle_play);
mImageButtonTogglePlay.setOnClickListener(this);
mImageButtonNext = (ImageButton) findViewById(R.id.player_button_next);
mImageButtonNext.setOnClickListener(this);
mImageButtonSwitchAspectRatio = (ImageButton) findViewById(R.id.player_button_switch_aspect_ratio);
mImageButtonSwitchAspectRatio.setOnClickListener(this);
mLinearLayoutControlBar = (LinearLayout) findViewById(R.id.player_control_bar);
mProgressBarPreparing = (ProgressBar) findViewById(R.id.player_prepairing);
protected void initializeData() throws IOException {
Intent intent = getIntent();
remoteUrl = intent.getStringExtra(REMOTE_URL);
if (remoteUrl == null) {
if (remoteUrl.startsWith("file:")) {
localUri = remoteU
cacheFilePath = App.getInstance().getMediaCachePath() + "cache.mp4";
File cacheFile = new File(cacheFilePath);
if (cacheFile.exists()) {
cacheFile.delete();
cacheFile.getParentFile().mkdirs();
cacheFile.createNewFile();
localUri = Uri.fromFile(cacheFile).toString();
new Thread(new Runnable() {
public void run() {
startBufferData();
}).start();
private void startBufferData() {
InputStream is = null;
FileOutputStream out = null;
out = new FileOutputStream(cacheFilePath);
if (URLUtil.isNetworkUrl(remoteUrl)) {
URL url = new URL(remoteUrl);
HttpURLConnection httpConnection = (HttpURLConnection) url
.openConnection();
is = httpConnection.getInputStream();
mediaLength = httpConnection.getContentLength();
// TODO fix this, very very bad idea
Book book = App.getInstance().getBook();
is = book.getEntryInputStream(remoteUrl);
mediaLength = book.getEntrySize(remoteUrl);
if (is == null || mediaLength == -1) {
byte[] buf = new byte[4 * 1024];
int size = 0;
int lastReadSize = 0;
mEventHandler.sendEmptyMessage(VIDEO_STATE_UPDATE);
while ((size = is.read(buf)) != -1) {
out.write(buf, 0, size);
readSize +=
} catch (IOException e) {
e.printStackTrace();
if (!isReady) {
if (readSize - lastReadSize & READY_BUFFER_LENGTH
|| readSize - lastReadSize == mediaLength) {
lastReadSize = readS
int timeCount = 0;
while (timeCount & 10) {
if (mMediaPlayer != null) {
mEventHandler
.sendEmptyMessage(VIDEO_CACHE_READY);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
timeCount++;
if (readSize - lastReadSize & CACHE_BUFFER_LENGTH
* (errorCount + 1)) {
lastReadSize = readS
mEventHandler.sendEmptyMessage(VIDEO_CACHE_UPDATE);
mEventHandler.sendEmptyMessage(VIDEO_CACHE_FINISH);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (is != null) {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
protected void resetMediaPlayer() {
int resource = -1;
/* initial status */
mMediaPlayerLoaded = false;
mTime = -1;
mLength = -1;
mCanSeek = true;
mAspectRatio = 0;
mImageButtonToggleMessage.setVisibility(View.GONE);
mImageButtonSwitchAudio.setVisibility(View.GONE);
mImageButtonSwitchSubtitle.setVisibility(View.GONE);
mImageButtonPrevious.setVisibility(View.GONE);
mImageButtonTogglePlay.setVisibility(View.VISIBLE);
resource = SystemUtility.getDrawableId("btn_play_0");
mImageButtonTogglePlay.setBackgroundResource(resource);
mImageButtonNext.setVisibility(View.GONE);
mImageButtonSwitchAspectRatio.setVisibility(View.VISIBLE);
resource = SystemUtility.getDrawableId("btn_aspect_ratio_0");
mImageButtonSwitchAspectRatio.setBackgroundResource(resource);
mLinearLayoutControlBar.setVisibility(View.GONE);
protected void selectMediaPlayer(String uri, boolean forceVlc) {
/* TODO: do this through configuration */
boolean useDefault = true;
int indexOfDot = uri.lastIndexOf('.');
if (indexOfDot != -1) {
String extension = uri.substring(indexOfDot).toLowerCase();
if (pareTo(".flv") == 0
|| pareTo(".hlv") == 0
|| pareTo(".m3u8") == 0
|| pareTo(".mkv") == 0
|| pareTo(".rm") == 0
|| pareTo(".rmvb") == 0) {
useDefault = false;
if (forceVlc) {
useDefault = false;
mSurfaceViewVlc.setVisibility(useDefault ? View.GONE : View.VISIBLE);
protected void createMediaPlayer(boolean useDefault, String uri,
SurfaceHolder holder) {
Log.d(LOGTAG, "createMediaPlayer() " + uri);
resetMediaPlayer();
mMediaPlayer = AbsMediaPlayer.getMediaPlayer(useDefault/* false */);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnInfoListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnProgressUpdateListener(this);
mMediaPlayer.setOnVideoSizeChangedListener(this);
mMediaPlayer.reset();
mMediaPlayer.setDisplay(holder);
mMediaPlayer.setDataSource(uri);
mMediaPlayer.prepareAsync();
protected void destroyMediaPlayer(boolean isDefault) {
if (!isDefault) {
mMediaPlayer.setDisplay(null);
mMediaPlayer.release();
mMediaPlayer = null;
protected void startMediaPlayer() {
if (mMediaPlayerStarted || !mMediaPlayerLoaded)
if (mMediaPlayer != null) {
mMediaPlayer.start();
mMediaPlayerStarted = true;
protected void changeSurfaceSize(AbsMediaPlayer player,
SurfaceView surface, int ar) {
int videoWidth = player.getVideoWidth();
int videoHeight = player.getVideoHeight();
if (videoWidth &= 0 || videoHeight &= 0) {
SurfaceHolder holder = surface.getHolder();
holder.setFixedSize(videoWidth, videoHeight);
int displayWidth = getWindowManager().getDefaultDisplay().getWidth();
int displayHeight = getWindowManager().getDefaultDisplay().getHeight();
int targetWidth = -1;
int targetHeight = -1;
switch (ar) {
case SURFACE_NONE: {
targetWidth = videoW
targetHeight = videoH
case SURFACE_FILL: {
case SURFACE_ORIG: {
displayWidth = videoW
displayHeight = videoH
case SURFACE_4_3: {
targetWidth = 4;
targetHeight = 3;
case SURFACE_16_9: {
targetWidth = 16;
targetHeight = 9;
case SURFACE_16_10: {
targetWidth = 16;
targetHeight = 10;
if (targetWidth & 0 && targetHeight & 0) {
double ard = (double) displayWidth / (double) displayH
double art = (double) targetWidth / (double) targetH
if (ard & art) {
displayWidth = displayHeight * targetWidth / targetH
displayHeight = displayWidth * targetHeight / targetW
LayoutParams lp = surface.getLayoutParams();
lp.width = displayW
lp.height = displayH
surface.setLayoutParams(lp);
surface.invalidate();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeEvents();
setContentView(R.layout.player);
initializeControls();
mProgressBarPreparing.setVisibility(View.VISIBLE);
initializeData();
} catch (IOException e) {
e.printStackTrace();
if (localUri == null) {
selectMediaPlayer(localUri, true);
public void onDestroy() {
super.onDestroy();
public void onStart() {
super.onStart();
public void onStop() {
super.onStop();
if (mMediaPlayer != null) {
mMediaPlayer.pause();
public boolean onTouch(View v, MotionEvent event) {
if (!mMediaPlayerLoaded) {
return true;
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
int visibility = mLinearLayoutControlBar.getVisibility();
if (visibility != View.VISIBLE) {
mLinearLayoutControlBar.setVisibility(View.VISIBLE);
mLinearLayoutControlBar.setVisibility(View.GONE);
return true;
return false;
public void onClick(View v) {
if (!mMediaPlayerLoaded)
int id = v.getId();
switch (id) {
case R.id.player_button_switch_audio: {
case R.id.player_button_switch_subtitle: {
case R.id.player_button_previous: {
case R.id.player_button_toggle_play: {
boolean playing = false;
if (mMediaPlayer != null)
playing = mMediaPlayer.isPlaying();
if (playing) {
if (mMediaPlayer != null)
mMediaPlayer.pause();
if (mMediaPlayer != null)
mMediaPlayer.start();
String name = String.format("btn_play_%d", !playing ? 1 : 0);
int resouce = SystemUtility.getDrawableId(name);
mImageButtonTogglePlay.setBackgroundResource(resouce);
case R.id.player_button_next: {
case R.id.player_button_switch_aspect_ratio: {
mAspectRatio = (mAspectRatio + 1) % SURFACE_MAX;
if (mMediaPlayer != null)
changeSurfaceSize(mMediaPlayer, mSurfaceViewVlc, mAspectRatio);
String name = String.format("btn_aspect_ratio_%d", mAspectRatio);
int resource = SystemUtility.getDrawableId(name);
mImageButtonSwitchAspectRatio.setBackgroundResource(resource);
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
/* not used */
public void onStartTrackingTouch(SeekBar seekBar) {
/* not used */
public void onStopTrackingTouch(SeekBar seekBar) {
if (!mMediaPlayerLoaded)
int id = seekBar.getId();
switch (id) {
case R.id.player_seekbar_progress: {
if (mCanSeek && mLength & 0) {
int position = seekBar.getProgress();
if (mMediaPlayer != null)
mMediaPlayer.seekTo(position);
public void onBufferingUpdate(AbsMediaPlayer mp, int percent) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_BUFFERING_UPDATE;
msg.arg1 =
mEventHandler.sendMessage(msg);
public void onCompletion(AbsMediaPlayer mp) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_COMPLETION;
mEventHandler.sendMessage(msg);
public boolean onError(AbsMediaPlayer mp, int what, int extra) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_ERROR;
msg.arg1 =
msg.arg2 =
mEventHandler.sendMessage(msg);
return true;
public boolean onInfo(AbsMediaPlayer mp, int what, int extra) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_INFO;
msg.arg1 =
msg.arg2 =
mEventHandler.sendMessage(msg);
return true;
public void onPrepared(AbsMediaPlayer mp) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_PREPARED;
mEventHandler.sendMessage(msg);
public void onProgressUpdate(AbsMediaPlayer mp, int time, int length) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_PROGRESS_UPDATE;
msg.arg1 =
msg.arg2 =
mEventHandler.sendMessage(msg);
public void onVideoSizeChangedListener(AbsMediaPlayer mp, int width,
int height) {
Message msg = new Message();
msg.what = MEDIA_PLAYER_VIDEO_SIZE_CHANGED;
msg.arg1 =
msg.arg2 =
mEventHandler.sendMessage(msg);
阅读(...) 评论()

我要回帖

更多关于 opensuse mp4 的文章

 

随机推荐