├── .gitignore ├── LICENSE ├── README.md ├── app-release.apk ├── app ├── .gitignore ├── build.gradle ├── libs │ └── armeabi-v7a │ │ ├── libijkplayer.so │ │ └── libijksdl.so ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zzh12138 │ │ └── jzvideodemo │ │ ├── CenterImageSpan.java │ │ ├── DensityUtil.java │ │ ├── MainActivity.java │ │ ├── MyApplication.java │ │ ├── adapter │ │ ├── CommentAdapter.java │ │ ├── NewsAdapter.java │ │ └── VideoListAdapter.java │ │ ├── bean │ │ ├── CommentBean.java │ │ ├── NewsBean.java │ │ └── ViewAttr.java │ │ ├── fragment │ │ ├── VideoCommentFragment.java │ │ └── VideoListFragment.java │ │ ├── itemDecoration │ │ ├── LineItemDecoration.java │ │ └── VideoListItemDecoration.java │ │ ├── player │ │ ├── JZExoPlayer.java │ │ ├── JZMediaIjkplayer.java │ │ ├── JZMediaInterface.java │ │ ├── JZMediaManager.java │ │ ├── JZMediaSystem.java │ │ ├── JZResizeTextureView.java │ │ ├── JZUserAction.java │ │ ├── JZUtils.java │ │ ├── JZVideoPlayer.java │ │ ├── JZVideoPlayerManager.java │ │ ├── JZVideoPlayerStandard.java │ │ └── SoDownloadIntentService.java │ │ └── view │ │ ├── MaskView.java │ │ ├── PlayerContainer.java │ │ └── VoiceAnimationView.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xhdpi │ ├── bg_mute_text.xml │ ├── bg_player_controller_shadow.webp │ ├── bg_player_replay.xml │ ├── bg_player_standard_seek.xml │ ├── bg_player_top_shadow.xml │ ├── bg_player_voice_shadow.webp │ ├── bg_rectangle_red_corner_4.xml │ ├── ic_launcher_background.xml │ ├── icon_comment.png │ ├── icon_left_back.png │ ├── icon_player_full_screen.webp │ ├── icon_player_loading.webp │ ├── icon_player_mute.webp │ ├── icon_player_not_mute.webp │ ├── icon_player_pause.webp │ ├── icon_player_play.webp │ ├── icon_player_replay.webp │ ├── icon_player_seek_thumb.webp │ ├── icon_praise.png │ ├── icon_share.png │ ├── logo_qq.webp │ ├── logo_sinaweibo.webp │ ├── logo_wechat.webp │ ├── logo_wechatmoments.webp │ └── player_loading.xml │ ├── drawable │ └── line.xml │ ├── layout │ ├── activity_main.xml │ ├── adapter_comment.xml │ ├── adapter_news_normal.xml │ ├── adapter_news_video.xml │ ├── adapter_video.xml │ ├── fragment_video_comment.xml │ ├── fragment_video_list.xml │ ├── layout_no_more.xml │ └── layout_player_standard.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_round.png │ └── icon_player_pause.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── ijk ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── tv │ │ └── danmaku │ │ └── ijk │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tv │ │ │ └── danmaku │ │ │ └── ijk │ │ │ └── media │ │ │ └── player │ │ │ ├── AbstractMediaPlayer.java │ │ │ ├── AndroidMediaPlayer.java │ │ │ ├── IMediaPlayer.java │ │ │ ├── ISurfaceTextureHolder.java │ │ │ ├── ISurfaceTextureHost.java │ │ │ ├── IjkLibLoader.java │ │ │ ├── IjkMediaCodecInfo.java │ │ │ ├── IjkMediaMeta.java │ │ │ ├── IjkMediaPlayer.java │ │ │ ├── IjkTimedText.java │ │ │ ├── MediaInfo.java │ │ │ ├── MediaPlayerProxy.java │ │ │ ├── TextureMediaPlayer.java │ │ │ ├── annotations │ │ │ ├── AccessedByNative.java │ │ │ └── CalledByNative.java │ │ │ ├── exceptions │ │ │ └── IjkMediaException.java │ │ │ ├── ffmpeg │ │ │ └── FFmpegApi.java │ │ │ ├── misc │ │ │ ├── AndroidMediaFormat.java │ │ │ ├── AndroidTrackInfo.java │ │ │ ├── IAndroidIO.java │ │ │ ├── IMediaDataSource.java │ │ │ ├── IMediaFormat.java │ │ │ ├── ITrackInfo.java │ │ │ ├── IjkMediaFormat.java │ │ │ └── IjkTrackInfo.java │ │ │ └── pragma │ │ │ ├── DebugLog.java │ │ │ └── Pragma.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── tv │ └── danmaku │ └── ijk │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.ap_ 3 | 4 | # Files for the ART/Dalvik VM 5 | *.dex 6 | 7 | # Java class files 8 | *.class 9 | 10 | # Generated files 11 | bin/ 12 | gen/ 13 | out/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio Navigation editor temp files 29 | .navigation/ 30 | 31 | # Android Studio captures folder 32 | captures/ 33 | 34 | # IntelliJ 35 | *.iml 36 | .idea/workspace.xml 37 | .idea/tasks.xml 38 | .idea/gradle.xml 39 | .idea/assetWizardSettings.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | .idea/caches 43 | 44 | # Keystore files 45 | # Uncomment the following line if you do not want to check your keystore files in. 46 | #*.jks 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | 51 | # Google Services (e.g. APIs or Firebase) 52 | google-services.json 53 | 54 | # Freeline 55 | freeline.py 56 | freeline/ 57 | freeline_project_description.json 58 | 59 | # fastlane 60 | fastlane/report.xml 61 | fastlane/Preview.html 62 | fastlane/screenshots 63 | fastlane/test_output 64 | fastlane/readme.md 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zzh12138 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JZVideoDemo 2 | #### VideoTranslate Demo by java 2.0 3 | ## 高仿腾讯新闻视频切换效果 4 | ###### 效果如下: 5 | ![avatar](https://github.com/zzh12138/VideoDemoJava/blob/master/m.gif) 6 | ###### [详细请戳这里](https://www.jianshu.com/p/4e21ecc90da6) 7 | ## License 8 | MIT 9 | -------------------------------------------------------------------------------- /app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app-release.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.zzh12138.jzvideodemo" 7 | minSdkVersion 15 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | ndk { 13 | abiFilters "x86", "armeabi-v7a" 14 | } 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | sourceSets { 23 | main { 24 | jniLibs.srcDirs = ['libs'] 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | implementation 'com.android.support:appcompat-v7:27.1.1' 32 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 33 | implementation 'com.google.android.exoplayer:exoplayer:2.7.1' 34 | compile project(path: ':ijk') 35 | implementation 'com.android.support:recyclerview-v7:27.1.1' 36 | implementation 'com.github.bumptech.glide:glide:4.7.1' 37 | annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' 38 | implementation 'com.jakewharton:butterknife:8.8.1' 39 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 40 | } 41 | -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libijkplayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/libs/armeabi-v7a/libijkplayer.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libijksdl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/libs/armeabi-v7a/libijksdl.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/CenterImageSpan.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.drawable.Drawable; 7 | import android.text.style.ImageSpan; 8 | 9 | /** 10 | * Created by zhangzhihao on 2018/6/10 15:11. 11 | */ 12 | 13 | public class CenterImageSpan extends ImageSpan { 14 | private static final String TAG = "CenterImageSpan"; 15 | public CenterImageSpan(Drawable d) { 16 | super(d); 17 | } 18 | 19 | public CenterImageSpan(Context context, int resourceId) { 20 | super(context, resourceId); 21 | } 22 | 23 | @Override 24 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 25 | Drawable drawable = getDrawable(); 26 | float transY =(bottom - top) / 2f - drawable.getBounds().bottom / 2f; 27 | canvas.save(); 28 | canvas.translate(x, transY); 29 | drawable.draw(canvas); 30 | canvas.restore(); 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by zhangzhihao on 2018/9/21 11:00. 7 | */ 8 | public class DensityUtil { 9 | public static int dipTopx(Context context, float dpValue) { 10 | final float scale = context.getResources().getDisplayMetrics().density; 11 | return (int) (dpValue * scale + 0.5f * (dpValue >= 0 ? 1 : -1)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.zzh12138.jzvideodemo.player.JZExoPlayer; 8 | import com.zzh12138.jzvideodemo.player.JZVideoPlayer; 9 | import com.zzh12138.jzvideodemo.player.SoDownloadIntentService; 10 | 11 | /** 12 | * Created by zhangzhihao on 2018/9/12 11:31. 13 | */ 14 | public class MyApplication extends Application { 15 | private static Context mContext; 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | mContext = getApplicationContext(); 21 | JZVideoPlayer.setMediaInterface(new JZExoPlayer()); 22 | Intent so = new Intent(mContext, SoDownloadIntentService.class); 23 | mContext.startService(so); 24 | } 25 | 26 | public static Context getInstance() { 27 | return mContext; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/adapter/CommentAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.bumptech.glide.request.RequestOptions; 14 | import com.zzh12138.jzvideodemo.R; 15 | import com.zzh12138.jzvideodemo.bean.CommentBean; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * Created by zhangzhihao on 2018/9/21 10:13. 21 | */ 22 | public class CommentAdapter extends RecyclerView.Adapter { 23 | private Context mContext; 24 | private List mList; 25 | 26 | public CommentAdapter(Context mContext, List mList) { 27 | this.mContext = mContext; 28 | this.mList = mList; 29 | } 30 | 31 | @NonNull 32 | @Override 33 | public CommentHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 34 | return new CommentHolder(LayoutInflater.from(mContext).inflate(R.layout.adapter_comment, parent, false)); 35 | } 36 | 37 | @Override 38 | public void onBindViewHolder(@NonNull CommentHolder holder, int position) { 39 | CommentBean bean = mList.get(position); 40 | holder.name.setText(bean.getUserName()); 41 | holder.content.setText(bean.getContent()); 42 | holder.praise.setText(String.valueOf(bean.getPraiseNum())); 43 | RequestOptions options = new RequestOptions().optionalCircleCrop(); 44 | Glide.with(mContext).load(R.mipmap.ic_launcher_round).apply(options).into(holder.avatar); 45 | if (bean.getParentComment() != null) { 46 | holder.parentContent.setVisibility(View.VISIBLE); 47 | StringBuilder builder = new StringBuilder(); 48 | builder.append("@") 49 | .append(bean.getParentComment().getUserName()) 50 | .append(": ") 51 | .append(bean.getParentComment().getContent()); 52 | holder.parentContent.setText(builder.toString()); 53 | } else { 54 | holder.parentContent.setVisibility(View.GONE); 55 | } 56 | holder.time.setText("2019-2-30"); 57 | } 58 | 59 | @Override 60 | public int getItemCount() { 61 | return mList == null ? 0 : mList.size(); 62 | } 63 | 64 | class CommentHolder extends RecyclerView.ViewHolder { 65 | TextView name; 66 | TextView content; 67 | TextView parentContent; 68 | TextView time; 69 | TextView praise; 70 | ImageView avatar; 71 | 72 | public CommentHolder(View itemView) { 73 | super(itemView); 74 | avatar = itemView.findViewById(R.id.adapter_comment_avatar); 75 | name = itemView.findViewById(R.id.adapter_comment_user_name); 76 | praise = itemView.findViewById(R.id.adapter_comment_praise); 77 | parentContent = itemView.findViewById(R.id.adapter_comment_parent_content); 78 | content = itemView.findViewById(R.id.adapter_comment_content); 79 | time = itemView.findViewById(R.id.adapter_comment_time); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/adapter/NewsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.bumptech.glide.Glide; 12 | import com.zzh12138.jzvideodemo.DensityUtil; 13 | import com.zzh12138.jzvideodemo.R; 14 | import com.zzh12138.jzvideodemo.bean.NewsBean; 15 | import com.zzh12138.jzvideodemo.bean.ViewAttr; 16 | import com.zzh12138.jzvideodemo.player.JZVideoPlayer; 17 | import com.zzh12138.jzvideodemo.player.JZVideoPlayerStandard; 18 | import com.zzh12138.jzvideodemo.view.PlayerContainer; 19 | 20 | import java.util.List; 21 | 22 | 23 | /** 24 | * Created by zhangzhihao on 2018/6/19 17:35. 25 | */ 26 | 27 | public class NewsAdapter extends RecyclerView.Adapter { 28 | 29 | private List mList; 30 | private Context mContext; 31 | private onVideoTitleClickListener onVideoTitleClickListener; 32 | 33 | public NewsAdapter(List mList, Context mContext) { 34 | this.mList = mList; 35 | this.mContext = mContext; 36 | } 37 | 38 | @Override 39 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 40 | View view = LayoutInflater.from(mContext).inflate(viewType, parent, false); 41 | if (viewType == R.layout.adapter_news_normal) { 42 | return new NormalHolder(view); 43 | } else { 44 | return new VideoHolder(view); 45 | } 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 50 | if (holder instanceof NormalHolder) { 51 | setNormalData((NormalHolder) holder, position); 52 | } else { 53 | setVideoData((VideoHolder) holder, position); 54 | } 55 | } 56 | 57 | private void setNormalData(NormalHolder holder, int position) { 58 | NewsBean bean = mList.get(position); 59 | holder.title.setText(bean.getTitle()); 60 | Glide.with(mContext).load(bean.getImageUrl()).into(holder.image); 61 | } 62 | 63 | private void setVideoData(final VideoHolder holder, int position) { 64 | final NewsBean bean = mList.get(position); 65 | holder.title.setText(bean.getTitle()); 66 | holder.title.setOnClickListener(new View.OnClickListener() { 67 | @Override 68 | public void onClick(View v) { 69 | if (onVideoTitleClickListener != null) { 70 | int[] location = new int[2]; 71 | holder.container.getLocationOnScreen(location); 72 | ViewAttr attr = new ViewAttr(); 73 | attr.setX(location[0]); 74 | attr.setY(location[1]); 75 | attr.setWidth(holder.container.getWidth()); 76 | attr.setHeight(holder.container.getHeight()); 77 | onVideoTitleClickListener.onTitleClick(holder.getLayoutPosition(), attr); 78 | } 79 | } 80 | }); 81 | Glide.with(mContext).load(bean.getImageUrl()).into(holder.player.coverImageView); 82 | holder.player.positionInList = position; 83 | holder.player.setUp(bean.getVideoUrl(), JZVideoPlayer.SCREEN_WINDOW_NORMAL, bean.getTitle()); 84 | holder.player.setVideoTime(bean.getDuration()) 85 | .setOnVideoPlayerContainerClickListener(new JZVideoPlayerStandard.OnVideoPlayerContainerClickListener() { 86 | @Override 87 | public void videoPlayerContainerClick() { 88 | if (onVideoTitleClickListener != null) { 89 | int[] location = new int[2]; 90 | holder.container.getLocationOnScreen(location); 91 | ViewAttr attr = new ViewAttr(); 92 | attr.setX(location[0]); 93 | attr.setY(location[1]); 94 | attr.setWidth(holder.container.getWidth()); 95 | attr.setHeight(holder.container.getHeight()); 96 | onVideoTitleClickListener.onTitleClick(holder.getLayoutPosition(), attr); 97 | } 98 | } 99 | }); 100 | } 101 | 102 | @Override 103 | public int getItemCount() { 104 | return mList == null ? 0 : mList.size(); 105 | } 106 | 107 | @Override 108 | public int getItemViewType(int position) { 109 | return mList.get(position).getType(); 110 | } 111 | 112 | public void setOnVideoTitleClickListener(NewsAdapter.onVideoTitleClickListener onVideoTitleClickListener) { 113 | this.onVideoTitleClickListener = onVideoTitleClickListener; 114 | } 115 | 116 | class NormalHolder extends RecyclerView.ViewHolder { 117 | 118 | TextView title; 119 | ImageView image; 120 | 121 | public NormalHolder(View itemView) { 122 | super(itemView); 123 | title = itemView.findViewById(R.id.adapter_news_normal_title); 124 | image = itemView.findViewById(R.id.adapter_news_normal_image); 125 | } 126 | } 127 | 128 | public class VideoHolder extends RecyclerView.ViewHolder { 129 | 130 | JZVideoPlayerStandard player; 131 | TextView title; 132 | PlayerContainer container; 133 | 134 | public VideoHolder(View itemView) { 135 | super(itemView); 136 | title = itemView.findViewById(R.id.adapter_video_title); 137 | player = itemView.findViewById(R.id.player); 138 | container = itemView.findViewById(R.id.adapter_video_container); 139 | container.setHeightRatio(9f) 140 | .setWidthRatio(16f); 141 | } 142 | } 143 | 144 | public interface onVideoTitleClickListener { 145 | void onTitleClick(int position, ViewAttr attr); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/bean/CommentBean.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.bean; 2 | 3 | /** 4 | * Created by zhangzhihao on 2018/9/21 10:30. 5 | */ 6 | public class CommentBean { 7 | private String id; 8 | private String userIcon; 9 | private String userName; 10 | private int praiseNum; 11 | private String content; 12 | private CommentBean parentComment; 13 | 14 | public String getId() { 15 | return id; 16 | } 17 | 18 | public void setId(String id) { 19 | this.id = id; 20 | } 21 | 22 | public String getUserIcon() { 23 | return userIcon; 24 | } 25 | 26 | public void setUserIcon(String userIcon) { 27 | this.userIcon = userIcon; 28 | } 29 | 30 | public String getUserName() { 31 | return userName; 32 | } 33 | 34 | public void setUserName(String userName) { 35 | this.userName = userName; 36 | } 37 | 38 | public int getPraiseNum() { 39 | return praiseNum; 40 | } 41 | 42 | public void setPraiseNum(int praiseNum) { 43 | this.praiseNum = praiseNum; 44 | } 45 | 46 | public String getContent() { 47 | return content; 48 | } 49 | 50 | public void setContent(String content) { 51 | this.content = content; 52 | } 53 | 54 | public CommentBean getParentComment() { 55 | return parentComment; 56 | } 57 | 58 | public void setParentComment(CommentBean parentComment) { 59 | this.parentComment = parentComment; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/bean/NewsBean.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by zhangzhihao on 2018/6/19 17:36. 8 | */ 9 | 10 | public class NewsBean implements Parcelable { 11 | private String title; 12 | private String imageUrl; 13 | private String videoUrl; 14 | private int type; 15 | private int commentNum; 16 | private long duration; 17 | 18 | 19 | public NewsBean() { 20 | } 21 | 22 | 23 | protected NewsBean(Parcel in) { 24 | title = in.readString(); 25 | imageUrl = in.readString(); 26 | videoUrl = in.readString(); 27 | type = in.readInt(); 28 | commentNum = in.readInt(); 29 | duration = in.readLong(); 30 | } 31 | 32 | public static final Creator CREATOR = new Creator() { 33 | @Override 34 | public NewsBean createFromParcel(Parcel in) { 35 | return new NewsBean(in); 36 | } 37 | 38 | @Override 39 | public NewsBean[] newArray(int size) { 40 | return new NewsBean[size]; 41 | } 42 | }; 43 | 44 | public int getCommentNum() { 45 | return commentNum; 46 | } 47 | 48 | public void setCommentNum(int commentNum) { 49 | this.commentNum = commentNum; 50 | } 51 | 52 | public String getTitle() { 53 | return title; 54 | } 55 | 56 | public void setTitle(String title) { 57 | this.title = title; 58 | } 59 | 60 | public String getImageUrl() { 61 | return imageUrl; 62 | } 63 | 64 | public void setImageUrl(String imageUrl) { 65 | this.imageUrl = imageUrl; 66 | } 67 | 68 | public String getVideoUrl() { 69 | return videoUrl; 70 | } 71 | 72 | public void setVideoUrl(String videoUrl) { 73 | this.videoUrl = videoUrl; 74 | } 75 | 76 | public int getType() { 77 | return type; 78 | } 79 | 80 | public void setType(int type) { 81 | this.type = type; 82 | } 83 | 84 | public long getDuration() { 85 | return duration; 86 | } 87 | 88 | public void setDuration(long duration) { 89 | this.duration = duration; 90 | } 91 | 92 | @Override 93 | public int describeContents() { 94 | return 0; 95 | } 96 | 97 | @Override 98 | public void writeToParcel(Parcel dest, int flags) { 99 | dest.writeString(title); 100 | dest.writeString(imageUrl); 101 | dest.writeString(videoUrl); 102 | dest.writeInt(type); 103 | dest.writeInt(commentNum); 104 | dest.writeLong(duration); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/bean/ViewAttr.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by zhangzhihao on 2018/6/21 11:52. 8 | */ 9 | 10 | public class ViewAttr implements Parcelable { 11 | private int x; 12 | private int y; 13 | private int width; 14 | private int height; 15 | 16 | public ViewAttr() { 17 | } 18 | 19 | protected ViewAttr(Parcel in) { 20 | x = in.readInt(); 21 | y = in.readInt(); 22 | width = in.readInt(); 23 | height = in.readInt(); 24 | } 25 | 26 | public static final Creator CREATOR = new Creator() { 27 | @Override 28 | public ViewAttr createFromParcel(Parcel in) { 29 | return new ViewAttr(in); 30 | } 31 | 32 | @Override 33 | public ViewAttr[] newArray(int size) { 34 | return new ViewAttr[size]; 35 | } 36 | }; 37 | 38 | public int getX() { 39 | return x; 40 | } 41 | 42 | public void setX(int x) { 43 | this.x = x; 44 | } 45 | 46 | public int getY() { 47 | return y; 48 | } 49 | 50 | public void setY(int y) { 51 | this.y = y; 52 | } 53 | 54 | public int getWidth() { 55 | return width; 56 | } 57 | 58 | public void setWidth(int width) { 59 | this.width = width; 60 | } 61 | 62 | public int getHeight() { 63 | return height; 64 | } 65 | 66 | public void setHeight(int height) { 67 | this.height = height; 68 | } 69 | 70 | @Override 71 | public int describeContents() { 72 | return 0; 73 | } 74 | 75 | @Override 76 | public void writeToParcel(Parcel dest, int flags) { 77 | dest.writeInt(x); 78 | dest.writeInt(y); 79 | dest.writeInt(width); 80 | dest.writeInt(height); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/fragment/VideoCommentFragment.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.fragment; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.ObjectAnimator; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.ViewTreeObserver; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import com.zzh12138.jzvideodemo.DensityUtil; 19 | import com.zzh12138.jzvideodemo.R; 20 | import com.zzh12138.jzvideodemo.adapter.CommentAdapter; 21 | import com.zzh12138.jzvideodemo.bean.CommentBean; 22 | import com.zzh12138.jzvideodemo.bean.NewsBean; 23 | import com.zzh12138.jzvideodemo.bean.ViewAttr; 24 | import com.zzh12138.jzvideodemo.itemDecoration.LineItemDecoration; 25 | import com.zzh12138.jzvideodemo.player.JZVideoPlayerManager; 26 | import com.zzh12138.jzvideodemo.view.PlayerContainer; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | import butterknife.BindView; 32 | import butterknife.ButterKnife; 33 | import butterknife.Unbinder; 34 | 35 | /** 36 | * Created by zhangzhihao on 2018/9/12 17:59. 37 | */ 38 | public class VideoCommentFragment extends Fragment { 39 | private static final String TAG = "VideoCommentFragment"; 40 | 41 | public static final long DURATION = 300; 42 | @BindView(R.id.container) 43 | PlayerContainer container; 44 | @BindView(R.id.close) 45 | TextView close; 46 | @BindView(R.id.recycler) 47 | RecyclerView recycler; 48 | @BindView(R.id.root) 49 | LinearLayout root; 50 | Unbinder unbinder; 51 | 52 | 53 | private NewsBean mNews; 54 | private ViewAttr mAttr; 55 | private int[] location; 56 | private List mList; 57 | private CommentAdapter mAdapter; 58 | private OnCloseClickListener onCloseClickListener; 59 | private ObjectAnimator animator; 60 | private boolean isChanging; 61 | 62 | 63 | @Nullable 64 | @Override 65 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 66 | View mView = inflater.inflate(R.layout.fragment_video_comment, container, false); 67 | unbinder = ButterKnife.bind(this, mView); 68 | initData(); 69 | animate(); 70 | initRecyclerView(); 71 | close.setOnClickListener(new View.OnClickListener() { 72 | @Override 73 | public void onClick(View v) { 74 | if (!isChanging) { 75 | isChanging = true; 76 | if (onCloseClickListener != null) { 77 | onCloseClickListener.closeCommentFragment(); 78 | close.postDelayed(new Runnable() { 79 | @Override 80 | public void run() { 81 | isChanging = false; 82 | } 83 | }, 250); 84 | } 85 | } 86 | } 87 | }); 88 | return mView; 89 | } 90 | 91 | private void initData() { 92 | location = new int[2]; 93 | mList = new ArrayList<>(20); 94 | for (int i = 0; i < 20; i++) { 95 | CommentBean bean = new CommentBean(); 96 | bean.setId(String.valueOf(i)); 97 | bean.setUserName("我就是个开发仔" + i); 98 | bean.setContent("大佬不要再秀了,bug仔学不动啦......"); 99 | bean.setPraiseNum(12138); 100 | if ((i & 1) == 0) { 101 | bean.setParentComment(null); 102 | } else { 103 | CommentBean parent = new CommentBean(); 104 | parent.setId(String.valueOf(100 + i)); 105 | parent.setUserName("大佬" + (100 + i)); 106 | parent.setContent("太简单了吧,hhhhhh"); 107 | bean.setParentComment(parent); 108 | } 109 | mList.add(bean); 110 | } 111 | container.setWidthRatio(16f) 112 | .setHeightRatio(9f); 113 | } 114 | 115 | private void animate() { 116 | animator = ObjectAnimator.ofInt(root, "backgroundColor", 0x00000000, 0xff000000); 117 | animator.setEvaluator(new ArgbEvaluator()); 118 | animator.setDuration(DURATION); 119 | animator.start(); 120 | container.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 121 | @Override 122 | public boolean onPreDraw() { 123 | container.getViewTreeObserver().removeOnPreDrawListener(this); 124 | container.getLocationOnScreen(location); 125 | container.setTranslationY(mAttr.getY() - location[1]); 126 | container.animate().translationY(0).setDuration(DURATION); 127 | JZVideoPlayerManager.getCurrentJzvd().attachToContainer(container); 128 | container.postDelayed(new Runnable() { 129 | @Override 130 | public void run() { 131 | if (close != null) { 132 | close.setVisibility(View.VISIBLE); 133 | } 134 | if (recycler != null) { 135 | recycler.setVisibility(View.VISIBLE); 136 | } 137 | } 138 | }, DURATION); 139 | return true; 140 | } 141 | }); 142 | } 143 | 144 | private void initRecyclerView() { 145 | mAdapter = new CommentAdapter(getContext(), mList); 146 | recycler.setLayoutManager(new LinearLayoutManager(getContext())); 147 | recycler.addItemDecoration(new LineItemDecoration(getContext(), R.drawable.line, DensityUtil.dipTopx(getContext(), 55) 148 | , DensityUtil.dipTopx(getContext(), 15))); 149 | recycler.setAdapter(mAdapter); 150 | } 151 | 152 | public void closeCommentFragment() { 153 | recycler.animate().alpha(0).setDuration(DURATION); 154 | close.animate().alpha(0).setDuration(DURATION); 155 | container.animate().translationY(mAttr.getY() - location[1]).setDuration(DURATION); 156 | animator.reverse(); 157 | } 158 | 159 | @Override 160 | public void onDestroyView() { 161 | super.onDestroyView(); 162 | unbinder.unbind(); 163 | } 164 | 165 | interface OnCloseClickListener { 166 | void closeCommentFragment(); 167 | } 168 | 169 | public void setOnCloseClickListener(OnCloseClickListener onCloseClickListener) { 170 | this.onCloseClickListener = onCloseClickListener; 171 | } 172 | 173 | public void setNews(NewsBean mNews) { 174 | this.mNews = mNews; 175 | } 176 | 177 | public void setAttr(ViewAttr mAttr) { 178 | this.mAttr = mAttr; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/itemDecoration/LineItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.itemDecoration; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Rect; 6 | import android.graphics.drawable.Drawable; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.view.View; 9 | 10 | /** 11 | * Created by zhangzhihao on 2018/9/12 17:28. 12 | */ 13 | public class LineItemDecoration extends RecyclerView.ItemDecoration { 14 | 15 | private Context mContext; 16 | private Drawable mDrawable; 17 | private int paddingLeft, paddingRight; 18 | 19 | public LineItemDecoration(Context mContext, int drawableId) { 20 | this.mContext = mContext; 21 | mDrawable = mContext.getResources().getDrawable(drawableId); 22 | } 23 | 24 | public LineItemDecoration(Context mContext, int drawableId, int paddingLeft, int paddingRight) { 25 | this.mContext = mContext; 26 | mDrawable = mContext.getResources().getDrawable(drawableId); 27 | this.paddingLeft = paddingLeft; 28 | this.paddingRight = paddingRight; 29 | } 30 | 31 | @Override 32 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 33 | super.onDraw(c, parent, state); 34 | int count = parent.getChildCount(); 35 | int left = parent.getPaddingLeft(); 36 | int right = parent.getWidth() - parent.getPaddingRight(); 37 | for (int i = 0; i < count - 1; i++) { 38 | View view = parent.getChildAt(i); 39 | RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); 40 | int top = view.getBottom() + params.bottomMargin; 41 | int bottom = top + mDrawable.getIntrinsicHeight(); 42 | mDrawable.setBounds(left+paddingLeft, top, right-paddingRight, bottom); 43 | mDrawable.draw(c); 44 | } 45 | } 46 | 47 | @Override 48 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 49 | super.getItemOffsets(outRect, view, parent, state); 50 | int position = parent.getChildAdapterPosition(view); 51 | int count = parent.getChildCount(); 52 | if (position < count - 1) { 53 | outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/itemDecoration/VideoListItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.itemDecoration; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Rect; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by zhangzhihao on 2018/9/17 16:42. 10 | */ 11 | public class VideoListItemDecoration extends RecyclerView.ItemDecoration { 12 | @Override 13 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 14 | super.getItemOffsets(outRect, view, parent, state); 15 | } 16 | 17 | @Override 18 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 19 | super.onDraw(c, parent, state); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZMediaIjkplayer.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | 4 | import android.media.AudioManager; 5 | import android.media.MediaPlayer; 6 | import android.util.Log; 7 | import android.view.Surface; 8 | 9 | import com.zzh12138.jzvideodemo.MyApplication; 10 | 11 | import java.io.IOException; 12 | 13 | import tv.danmaku.ijk.media.player.IMediaPlayer; 14 | import tv.danmaku.ijk.media.player.IjkMediaPlayer; 15 | import tv.danmaku.ijk.media.player.IjkTimedText; 16 | 17 | 18 | /** 19 | * Created by Nathen on 2017/11/18. 20 | */ 21 | 22 | public class JZMediaIjkplayer extends JZMediaInterface implements IMediaPlayer.OnPreparedListener, IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnTimedTextListener { 23 | private static final String TAG = "JZMediaIjkplayer"; 24 | IjkMediaPlayer ijkMediaPlayer; 25 | 26 | @Override 27 | public void start() { 28 | ijkMediaPlayer.start(); 29 | } 30 | 31 | @Override 32 | public void prepare() { 33 | Log.e(TAG, "prepare: "); 34 | ijkMediaPlayer = new IjkMediaPlayer(MyApplication.getInstance()); 35 | 36 | 37 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); 38 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0); 39 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32); 40 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1); 41 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); 42 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0); 43 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); 44 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 1024 * 1024); 45 | 46 | ijkMediaPlayer.setOnPreparedListener(JZMediaIjkplayer.this); 47 | ijkMediaPlayer.setOnVideoSizeChangedListener(JZMediaIjkplayer.this); 48 | ijkMediaPlayer.setOnCompletionListener(JZMediaIjkplayer.this); 49 | ijkMediaPlayer.setOnErrorListener(JZMediaIjkplayer.this); 50 | ijkMediaPlayer.setOnInfoListener(JZMediaIjkplayer.this); 51 | ijkMediaPlayer.setOnBufferingUpdateListener(JZMediaIjkplayer.this); 52 | ijkMediaPlayer.setOnSeekCompleteListener(JZMediaIjkplayer.this); 53 | ijkMediaPlayer.setOnTimedTextListener(JZMediaIjkplayer.this); 54 | ijkMediaPlayer.setVolume(0f, 0f); 55 | 56 | try { 57 | ijkMediaPlayer.setDataSource(currentDataSource.toString()); 58 | ijkMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 59 | ijkMediaPlayer.setScreenOnWhilePlaying(true); 60 | ijkMediaPlayer.prepareAsync(); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | @Override 67 | public void pause() { 68 | ijkMediaPlayer.pause(); 69 | } 70 | 71 | @Override 72 | public boolean isPlaying() { 73 | return ijkMediaPlayer.isPlaying(); 74 | } 75 | 76 | @Override 77 | public void seekTo(long time) { 78 | ijkMediaPlayer.seekTo(time); 79 | } 80 | 81 | @Override 82 | public void release() { 83 | if (ijkMediaPlayer != null) 84 | ijkMediaPlayer.release(); 85 | } 86 | 87 | @Override 88 | public long getCurrentPosition() { 89 | return ijkMediaPlayer.getCurrentPosition(); 90 | } 91 | 92 | @Override 93 | public long getDuration() { 94 | return ijkMediaPlayer.getDuration(); 95 | } 96 | 97 | @Override 98 | public void setSurface(Surface surface) { 99 | ijkMediaPlayer.setSurface(surface); 100 | } 101 | 102 | @Override 103 | public void setVolume(float leftVolume, float rightVolume) { 104 | ijkMediaPlayer.setVolume(leftVolume, rightVolume); 105 | } 106 | 107 | @Override 108 | public void onPrepared(IMediaPlayer iMediaPlayer) { 109 | ijkMediaPlayer.start(); 110 | if (currentDataSource.toString().toLowerCase().contains("mp3")) { 111 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 112 | @Override 113 | public void run() { 114 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 115 | JZVideoPlayerManager.getCurrentJzvd().onPrepared(); 116 | } 117 | } 118 | }); 119 | } 120 | } 121 | 122 | @Override 123 | public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) { 124 | JZMediaManager.instance().currentVideoWidth = iMediaPlayer.getVideoWidth(); 125 | JZMediaManager.instance().currentVideoHeight = iMediaPlayer.getVideoHeight(); 126 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 127 | @Override 128 | public void run() { 129 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 130 | JZVideoPlayerManager.getCurrentJzvd().onVideoSizeChanged(); 131 | } 132 | } 133 | }); 134 | } 135 | 136 | @Override 137 | public void onCompletion(IMediaPlayer iMediaPlayer) { 138 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 139 | @Override 140 | public void run() { 141 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 142 | JZVideoPlayerManager.getCurrentJzvd().onAutoCompletion(); 143 | } 144 | } 145 | }); 146 | } 147 | 148 | @Override 149 | public boolean onError(IMediaPlayer iMediaPlayer, final int what, final int extra) { 150 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 151 | @Override 152 | public void run() { 153 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 154 | JZVideoPlayerManager.getCurrentJzvd().onError(what, extra); 155 | } 156 | } 157 | }); 158 | return true; 159 | } 160 | 161 | @Override 162 | public boolean onInfo(IMediaPlayer iMediaPlayer, final int what, final int extra) { 163 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 164 | @Override 165 | public void run() { 166 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 167 | if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 168 | JZVideoPlayerManager.getCurrentJzvd().onPrepared(); 169 | } else { 170 | JZVideoPlayerManager.getCurrentJzvd().onInfo(what, extra); 171 | } 172 | } 173 | } 174 | }); 175 | return false; 176 | } 177 | 178 | @Override 179 | public void onBufferingUpdate(IMediaPlayer iMediaPlayer, final int percent) { 180 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 181 | @Override 182 | public void run() { 183 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 184 | JZVideoPlayerManager.getCurrentJzvd().setBufferProgress(percent); 185 | } 186 | } 187 | }); 188 | } 189 | 190 | @Override 191 | public void onSeekComplete(IMediaPlayer iMediaPlayer) { 192 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 193 | @Override 194 | public void run() { 195 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 196 | JZVideoPlayerManager.getCurrentJzvd().onSeekComplete(); 197 | } 198 | } 199 | }); 200 | } 201 | 202 | @Override 203 | public void onTimedText(IMediaPlayer iMediaPlayer, IjkTimedText ijkTimedText) { 204 | 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZMediaInterface.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.view.Surface; 4 | 5 | /** 6 | * Created by Nathen on 2017/11/7. 7 | * 自定义播放器 8 | */ 9 | public abstract class JZMediaInterface { 10 | 11 | public Object currentDataSource;//正在播放的当前url或uri 12 | /** 13 | * 第一个是url的map 14 | * 第二个是loop 15 | * 第三个是header 16 | * 第四个是context 17 | */ 18 | public Object[] dataSourceObjects;//包含了地址的map(多分辨率用),context,loop,header等 19 | 20 | public abstract void start(); 21 | 22 | public abstract void prepare(); 23 | 24 | public abstract void pause(); 25 | 26 | public abstract boolean isPlaying(); 27 | 28 | public abstract void seekTo(long time); 29 | 30 | public abstract void release(); 31 | 32 | public abstract long getCurrentPosition(); 33 | 34 | public abstract long getDuration(); 35 | 36 | public abstract void setSurface(Surface surface); 37 | 38 | public abstract void setVolume(float leftVolume, float rightVolume); 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZMediaManager.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.graphics.SurfaceTexture; 4 | import android.os.Handler; 5 | import android.os.HandlerThread; 6 | import android.os.Looper; 7 | import android.os.Message; 8 | import android.util.Log; 9 | import android.view.Surface; 10 | import android.view.TextureView; 11 | import android.widget.Toast; 12 | 13 | import com.zzh12138.jzvideodemo.MyApplication; 14 | 15 | 16 | /** 17 | * 这个类用来和jzvd互相调用,当jzvd需要调用Media的时候调用这个类,当MediaPlayer有回调的时候,通过这个类回调JZVD 18 | * Created by Nathen on 2017/11/18. 19 | */ 20 | public class JZMediaManager implements TextureView.SurfaceTextureListener { 21 | 22 | public static final String TAG = "JiaoZiVideoPlayer"; 23 | public static final int HANDLER_PREPARE = 0; 24 | public static final int HANDLER_RELEASE = 2; 25 | 26 | public static JZResizeTextureView textureView; 27 | public static SurfaceTexture savedSurfaceTexture; 28 | public static Surface surface; 29 | public static JZMediaManager jzMediaManager; 30 | public int positionInList = -1; 31 | public JZMediaInterface jzMediaInterface; 32 | public int currentVideoWidth = 0; 33 | public int currentVideoHeight = 0; 34 | 35 | public HandlerThread mMediaHandlerThread; 36 | public MediaHandler mMediaHandler; 37 | public Handler mainThreadHandler; 38 | public static boolean isWiFi; 39 | 40 | public JZMediaManager() { 41 | mMediaHandlerThread = new HandlerThread(TAG); 42 | mMediaHandlerThread.start(); 43 | mMediaHandler = new MediaHandler(mMediaHandlerThread.getLooper()); 44 | mainThreadHandler = new Handler(); 45 | if (jzMediaInterface == null) 46 | jzMediaInterface = new JZMediaSystem(); 47 | 48 | } 49 | 50 | public static JZMediaManager instance() { 51 | if (jzMediaManager == null) { 52 | jzMediaManager = new JZMediaManager(); 53 | } 54 | return jzMediaManager; 55 | } 56 | 57 | public static Object[] getDataSource() { 58 | return instance().jzMediaInterface.dataSourceObjects; 59 | } 60 | 61 | //这几个方法是不是多余了,为了不让其他地方动MediaInterface的方法 62 | public static void setDataSource(Object[] dataSourceObjects) { 63 | instance().jzMediaInterface.dataSourceObjects = dataSourceObjects; 64 | } 65 | 66 | //正在播放的url或者uri 67 | public static Object getCurrentDataSource() { 68 | return instance().jzMediaInterface.currentDataSource; 69 | } 70 | 71 | public static void setCurrentDataSource(Object currentDataSource) { 72 | instance().jzMediaInterface.currentDataSource = currentDataSource; 73 | } 74 | 75 | public static long getCurrentPosition() { 76 | return instance().jzMediaInterface.getCurrentPosition(); 77 | } 78 | 79 | public static long getDuration() { 80 | return instance().jzMediaInterface.getDuration(); 81 | } 82 | 83 | public static void seekTo(long time) { 84 | instance().jzMediaInterface.seekTo(time); 85 | } 86 | 87 | public static void pause() { 88 | instance().jzMediaInterface.pause(); 89 | } 90 | 91 | public static void start() { 92 | instance().jzMediaInterface.start(); 93 | } 94 | 95 | public static boolean isPlaying() { 96 | return instance().jzMediaInterface.isPlaying(); 97 | } 98 | 99 | public void releaseMediaPlayer() { 100 | mMediaHandler.removeCallbacksAndMessages(null); 101 | Message msg = new Message(); 102 | msg.what = HANDLER_RELEASE; 103 | mMediaHandler.sendMessage(msg); 104 | } 105 | 106 | public void prepare() { 107 | releaseMediaPlayer(); 108 | Message msg = new Message(); 109 | msg.what = HANDLER_PREPARE; 110 | mMediaHandler.sendMessage(msg); 111 | } 112 | 113 | @Override 114 | public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { 115 | if (JZVideoPlayerManager.getCurrentJzvd() == null) return; 116 | Log.i(TAG, "onSurfaceTextureAvailable [" + JZVideoPlayerManager.getCurrentJzvd().hashCode() + "] "); 117 | if (savedSurfaceTexture == null) { 118 | savedSurfaceTexture = surfaceTexture; 119 | prepare(); 120 | } else { 121 | textureView.setSurfaceTexture(savedSurfaceTexture); 122 | } 123 | } 124 | 125 | @Override 126 | public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { 127 | 128 | } 129 | 130 | @Override 131 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { 132 | return savedSurfaceTexture == null; 133 | } 134 | 135 | @Override 136 | public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { 137 | 138 | } 139 | 140 | 141 | public class MediaHandler extends Handler { 142 | public MediaHandler(Looper looper) { 143 | super(looper); 144 | } 145 | 146 | @Override 147 | public void handleMessage(Message msg) { 148 | super.handleMessage(msg); 149 | switch (msg.what) { 150 | case HANDLER_PREPARE: 151 | currentVideoWidth = 0; 152 | currentVideoHeight = 0; 153 | try { 154 | jzMediaInterface.prepare(); 155 | } catch (Throwable e) { 156 | e.printStackTrace(); 157 | Object dataSource = JZMediaManager.getCurrentDataSource(); 158 | Log.e(TAG, "handleMessage: " + e.getMessage()); 159 | Toast.makeText(MyApplication.getInstance(), "so error", Toast.LENGTH_SHORT).show(); 160 | JZVideoPlayer.setMediaInterface(new JZExoPlayer()); 161 | jzMediaInterface.currentDataSource = dataSource; 162 | jzMediaInterface.prepare(); 163 | } 164 | 165 | if (savedSurfaceTexture != null) { 166 | if (surface != null) { 167 | surface.release(); 168 | } 169 | surface = new Surface(savedSurfaceTexture); 170 | jzMediaInterface.setSurface(surface); 171 | } 172 | break; 173 | case HANDLER_RELEASE: 174 | jzMediaInterface.release(); 175 | break; 176 | default: 177 | break; 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZMediaSystem.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.content.pm.ActivityInfo; 4 | import android.media.AudioManager; 5 | import android.media.MediaMetadataRetriever; 6 | import android.media.MediaPlayer; 7 | import android.util.Log; 8 | import android.view.Surface; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.Map; 12 | 13 | import static com.zzh12138.jzvideodemo.player.JZVideoPlayer.SCREEN_WINDOW_FULLSCREEN; 14 | 15 | 16 | /** 17 | * Created by Nathen on 2017/11/8. 18 | * 实现系统的播放引擎 19 | */ 20 | public class JZMediaSystem extends JZMediaInterface implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener, MediaPlayer.OnVideoSizeChangedListener { 21 | public MediaPlayer mediaPlayer; 22 | 23 | @Override 24 | public void start() { 25 | mediaPlayer.start(); 26 | } 27 | 28 | @Override 29 | public void prepare() { 30 | try { 31 | mediaPlayer = new MediaPlayer(); 32 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 33 | if (dataSourceObjects.length > 1) { 34 | mediaPlayer.setLooping((boolean) dataSourceObjects[1]); 35 | } 36 | mediaPlayer.setOnPreparedListener(JZMediaSystem.this); 37 | mediaPlayer.setOnCompletionListener(JZMediaSystem.this); 38 | mediaPlayer.setOnBufferingUpdateListener(JZMediaSystem.this); 39 | mediaPlayer.setScreenOnWhilePlaying(true); 40 | mediaPlayer.setOnSeekCompleteListener(JZMediaSystem.this); 41 | mediaPlayer.setOnErrorListener(JZMediaSystem.this); 42 | mediaPlayer.setOnInfoListener(JZMediaSystem.this); 43 | mediaPlayer.setOnVideoSizeChangedListener(JZMediaSystem.this); 44 | mediaPlayer.setVolume(0f, 0f); 45 | Class clazz = MediaPlayer.class; 46 | Method method = clazz.getDeclaredMethod("setDataSource", String.class, Map.class); 47 | if (dataSourceObjects.length > 2) { 48 | method.invoke(mediaPlayer, currentDataSource.toString(), dataSourceObjects[2]); 49 | } else { 50 | method.invoke(mediaPlayer, currentDataSource.toString(), null); 51 | } 52 | mediaPlayer.prepareAsync(); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | @Override 59 | public void pause() { 60 | mediaPlayer.pause(); 61 | } 62 | 63 | @Override 64 | public boolean isPlaying() { 65 | return mediaPlayer != null && mediaPlayer.isPlaying(); 66 | } 67 | 68 | @Override 69 | public void seekTo(long time) { 70 | try { 71 | mediaPlayer.seekTo((int) time); 72 | } catch (IllegalStateException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | 77 | @Override 78 | public void release() { 79 | if (mediaPlayer != null) 80 | mediaPlayer.release(); 81 | } 82 | 83 | @Override 84 | public long getCurrentPosition() { 85 | if (mediaPlayer != null) { 86 | return mediaPlayer.getCurrentPosition(); 87 | } else { 88 | return 0; 89 | } 90 | } 91 | 92 | @Override 93 | public long getDuration() { 94 | if (mediaPlayer != null) { 95 | return mediaPlayer.getDuration(); 96 | } else { 97 | return 0; 98 | } 99 | } 100 | 101 | @Override 102 | public void setSurface(Surface surface) { 103 | mediaPlayer.setSurface(surface); 104 | } 105 | 106 | @Override 107 | public void setVolume(float leftVolume, float rightVolume) { 108 | mediaPlayer.setVolume(leftVolume, rightVolume); 109 | } 110 | 111 | @Override 112 | public void onPrepared(MediaPlayer mediaPlayer) { 113 | mediaPlayer.start(); 114 | if (currentDataSource.toString().toLowerCase().contains("mp3") || 115 | currentDataSource.toString().toLowerCase().contains("wav")) { 116 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 117 | @Override 118 | public void run() { 119 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 120 | JZVideoPlayerManager.getCurrentJzvd().onPrepared(); 121 | } 122 | } 123 | }); 124 | } 125 | } 126 | 127 | @Override 128 | public void onCompletion(MediaPlayer mediaPlayer) { 129 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 130 | @Override 131 | public void run() { 132 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 133 | JZVideoPlayerManager.getCurrentJzvd().onAutoCompletion(); 134 | } 135 | } 136 | }); 137 | } 138 | 139 | @Override 140 | public void onBufferingUpdate(MediaPlayer mediaPlayer, final int percent) { 141 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 142 | @Override 143 | public void run() { 144 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 145 | JZVideoPlayerManager.getCurrentJzvd().setBufferProgress(percent); 146 | } 147 | } 148 | }); 149 | } 150 | 151 | @Override 152 | public void onSeekComplete(MediaPlayer mediaPlayer) { 153 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 154 | @Override 155 | public void run() { 156 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 157 | JZVideoPlayerManager.getCurrentJzvd().onSeekComplete(); 158 | } 159 | } 160 | }); 161 | } 162 | 163 | @Override 164 | public boolean onError(MediaPlayer mediaPlayer, final int what, final int extra) { 165 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 166 | @Override 167 | public void run() { 168 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 169 | JZVideoPlayerManager.getCurrentJzvd().onError(what, extra); 170 | } 171 | } 172 | }); 173 | return true; 174 | } 175 | 176 | @Override 177 | public boolean onInfo(MediaPlayer mediaPlayer, final int what, final int extra) { 178 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 179 | @Override 180 | public void run() { 181 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 182 | if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 183 | if (JZVideoPlayerManager.getCurrentJzvd().currentState == JZVideoPlayer.CURRENT_STATE_PREPARING 184 | || JZVideoPlayerManager.getCurrentJzvd().currentState == JZVideoPlayer.CURRENT_STATE_PREPARING_CHANGING_URL) { 185 | JZVideoPlayerManager.getCurrentJzvd().onPrepared(); 186 | } 187 | } else { 188 | JZVideoPlayerManager.getCurrentJzvd().onInfo(what, extra); 189 | } 190 | } 191 | } 192 | }); 193 | return false; 194 | } 195 | 196 | @Override 197 | public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) { 198 | JZMediaManager.instance().currentVideoWidth = width; 199 | JZMediaManager.instance().currentVideoHeight = height; 200 | if (JZVideoPlayerManager.getCurrentJzvd().currentScreen == SCREEN_WINDOW_FULLSCREEN) { 201 | if (JZMediaManager.instance().currentVideoWidth > JZMediaManager.instance().currentVideoHeight) { 202 | JZUtils.setRequestedOrientation(JZVideoPlayerManager.getCurrentJzvd().getContext(), ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 203 | } else { 204 | JZUtils.setRequestedOrientation(JZVideoPlayerManager.getCurrentJzvd().getContext(), ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 205 | } 206 | } 207 | JZMediaManager.instance().mainThreadHandler.post(new Runnable() { 208 | @Override 209 | public void run() { 210 | if (JZVideoPlayerManager.getCurrentJzvd() != null) { 211 | JZVideoPlayerManager.getCurrentJzvd().onVideoSizeChanged(); 212 | } 213 | } 214 | }); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZResizeTextureView.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.TextureView; 7 | import android.view.View; 8 | 9 | 10 | 11 | /** 12 | *

参照Android系统的VideoView的onMeasure方法 13 | *
注意!relativelayout中无法全屏,要嵌套一个linearlayout

14 | *

Referring Android system Video View of onMeasure method 15 | *
NOTE! Can not fullscreen relativelayout, to nest a linearlayout

16 | * Created by Nathen 17 | * On 2016/06/02 00:01 18 | */ 19 | public class JZResizeTextureView extends TextureView { 20 | protected static final String TAG = "JZResizeTextureView"; 21 | 22 | public int currentVideoWidth = 0; 23 | public int currentVideoHeight = 0; 24 | 25 | public JZResizeTextureView(Context context) { 26 | super(context); 27 | currentVideoWidth = 0; 28 | currentVideoHeight = 0; 29 | } 30 | 31 | public JZResizeTextureView(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | currentVideoWidth = 0; 34 | currentVideoHeight = 0; 35 | } 36 | 37 | public void setVideoSize(int currentVideoWidth, int currentVideoHeight) { 38 | if (this.currentVideoWidth != currentVideoWidth || this.currentVideoHeight != currentVideoHeight) { 39 | this.currentVideoWidth = currentVideoWidth; 40 | this.currentVideoHeight = currentVideoHeight; 41 | requestLayout(); 42 | } 43 | } 44 | 45 | @Override 46 | public void setRotation(float rotation) { 47 | if (rotation != getRotation()) { 48 | super.setRotation(rotation); 49 | requestLayout(); 50 | } 51 | } 52 | 53 | @Override 54 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 55 | Log.i(TAG, "onMeasure " + " [" + this.hashCode() + "] "); 56 | int viewRotation = (int) getRotation(); 57 | int videoWidth = currentVideoWidth; 58 | int videoHeight = currentVideoHeight; 59 | 60 | 61 | 62 | int parentHeight = ((View) getParent()).getMeasuredHeight(); 63 | int parentWidth = ((View) getParent()).getMeasuredWidth(); 64 | if (parentWidth != 0 && parentHeight != 0 && videoWidth != 0 && videoHeight != 0) { 65 | if (JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE == JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT) { 66 | if (viewRotation == 90 || viewRotation == 270) { 67 | int tempSize = parentWidth; 68 | parentWidth = parentHeight; 69 | parentHeight = tempSize; 70 | } 71 | /**强制充满**/ 72 | videoHeight = videoWidth * parentHeight / parentWidth; 73 | } 74 | } 75 | 76 | // 如果判断成立,则说明显示的TextureView和本身的位置是有90度的旋转的,所以需要交换宽高参数。 77 | if (viewRotation == 90 || viewRotation == 270) { 78 | int tempMeasureSpec = widthMeasureSpec; 79 | widthMeasureSpec = heightMeasureSpec; 80 | heightMeasureSpec = tempMeasureSpec; 81 | } 82 | 83 | int width = getDefaultSize(videoWidth, widthMeasureSpec); 84 | int height = getDefaultSize(videoHeight, heightMeasureSpec); 85 | if (videoWidth > 0 && videoHeight > 0) { 86 | 87 | int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 88 | int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 89 | int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 90 | int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 91 | 92 | Log.i(TAG, "widthMeasureSpec [" + MeasureSpec.toString(widthMeasureSpec) + "]"); 93 | Log.i(TAG, "heightMeasureSpec [" + MeasureSpec.toString(heightMeasureSpec) + "]"); 94 | 95 | if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { 96 | // the size is fixed 97 | width = widthSpecSize; 98 | height = heightSpecSize; 99 | // for compatibility, we adjust size based on aspect ratio 100 | if (videoWidth * height < width * videoHeight) { 101 | width = height * videoWidth / videoHeight; 102 | } else if (videoWidth * height > width * videoHeight) { 103 | height = width * videoHeight / videoWidth; 104 | } 105 | } else if (widthSpecMode == MeasureSpec.EXACTLY) { 106 | // only the width is fixed, adjust the height to match aspect ratio if possible 107 | width = widthSpecSize; 108 | height = width * videoHeight / videoWidth; 109 | if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { 110 | // couldn't match aspect ratio within the constraints 111 | height = heightSpecSize; 112 | width = height * videoWidth / videoHeight; 113 | } 114 | } else if (heightSpecMode == MeasureSpec.EXACTLY) { 115 | // only the height is fixed, adjust the width to match aspect ratio if possible 116 | height = heightSpecSize; 117 | width = height * videoWidth / videoHeight; 118 | if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { 119 | // couldn't match aspect ratio within the constraints 120 | width = widthSpecSize; 121 | height = width * videoHeight / videoWidth; 122 | } 123 | } else { 124 | // neither the width nor the height are fixed, try to use actual video size 125 | width = videoWidth; 126 | height = videoHeight; 127 | if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { 128 | // too tall, decrease both width and height 129 | height = heightSpecSize; 130 | width = height * videoWidth / videoHeight; 131 | } 132 | if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { 133 | // too wide, decrease both width and height 134 | width = widthSpecSize; 135 | height = width * videoHeight / videoWidth; 136 | } 137 | } 138 | } else { 139 | // no size yet, just adopt the given spec sizes 140 | } 141 | if (parentWidth != 0 && parentHeight != 0 && videoWidth != 0 && videoHeight != 0) { 142 | if (JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE == JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL) { 143 | /**原图**/ 144 | height = videoHeight; 145 | width = videoWidth; 146 | } else if (JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE == JZVideoPlayer.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP) { 147 | if (viewRotation == 90 || viewRotation == 270) { 148 | int tempSize = parentWidth; 149 | parentWidth = parentHeight; 150 | parentHeight = tempSize; 151 | } 152 | /**充满剪切**/ 153 | if (((double) videoHeight / videoWidth) > ((double) parentHeight / parentWidth)) { 154 | height = (int) (((double) parentWidth / (double) width * (double) height)); 155 | width = parentWidth; 156 | } else if (((double) videoHeight / videoWidth) < ((double) parentHeight / parentWidth)) { 157 | width = (int) (((double) parentHeight / (double) height * (double) width)); 158 | height = parentHeight; 159 | } 160 | } 161 | } 162 | setMeasuredDimension(width, height); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZUserAction.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | /** 4 | * Created by Nathen 5 | * On 2016/04/04 22:13 6 | */ 7 | public interface JZUserAction { 8 | 9 | int ON_CLICK_START_ICON = 0; 10 | /** 11 | * 视频损坏点击重试 12 | */ 13 | int ON_CLICK_START_ERROR = 1; 14 | int ON_CLICK_START_AUTO_COMPLETE = 2; 15 | 16 | int ON_CLICK_PAUSE = 3; 17 | int ON_CLICK_RESUME = 4; 18 | int ON_SEEK_POSITION = 5; 19 | int ON_AUTO_COMPLETE = 6; 20 | 21 | int ON_ENTER_FULLSCREEN = 7; 22 | int ON_QUIT_FULLSCREEN = 8; 23 | int ON_ENTER_TINYSCREEN = 9; 24 | int ON_QUIT_TINYSCREEN = 10; 25 | 26 | 27 | int ON_TOUCH_SCREEN_SEEK_VOLUME = 11; 28 | int ON_TOUCH_SCREEN_SEEK_POSITION = 12; 29 | 30 | void onEvent(int type, Object url, int screen, Object... objects); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZUtils.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.ContextWrapper; 6 | import android.content.SharedPreferences; 7 | import android.graphics.Rect; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.view.ContextThemeWrapper; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.util.Log; 14 | import android.view.View; 15 | import android.view.Window; 16 | import android.widget.AbsListView; 17 | 18 | 19 | import com.zzh12138.jzvideodemo.R; 20 | 21 | import java.util.Formatter; 22 | import java.util.LinkedHashMap; 23 | import java.util.Locale; 24 | 25 | 26 | /** 27 | * Created by Nathen 28 | * On 2016/02/21 12:25 29 | */ 30 | public class JZUtils { 31 | public static final String TAG = "JiaoZiVideoPlayer"; 32 | 33 | public static String stringForTime(long timeMs) { 34 | if (timeMs <= 0 || timeMs >= 24 * 60 * 60 * 1000) { 35 | return "00:00"; 36 | } 37 | long totalSeconds = timeMs / 1000; 38 | int seconds = (int) (totalSeconds % 60); 39 | int minutes = (int) ((totalSeconds / 60) % 60); 40 | int hours = (int) (totalSeconds / 3600); 41 | StringBuilder stringBuilder = new StringBuilder(); 42 | Formatter mFormatter = new Formatter(stringBuilder, Locale.getDefault()); 43 | if (hours > 0) { 44 | return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); 45 | } else { 46 | return mFormatter.format("%02d:%02d", minutes, seconds).toString(); 47 | } 48 | } 49 | 50 | /** 51 | * This method requires the caller to hold the permission ACCESS_NETWORK_STATE. 52 | * 53 | * @param context context 54 | * @return if wifi is connected,return true 55 | */ 56 | public static boolean isWifiConnected(Context context) { 57 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 58 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 59 | return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI; 60 | } 61 | 62 | /** 63 | * Get activity from context object 64 | * 65 | * @param context context 66 | * @return object of Activity or null if it is not Activity 67 | */ 68 | public static Activity scanForActivity(Context context) { 69 | if (context == null) return null; 70 | 71 | if (context instanceof Activity) { 72 | return (Activity) context; 73 | } else if (context instanceof ContextWrapper) { 74 | return scanForActivity(((ContextWrapper) context).getBaseContext()); 75 | } 76 | 77 | return null; 78 | } 79 | 80 | /** 81 | * Get AppCompatActivity from context 82 | * 83 | * @param context context 84 | * @return AppCompatActivity if it's not null 85 | */ 86 | public static AppCompatActivity getAppCompActivity(Context context) { 87 | if (context == null) return null; 88 | if (context instanceof AppCompatActivity) { 89 | return (AppCompatActivity) context; 90 | } else if (context instanceof ContextThemeWrapper) { 91 | return getAppCompActivity(((ContextThemeWrapper) context).getBaseContext()); 92 | } 93 | return null; 94 | } 95 | 96 | public static void setRequestedOrientation(Context context, int orientation) { 97 | if (JZUtils.getAppCompActivity(context) != null) { 98 | JZUtils.getAppCompActivity(context).setRequestedOrientation( 99 | orientation); 100 | } else { 101 | JZUtils.scanForActivity(context).setRequestedOrientation( 102 | orientation); 103 | } 104 | } 105 | 106 | public static Window getWindow(Context context) { 107 | if (JZUtils.getAppCompActivity(context) != null) { 108 | return JZUtils.getAppCompActivity(context).getWindow(); 109 | } else { 110 | return JZUtils.scanForActivity(context).getWindow(); 111 | } 112 | } 113 | 114 | public static int dip2px(Context context, float dpValue) { 115 | final float scale = context.getResources().getDisplayMetrics().density; 116 | return (int) (dpValue * scale + 0.5f); 117 | } 118 | 119 | public static void saveProgress(Context context, Object url, long progress) { 120 | if (!JZVideoPlayer.SAVE_PROGRESS) return; 121 | Log.i(TAG, "saveProgress: " + progress); 122 | if (progress < 500) { 123 | progress = 0; 124 | } 125 | SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", 126 | Context.MODE_PRIVATE); 127 | SharedPreferences.Editor editor = spn.edit(); 128 | editor.putLong("newVersion:" + url.toString(), progress).apply(); 129 | } 130 | 131 | public static long getSavedProgress(Context context, Object url) { 132 | if (!JZVideoPlayer.SAVE_PROGRESS) return 0; 133 | SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", 134 | Context.MODE_PRIVATE); 135 | return spn.getLong("newVersion:" + url.toString(), 0); 136 | } 137 | 138 | /** 139 | * if url == null, clear all progress 140 | * 141 | * @param context context 142 | * @param url if url!=null clear this url progress 143 | */ 144 | public static void clearSavedProgress(Context context, Object url) { 145 | if (url == null) { 146 | SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", 147 | Context.MODE_PRIVATE); 148 | spn.edit().clear().apply(); 149 | } else { 150 | SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", 151 | Context.MODE_PRIVATE); 152 | spn.edit().putLong("newVersion:" + url.toString(), 0).apply(); 153 | } 154 | } 155 | 156 | public static Object getCurrentFromDataSource(Object[] dataSourceObjects, int index) { 157 | LinkedHashMap map = (LinkedHashMap) dataSourceObjects[0]; 158 | if (map != null && map.size() > 0) { 159 | return getValueFromLinkedMap(map, index); 160 | } 161 | return null; 162 | } 163 | 164 | public static Object getValueFromLinkedMap(LinkedHashMap map, int index) { 165 | int currentIndex = 0; 166 | for (String key : map.keySet()) { 167 | if (currentIndex == index) { 168 | return map.get(key); 169 | } 170 | currentIndex++; 171 | } 172 | return null; 173 | } 174 | 175 | public static boolean dataSourceObjectsContainsUri(Object[] dataSourceObjects, Object object) { 176 | LinkedHashMap map = (LinkedHashMap) dataSourceObjects[0]; 177 | if (map != null && object != null) { 178 | return map.containsValue(object); 179 | } 180 | return false; 181 | } 182 | 183 | public static String getKeyFromDataSource(Object[] dataSourceObjects, int index) { 184 | LinkedHashMap map = (LinkedHashMap) dataSourceObjects[0]; 185 | int currentIndex = 0; 186 | for (String key : map.keySet()) { 187 | if (currentIndex == index) { 188 | return key; 189 | } 190 | currentIndex++; 191 | } 192 | return null; 193 | } 194 | 195 | public static float getViewVisiblePercent(View view) { 196 | if (view == null) { 197 | return 0f; 198 | } 199 | float height = view.getHeight(); 200 | Rect rect = new Rect(); 201 | if (!view.getLocalVisibleRect(rect)) { 202 | return 0f; 203 | } 204 | float visibleHeight = rect.bottom - rect.top; 205 | Log.d(TAG, "getViewVisiblePercent: emm " + visibleHeight); 206 | return visibleHeight / height; 207 | } 208 | 209 | 210 | public static void onScrollPlayVideo(AbsListView absListView, int firstVisiblePosition, int lastVisiblePosition) { 211 | for (int i = firstVisiblePosition; i <= lastVisiblePosition; i++) { 212 | View ad = absListView.getChildAt(i - firstVisiblePosition); 213 | View view = ad.findViewById(R.id.player); 214 | if (view != null && view instanceof JZVideoPlayerStandard) { 215 | JZVideoPlayerStandard player = (JZVideoPlayerStandard) view; 216 | Log.d(TAG, "onScrollStateChanged: " + JZUtils.getViewVisiblePercent(player)); 217 | if (JZMediaManager.instance().positionInList != i && getViewVisiblePercent(player) == 1f) { 218 | player.startButton.performClick(); 219 | break; 220 | } 221 | } 222 | } 223 | } 224 | 225 | public static void onScrollPlayVideo(RecyclerView recyclerView, int firstVisiblePosition, int lastVisiblePosition) { 226 | if (JZMediaManager.isWiFi) { 227 | for (int i = 0; i <= lastVisiblePosition - firstVisiblePosition; i++) { 228 | View child = recyclerView.getChildAt(i); 229 | View view = child.findViewById(R.id.player); 230 | if (view != null && view instanceof JZVideoPlayerStandard) { 231 | JZVideoPlayerStandard player = (JZVideoPlayerStandard) view; 232 | if (getViewVisiblePercent(player) == 1f) { 233 | if (JZMediaManager.instance().positionInList != i + firstVisiblePosition) { 234 | player.startButton.performClick(); 235 | } 236 | break; 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | public static void onScrollReleaseAllVideos(AbsListView view, int firstVisiblePosition, int visibleItemCount, int totalItemCount) { 244 | onScrollReleaseAllVideos(firstVisiblePosition, firstVisiblePosition + visibleItemCount,0); 245 | } 246 | 247 | public static void onScrollReleaseAllVideos(int firstVisiblePosition, int lastVisiblePosition,float percent) { 248 | int currentPlayPosition = JZMediaManager.instance().positionInList; 249 | if (currentPlayPosition >= 0) { 250 | if ((currentPlayPosition <= firstVisiblePosition || currentPlayPosition >= lastVisiblePosition - 1)) { 251 | if (getViewVisiblePercent(JZVideoPlayerManager.getCurrentJzvd()) < percent) { 252 | JZVideoPlayer.releaseAllVideos(); 253 | } 254 | } 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/JZVideoPlayerManager.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | /** 4 | * Put JZVideoPlayer into layout 5 | * From a JZVideoPlayer to another JZVideoPlayer 6 | * Created by Nathen on 16/7/26. 7 | */ 8 | public class JZVideoPlayerManager { 9 | 10 | public static JZVideoPlayer FIRST_FLOOR_JZVD; 11 | public static JZVideoPlayer SECOND_FLOOR_JZVD; 12 | 13 | public static JZVideoPlayer getFirstFloor() { 14 | return FIRST_FLOOR_JZVD; 15 | } 16 | 17 | public static void setFirstFloor(JZVideoPlayer jzVideoPlayer) { 18 | FIRST_FLOOR_JZVD = jzVideoPlayer; 19 | } 20 | 21 | public static JZVideoPlayer getSecondFloor() { 22 | return SECOND_FLOOR_JZVD; 23 | } 24 | 25 | public static void setSecondFloor(JZVideoPlayer jzVideoPlayer) { 26 | SECOND_FLOOR_JZVD = jzVideoPlayer; 27 | } 28 | 29 | public static JZVideoPlayer getCurrentJzvd() { 30 | if (getSecondFloor() != null) { 31 | return getSecondFloor(); 32 | } 33 | return getFirstFloor(); 34 | } 35 | 36 | public static void completeAll() { 37 | if (SECOND_FLOOR_JZVD != null) { 38 | SECOND_FLOOR_JZVD.onCompletion(); 39 | SECOND_FLOOR_JZVD = null; 40 | } 41 | if (FIRST_FLOOR_JZVD != null) { 42 | FIRST_FLOOR_JZVD.onCompletion(); 43 | FIRST_FLOOR_JZVD = null; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/player/SoDownloadIntentService.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.player; 2 | 3 | import android.app.IntentService; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.support.annotation.Nullable; 7 | import android.widget.Toast; 8 | 9 | import com.zzh12138.jzvideodemo.MyApplication; 10 | 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.net.URL; 16 | import java.net.URLConnection; 17 | 18 | /** 19 | * Created by zhangzhihao on 2018/10/10 14:37. 20 | */ 21 | public class SoDownloadIntentService extends IntentService { 22 | 23 | 24 | public SoDownloadIntentService() { 25 | super("SoDownloadIntentService"); 26 | } 27 | 28 | @Override 29 | protected void onHandleIntent(@Nullable Intent intent) { 30 | File dir = getDir("libs", Context.MODE_PRIVATE); 31 | File soFile = new File(dir, "ijkffmpeg.so"); 32 | if (soFile.exists()) { 33 | if (JZVideoPlayerManager.getCurrentJzvd() == null) { 34 | JZVideoPlayer.setMediaInterface(new JZMediaIjkplayer()); 35 | } 36 | } else { 37 | String url = "http://bmob-cdn-21848.b0.upaiyun.com/2018/10/12/2296183a4014f71080f878c0818d38b0.so"; 38 | try { 39 | URL downUrl = new URL(url); 40 | URLConnection connection = downUrl.openConnection(); 41 | InputStream is = connection.getInputStream(); 42 | int fileSize = connection.getContentLength(); 43 | if (fileSize <= 0) { 44 | throw new RuntimeException("file error"); 45 | } 46 | if (is == null) { 47 | throw new RuntimeException("stream is null"); 48 | } 49 | if (!dir.exists()) { 50 | dir.mkdirs(); 51 | } 52 | FileOutputStream fos = new FileOutputStream(soFile); 53 | byte buf[] = new byte[1024]; 54 | do { 55 | int num = is.read(buf); 56 | if (num == -1) { 57 | break; 58 | } 59 | fos.write(buf, 0, num); 60 | } while (true); 61 | is.close(); 62 | if (JZVideoPlayerManager.getCurrentJzvd() == null) { 63 | JZVideoPlayer.setMediaInterface(new JZMediaIjkplayer()); 64 | } 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/view/MaskView.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.support.annotation.Nullable; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | import com.zzh12138.jzvideodemo.R; 13 | 14 | 15 | /** 16 | * Created by zhangzhihao on 2018/8/14 18:32. 17 | */ 18 | public class MaskView extends View { 19 | private static final String TAG = "MaskView"; 20 | private int BACKGROUND_COLOR = 0xFF000000; 21 | private int startY; 22 | private int endY; 23 | private Paint mPaint; 24 | private int mBackgroundColor; 25 | private int mWidth; 26 | private int mHeight; 27 | 28 | 29 | public MaskView(Context context) { 30 | this(context, null); 31 | } 32 | 33 | public MaskView(Context context, @Nullable AttributeSet attrs) { 34 | this(context, attrs, 0); 35 | } 36 | 37 | public MaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 38 | super(context, attrs, defStyleAttr); 39 | if (attrs != null) { 40 | TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaskView); 41 | mBackgroundColor = arr.getColor(R.styleable.MaskView_backgroundColor, BACKGROUND_COLOR); 42 | arr.recycle(); 43 | } 44 | mPaint = new Paint(); 45 | mPaint.setStyle(Paint.Style.FILL); 46 | mPaint.setColor(mBackgroundColor); 47 | } 48 | 49 | @Override 50 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 51 | super.onSizeChanged(w, h, oldw, oldh); 52 | mWidth = w; 53 | mHeight = h; 54 | } 55 | 56 | @Override 57 | protected void onDraw(Canvas canvas) { 58 | super.onDraw(canvas); 59 | canvas.drawRect(0, 0, mWidth, startY, mPaint); 60 | mPaint.setColor(Color.TRANSPARENT); 61 | canvas.drawRect(0, startY, mWidth, endY, mPaint); 62 | mPaint.setColor(mBackgroundColor); 63 | canvas.drawRect(0, endY, mWidth, mHeight, mPaint); 64 | } 65 | 66 | public void changeMaskLocation(int startY, int endY) { 67 | this.startY = startY; 68 | this.endY = endY; 69 | invalidate(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/view/PlayerContainer.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.view; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.util.AttributeSet; 7 | import android.widget.FrameLayout; 8 | 9 | /** 10 | * Created by zhangzhihao on 2018/8/30 9:56. 11 | * 播放器容器 12 | * 只有当需要做页面平移的时候才需要使用 13 | */ 14 | public class PlayerContainer extends FrameLayout { 15 | private float widthRatio, heightRatio; 16 | 17 | public PlayerContainer(@NonNull Context context) { 18 | super(context); 19 | } 20 | 21 | public PlayerContainer(@NonNull Context context, @Nullable AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public PlayerContainer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 31 | if (widthRatio == 0f || heightRatio == 0f) { 32 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 33 | } else { 34 | int specWidth = MeasureSpec.getSize(widthMeasureSpec); 35 | int specHeight = (int) (specWidth * heightRatio / widthRatio); 36 | setMeasuredDimension(specWidth, specHeight); 37 | if(getChildCount()>0) { 38 | int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.EXACTLY); 39 | int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.EXACTLY); 40 | getChildAt(0).measure(childWidthMeasureSpec, childHeightMeasureSpec); 41 | } 42 | } 43 | } 44 | 45 | public PlayerContainer setWidthRatio(float widthRatio) { 46 | this.widthRatio = widthRatio; 47 | return this; 48 | } 49 | 50 | public PlayerContainer setHeightRatio(float heightRatio) { 51 | this.heightRatio = heightRatio; 52 | return this; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/zzh12138/jzvideodemo/view/VoiceAnimationView.java: -------------------------------------------------------------------------------- 1 | package com.zzh12138.jzvideodemo.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.support.annotation.Nullable; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | 11 | import com.zzh12138.jzvideodemo.R; 12 | 13 | 14 | /** 15 | * Created by zhangzhihao on 2018/7/26 10:10. 16 | */ 17 | 18 | public class VoiceAnimationView extends View { 19 | private static final String TAG = "VoiceAnimationView"; 20 | 21 | private static final int DEFAULT_COLOR = 0xffffffff; 22 | private static final int DEFAULT_NUM = 3; 23 | private static final int DEFAULT_WIDTH = 5; 24 | private Paint mPaint; 25 | /** 26 | * 矩形个数 27 | */ 28 | private int mCount; 29 | /** 30 | * 矩形间距 31 | */ 32 | private float mPadding; 33 | /** 34 | * 矩形最大高度,默认为控件高度 35 | */ 36 | private float maxRectangleHeight; 37 | /** 38 | * 矩形最小高度 39 | */ 40 | private float minRectangleHeight; 41 | private float mRectangleWidth; 42 | /** 43 | * 颜色 44 | */ 45 | private int mColor; 46 | 47 | private float percent; 48 | 49 | public VoiceAnimationView(Context context) { 50 | this(context, null); 51 | } 52 | 53 | public VoiceAnimationView(Context context, @Nullable AttributeSet attrs) { 54 | this(context, attrs, 0); 55 | } 56 | 57 | public VoiceAnimationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 58 | super(context, attrs, defStyleAttr); 59 | if (attrs != null) { 60 | TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.VoiceAnimationView); 61 | mColor = arr.getColor(R.styleable.VoiceAnimationView_rectangleColor, DEFAULT_COLOR); 62 | mCount = arr.getInteger(R.styleable.VoiceAnimationView_rectangleNum, DEFAULT_NUM); 63 | mPadding = arr.getDimension(R.styleable.VoiceAnimationView_rectanglePadding, DEFAULT_WIDTH); 64 | } 65 | mPaint = new Paint(); 66 | mPaint.setAntiAlias(true); 67 | mPaint.setColor(mColor); 68 | mPaint.setStyle(Paint.Style.FILL); 69 | } 70 | 71 | 72 | @Override 73 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 74 | super.onSizeChanged(w, h, oldw, oldh); 75 | maxRectangleHeight = h-getPaddingBottom()-getPaddingTop(); 76 | minRectangleHeight = h / 3f; 77 | mRectangleWidth = (w - getPaddingLeft() - getPaddingRight() - mPadding * (mCount - 1)) / (float) mCount; 78 | } 79 | 80 | @Override 81 | protected void onDraw(Canvas canvas) { 82 | super.onDraw(canvas); 83 | for (int i = 0; i < mCount; i++) { 84 | float top ; 85 | if ((i & 1) != 0) { 86 | top = (maxRectangleHeight - minRectangleHeight) * (1 - percent); 87 | } else { 88 | top = (maxRectangleHeight - minRectangleHeight - 5) * percent + i * 5; 89 | } 90 | if (top > maxRectangleHeight - minRectangleHeight) { 91 | top = maxRectangleHeight - minRectangleHeight; 92 | } 93 | canvas.drawRect(getPaddingLeft() + i * (mRectangleWidth + mPadding), top+getPaddingTop(), 94 | getPaddingLeft() + mRectangleWidth + i * (mRectangleWidth + mPadding), maxRectangleHeight , mPaint); 95 | } 96 | } 97 | 98 | public float getPercent() { 99 | return percent; 100 | } 101 | 102 | public void setPercent(float percent) { 103 | this.percent = percent; 104 | invalidate(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_mute_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_player_controller_shadow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/bg_player_controller_shadow.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_player_replay.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_player_standard_seek.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_player_top_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_player_voice_shadow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/bg_player_voice_shadow.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_rectangle_red_corner_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_comment.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_left_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_left_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_full_screen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_full_screen.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_loading.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_loading.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_mute.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_mute.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_not_mute.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_not_mute.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_pause.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_pause.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_play.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_play.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_replay.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_replay.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_player_seek_thumb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_player_seek_thumb.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_praise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_praise.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/icon_share.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo_qq.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/logo_qq.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo_sinaweibo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/logo_sinaweibo.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo_wechat.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/logo_wechat.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo_wechatmoments.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/drawable-xhdpi/logo_wechatmoments.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/player_loading.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_comment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 29 | 30 | 41 | 42 | 56 | 57 | 68 | 69 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_news_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_news_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 21 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 21 | 22 | 26 | 27 | 28 | 33 | 34 | 42 | 43 | 55 | 56 | 67 | 68 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_video_comment.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 25 | 26 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_video_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 27 | 28 | 34 | 35 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_no_more.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_player_pause.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xhdpi/icon_player_pause.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/JZVideoDemo/e0c114d25e7dbc695e811e3f5fb09aa46497e014/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #ffffff 7 | #000000 8 | #00000000 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | JZVideoDemo 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.3' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 12 09:48:12 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /ijk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ijk/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | 6 | 7 | 8 | defaultConfig { 9 | minSdkVersion 15 10 | targetSdkVersion 27 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | 30 | implementation 'com.android.support:appcompat-v7:27.1.1' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | } 35 | -------------------------------------------------------------------------------- /ijk/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /ijk/src/androidTest/java/tv/danmaku/ijk/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package tv.danmaku.ijk; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("tv.danmaku.ijk.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ijk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource; 21 | 22 | @SuppressWarnings("WeakerAccess") 23 | public abstract class AbstractMediaPlayer implements IMediaPlayer { 24 | private OnPreparedListener mOnPreparedListener; 25 | private OnCompletionListener mOnCompletionListener; 26 | private OnBufferingUpdateListener mOnBufferingUpdateListener; 27 | private OnSeekCompleteListener mOnSeekCompleteListener; 28 | private OnVideoSizeChangedListener mOnVideoSizeChangedListener; 29 | private OnErrorListener mOnErrorListener; 30 | private OnInfoListener mOnInfoListener; 31 | private OnTimedTextListener mOnTimedTextListener; 32 | 33 | public final void setOnPreparedListener(OnPreparedListener listener) { 34 | mOnPreparedListener = listener; 35 | } 36 | 37 | public final void setOnCompletionListener(OnCompletionListener listener) { 38 | mOnCompletionListener = listener; 39 | } 40 | 41 | public final void setOnBufferingUpdateListener( 42 | OnBufferingUpdateListener listener) { 43 | mOnBufferingUpdateListener = listener; 44 | } 45 | 46 | public final void setOnSeekCompleteListener(OnSeekCompleteListener listener) { 47 | mOnSeekCompleteListener = listener; 48 | } 49 | 50 | public final void setOnVideoSizeChangedListener( 51 | OnVideoSizeChangedListener listener) { 52 | mOnVideoSizeChangedListener = listener; 53 | } 54 | 55 | public final void setOnErrorListener(OnErrorListener listener) { 56 | mOnErrorListener = listener; 57 | } 58 | 59 | public final void setOnInfoListener(OnInfoListener listener) { 60 | mOnInfoListener = listener; 61 | } 62 | 63 | public final void setOnTimedTextListener(OnTimedTextListener listener) { 64 | mOnTimedTextListener = listener; 65 | } 66 | 67 | public void resetListeners() { 68 | mOnPreparedListener = null; 69 | mOnBufferingUpdateListener = null; 70 | mOnCompletionListener = null; 71 | mOnSeekCompleteListener = null; 72 | mOnVideoSizeChangedListener = null; 73 | mOnErrorListener = null; 74 | mOnInfoListener = null; 75 | mOnTimedTextListener = null; 76 | } 77 | 78 | protected final void notifyOnPrepared() { 79 | if (mOnPreparedListener != null) 80 | mOnPreparedListener.onPrepared(this); 81 | } 82 | 83 | protected final void notifyOnCompletion() { 84 | if (mOnCompletionListener != null) 85 | mOnCompletionListener.onCompletion(this); 86 | } 87 | 88 | protected final void notifyOnBufferingUpdate(int percent) { 89 | if (mOnBufferingUpdateListener != null) 90 | mOnBufferingUpdateListener.onBufferingUpdate(this, percent); 91 | } 92 | 93 | protected final void notifyOnSeekComplete() { 94 | if (mOnSeekCompleteListener != null) 95 | mOnSeekCompleteListener.onSeekComplete(this); 96 | } 97 | 98 | protected final void notifyOnVideoSizeChanged(int width, int height, 99 | int sarNum, int sarDen) { 100 | if (mOnVideoSizeChangedListener != null) 101 | mOnVideoSizeChangedListener.onVideoSizeChanged(this, width, height, 102 | sarNum, sarDen); 103 | } 104 | 105 | protected final boolean notifyOnError(int what, int extra) { 106 | return mOnErrorListener != null && mOnErrorListener.onError(this, what, extra); 107 | } 108 | 109 | protected final boolean notifyOnInfo(int what, int extra) { 110 | return mOnInfoListener != null && mOnInfoListener.onInfo(this, what, extra); 111 | } 112 | 113 | protected final void notifyOnTimedText(IjkTimedText text) { 114 | if (mOnTimedTextListener != null) 115 | mOnTimedTextListener.onTimedText(this, text); 116 | } 117 | 118 | public void setDataSource(IMediaDataSource mediaDataSource) { 119 | throw new UnsupportedOperationException(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import android.annotation.TargetApi; 21 | import android.content.Context; 22 | import android.net.Uri; 23 | import android.os.Build; 24 | import android.view.Surface; 25 | import android.view.SurfaceHolder; 26 | 27 | import java.io.FileDescriptor; 28 | import java.io.IOException; 29 | import java.util.Map; 30 | 31 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource; 32 | import tv.danmaku.ijk.media.player.misc.ITrackInfo; 33 | 34 | public interface IMediaPlayer { 35 | /* 36 | * Do not change these values without updating their counterparts in native 37 | */ 38 | int MEDIA_INFO_UNKNOWN = 1; 39 | int MEDIA_INFO_STARTED_AS_NEXT = 2; 40 | int MEDIA_INFO_VIDEO_RENDERING_START = 3; 41 | int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; 42 | int MEDIA_INFO_BUFFERING_START = 701; 43 | int MEDIA_INFO_BUFFERING_END = 702; 44 | int MEDIA_INFO_NETWORK_BANDWIDTH = 703; 45 | int MEDIA_INFO_BAD_INTERLEAVING = 800; 46 | int MEDIA_INFO_NOT_SEEKABLE = 801; 47 | int MEDIA_INFO_METADATA_UPDATE = 802; 48 | int MEDIA_INFO_TIMED_TEXT_ERROR = 900; 49 | int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; 50 | int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; 51 | 52 | int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001; 53 | int MEDIA_INFO_AUDIO_RENDERING_START = 10002; 54 | int MEDIA_INFO_AUDIO_DECODED_START = 10003; 55 | int MEDIA_INFO_VIDEO_DECODED_START = 10004; 56 | int MEDIA_INFO_OPEN_INPUT = 10005; 57 | int MEDIA_INFO_FIND_STREAM_INFO = 10006; 58 | int MEDIA_INFO_COMPONENT_OPEN = 10007; 59 | int MEDIA_INFO_VIDEO_SEEK_RENDERING_START = 10008; 60 | int MEDIA_INFO_AUDIO_SEEK_RENDERING_START = 10009; 61 | int MEDIA_INFO_MEDIA_ACCURATE_SEEK_COMPLETE = 10100; 62 | 63 | int MEDIA_ERROR_UNKNOWN = 1; 64 | int MEDIA_ERROR_SERVER_DIED = 100; 65 | int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; 66 | int MEDIA_ERROR_IO = -1004; 67 | int MEDIA_ERROR_MALFORMED = -1007; 68 | int MEDIA_ERROR_UNSUPPORTED = -1010; 69 | int MEDIA_ERROR_TIMED_OUT = -110; 70 | 71 | void setDisplay(SurfaceHolder sh); 72 | 73 | void setDataSource(Context context, Uri uri) 74 | throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 75 | 76 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 77 | void setDataSource(Context context, Uri uri, Map headers) 78 | throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 79 | 80 | void setDataSource(FileDescriptor fd) 81 | throws IOException, IllegalArgumentException, IllegalStateException; 82 | 83 | void setDataSource(String path) 84 | throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 85 | 86 | String getDataSource(); 87 | 88 | void prepareAsync() throws IllegalStateException; 89 | 90 | void start() throws IllegalStateException; 91 | 92 | void stop() throws IllegalStateException; 93 | 94 | void pause() throws IllegalStateException; 95 | 96 | void setScreenOnWhilePlaying(boolean screenOn); 97 | 98 | int getVideoWidth(); 99 | 100 | int getVideoHeight(); 101 | 102 | boolean isPlaying(); 103 | 104 | void seekTo(long msec) throws IllegalStateException; 105 | 106 | long getCurrentPosition(); 107 | 108 | long getDuration(); 109 | 110 | void release(); 111 | 112 | void reset(); 113 | 114 | void setVolume(float leftVolume, float rightVolume); 115 | 116 | int getAudioSessionId(); 117 | 118 | MediaInfo getMediaInfo(); 119 | 120 | @SuppressWarnings("EmptyMethod") 121 | @Deprecated 122 | void setLogEnabled(boolean enable); 123 | 124 | @Deprecated 125 | boolean isPlayable(); 126 | 127 | void setOnPreparedListener(OnPreparedListener listener); 128 | 129 | void setOnCompletionListener(OnCompletionListener listener); 130 | 131 | void setOnBufferingUpdateListener( 132 | OnBufferingUpdateListener listener); 133 | 134 | void setOnSeekCompleteListener( 135 | OnSeekCompleteListener listener); 136 | 137 | void setOnVideoSizeChangedListener( 138 | OnVideoSizeChangedListener listener); 139 | 140 | void setOnErrorListener(OnErrorListener listener); 141 | 142 | void setOnInfoListener(OnInfoListener listener); 143 | 144 | void setOnTimedTextListener(OnTimedTextListener listener); 145 | 146 | /*-------------------- 147 | * Listeners 148 | */ 149 | interface OnPreparedListener { 150 | void onPrepared(IMediaPlayer mp); 151 | } 152 | 153 | interface OnCompletionListener { 154 | void onCompletion(IMediaPlayer mp); 155 | } 156 | 157 | interface OnBufferingUpdateListener { 158 | void onBufferingUpdate(IMediaPlayer mp, int percent); 159 | } 160 | 161 | interface OnSeekCompleteListener { 162 | void onSeekComplete(IMediaPlayer mp); 163 | } 164 | 165 | interface OnVideoSizeChangedListener { 166 | void onVideoSizeChanged(IMediaPlayer mp, int width, int height, 167 | int sar_num, int sar_den); 168 | } 169 | 170 | interface OnErrorListener { 171 | boolean onError(IMediaPlayer mp, int what, int extra); 172 | } 173 | 174 | interface OnInfoListener { 175 | boolean onInfo(IMediaPlayer mp, int what, int extra); 176 | } 177 | 178 | interface OnTimedTextListener { 179 | void onTimedText(IMediaPlayer mp, IjkTimedText text); 180 | } 181 | 182 | /*-------------------- 183 | * Optional 184 | */ 185 | void setAudioStreamType(int streamtype); 186 | 187 | @Deprecated 188 | void setKeepInBackground(boolean keepInBackground); 189 | 190 | int getVideoSarNum(); 191 | 192 | int getVideoSarDen(); 193 | 194 | @Deprecated 195 | void setWakeMode(Context context, int mode); 196 | 197 | void setLooping(boolean looping); 198 | 199 | boolean isLooping(); 200 | 201 | /*-------------------- 202 | * AndroidMediaPlayer: JELLY_BEAN 203 | */ 204 | ITrackInfo[] getTrackInfo(); 205 | 206 | /*-------------------- 207 | * AndroidMediaPlayer: ICE_CREAM_SANDWICH: 208 | */ 209 | void setSurface(Surface surface); 210 | 211 | /*-------------------- 212 | * AndroidMediaPlayer: M: 213 | */ 214 | void setDataSource(IMediaDataSource mediaDataSource); 215 | } 216 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import android.graphics.SurfaceTexture; 21 | 22 | public interface ISurfaceTextureHolder { 23 | void setSurfaceTexture(SurfaceTexture surfaceTexture); 24 | 25 | SurfaceTexture getSurfaceTexture(); 26 | 27 | void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost); 28 | } 29 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHost.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import android.graphics.SurfaceTexture; 21 | 22 | public interface ISurfaceTextureHost { 23 | void releaseSurfaceTexture(SurfaceTexture surfaceTexture); 24 | } 25 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/IjkLibLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | public interface IjkLibLoader { 21 | void loadLibrary(String libName) throws UnsatisfiedLinkError, 22 | SecurityException; 23 | } 24 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/IjkMediaCodecInfo.java: -------------------------------------------------------------------------------- 1 | package tv.danmaku.ijk.media.player; 2 | 3 | import android.annotation.TargetApi; 4 | import android.media.MediaCodecInfo; 5 | import android.media.MediaCodecInfo.CodecCapabilities; 6 | import android.media.MediaCodecInfo.CodecProfileLevel; 7 | import android.os.Build; 8 | import android.text.TextUtils; 9 | import android.util.Log; 10 | 11 | import java.util.Locale; 12 | import java.util.Map; 13 | import java.util.TreeMap; 14 | 15 | public class IjkMediaCodecInfo { 16 | private final static String TAG = "IjkMediaCodecInfo"; 17 | 18 | public static final int RANK_MAX = 1000; 19 | public static final int RANK_TESTED = 800; 20 | public static final int RANK_ACCEPTABLE = 700; 21 | public static final int RANK_LAST_CHANCE = 600; 22 | public static final int RANK_SECURE = 300; 23 | public static final int RANK_SOFTWARE = 200; 24 | public static final int RANK_NON_STANDARD = 100; 25 | public static final int RANK_NO_SENSE = 0; 26 | 27 | public MediaCodecInfo mCodecInfo; 28 | public int mRank = 0; 29 | public String mMimeType; 30 | 31 | private static Map sKnownCodecList; 32 | 33 | private static synchronized Map getKnownCodecList() { 34 | if (sKnownCodecList != null) 35 | return sKnownCodecList; 36 | 37 | sKnownCodecList = new TreeMap( 38 | String.CASE_INSENSITIVE_ORDER); 39 | 40 | // ----- Nvidia ----- 41 | // Tegra3 42 | // Nexus 7 (2012) 43 | // Tegra K1 44 | // Nexus 9 45 | sKnownCodecList.put("OMX.Nvidia.h264.decode", RANK_TESTED); 46 | sKnownCodecList.put("OMX.Nvidia.h264.decode.secure", RANK_SECURE); 47 | 48 | // ----- Intel ----- 49 | // Atom Z3735 50 | // Teclast X98 Air 51 | sKnownCodecList.put("OMX.Intel.hw_vd.h264", RANK_TESTED + 1); 52 | // Atom Z2560 53 | // Dell Venue 7 3730 54 | sKnownCodecList.put("OMX.Intel.VideoDecoder.AVC", RANK_TESTED); 55 | 56 | // ----- Qualcomm ----- 57 | // MSM8260 58 | // Xiaomi MI 1S 59 | sKnownCodecList.put("OMX.qcom.video.decoder.avc", RANK_TESTED); 60 | sKnownCodecList.put("OMX.ittiam.video.decoder.avc", RANK_NO_SENSE); 61 | 62 | // ----- Samsung ----- 63 | // Exynos 3110 64 | // Nexus S 65 | sKnownCodecList.put("OMX.SEC.avc.dec", RANK_TESTED); 66 | sKnownCodecList.put("OMX.SEC.AVC.Decoder", RANK_TESTED - 1); 67 | // OMX.SEC.avcdec doesn't reorder output pictures on GT-9100 68 | sKnownCodecList.put("OMX.SEC.avcdec", RANK_TESTED - 2); 69 | sKnownCodecList.put("OMX.SEC.avc.sw.dec", RANK_SOFTWARE); 70 | // Exynos 5 ? 71 | sKnownCodecList.put("OMX.Exynos.avc.dec", RANK_TESTED); 72 | sKnownCodecList.put("OMX.Exynos.AVC.Decoder", RANK_TESTED - 1); 73 | 74 | // ------ Huawei hisilicon ------ 75 | // Kirin 910, Mali 450 MP 76 | // Huawei HONOR 3C (H30-L01) 77 | sKnownCodecList.put("OMX.k3.video.decoder.avc", RANK_TESTED); 78 | // Kirin 920, Mali T624 79 | // Huawei HONOR 6 80 | sKnownCodecList.put("OMX.IMG.MSVDX.Decoder.AVC", RANK_TESTED); 81 | 82 | // ----- TI ----- 83 | // TI OMAP4460 84 | // Galaxy Nexus 85 | sKnownCodecList.put("OMX.TI.DUCATI1.VIDEO.DECODER", RANK_TESTED); 86 | 87 | // ------ RockChip ------ 88 | // Youku TVBox 89 | sKnownCodecList.put("OMX.rk.video_decoder.avc", RANK_TESTED); 90 | 91 | // ------ AMLogic ----- 92 | // MiBox1, 1s, 2 93 | sKnownCodecList.put("OMX.amlogic.avc.decoder.awesome", RANK_TESTED); 94 | 95 | // ------ Marvell ------ 96 | // Lenovo A788t 97 | sKnownCodecList.put("OMX.MARVELL.VIDEO.HW.CODA7542DECODER", RANK_TESTED); 98 | sKnownCodecList.put("OMX.MARVELL.VIDEO.H264DECODER", RANK_SOFTWARE); 99 | 100 | // ----- TODO: need test ----- 101 | sKnownCodecList.remove("OMX.Action.Video.Decoder"); 102 | sKnownCodecList.remove("OMX.allwinner.video.decoder.avc"); 103 | sKnownCodecList.remove("OMX.BRCM.vc4.decoder.avc"); 104 | sKnownCodecList.remove("OMX.brcm.video.h264.hw.decoder"); 105 | sKnownCodecList.remove("OMX.brcm.video.h264.decoder"); 106 | sKnownCodecList.remove("OMX.cosmo.video.decoder.avc"); 107 | sKnownCodecList.remove("OMX.duos.h264.decoder"); 108 | sKnownCodecList.remove("OMX.hantro.81x0.video.decoder"); 109 | sKnownCodecList.remove("OMX.hantro.G1.video.decoder"); 110 | sKnownCodecList.remove("OMX.hisi.video.decoder"); 111 | sKnownCodecList.remove("OMX.LG.decoder.video.avc"); 112 | sKnownCodecList.remove("OMX.MS.AVC.Decoder"); 113 | sKnownCodecList.remove("OMX.RENESAS.VIDEO.DECODER.H264"); 114 | sKnownCodecList.remove("OMX.RTK.video.decoder"); 115 | sKnownCodecList.remove("OMX.sprd.h264.decoder"); 116 | sKnownCodecList.remove("OMX.ST.VFM.H264Dec"); 117 | sKnownCodecList.remove("OMX.vpu.video_decoder.avc"); 118 | sKnownCodecList.remove("OMX.WMT.decoder.avc"); 119 | 120 | // Really ? 121 | sKnownCodecList.remove("OMX.bluestacks.hw.decoder"); 122 | 123 | // --------------- 124 | // Useless codec 125 | // ----- google ----- 126 | sKnownCodecList.put("OMX.google.h264.decoder", RANK_SOFTWARE); 127 | sKnownCodecList.put("OMX.google.h264.lc.decoder", RANK_SOFTWARE); 128 | // ----- huawei k920 ----- 129 | sKnownCodecList.put("OMX.k3.ffmpeg.decoder", RANK_SOFTWARE); 130 | sKnownCodecList.put("OMX.ffmpeg.video.decoder", RANK_SOFTWARE); 131 | // ----- unknown ----- 132 | sKnownCodecList.put("OMX.sprd.soft.h264.decoder", RANK_SOFTWARE); 133 | 134 | return sKnownCodecList; 135 | } 136 | 137 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 138 | public static IjkMediaCodecInfo setupCandidate(MediaCodecInfo codecInfo, 139 | String mimeType) { 140 | if (codecInfo == null 141 | || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) 142 | return null; 143 | 144 | String name = codecInfo.getName(); 145 | if (TextUtils.isEmpty(name)) 146 | return null; 147 | 148 | name = name.toLowerCase(Locale.US); 149 | int rank = RANK_NO_SENSE; 150 | if (!name.startsWith("omx.")) { 151 | rank = RANK_NON_STANDARD; 152 | } else if (name.startsWith("omx.pv")) { 153 | rank = RANK_SOFTWARE; 154 | } else if (name.startsWith("omx.google.")) { 155 | rank = RANK_SOFTWARE; 156 | } else if (name.startsWith("omx.ffmpeg.")) { 157 | rank = RANK_SOFTWARE; 158 | } else if (name.startsWith("omx.k3.ffmpeg.")) { 159 | rank = RANK_SOFTWARE; 160 | } else if (name.startsWith("omx.avcodec.")) { 161 | rank = RANK_SOFTWARE; 162 | } else if (name.startsWith("omx.ittiam.")) { 163 | // unknown codec in qualcomm SoC 164 | rank = RANK_NO_SENSE; 165 | } else if (name.startsWith("omx.mtk.")) { 166 | // 1. MTK only works on 4.3 and above 167 | // 2. MTK works on MIUI 6 (4.2.1) 168 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) 169 | rank = RANK_NO_SENSE; 170 | else 171 | rank = RANK_TESTED; 172 | } else { 173 | Integer knownRank = getKnownCodecList().get(name); 174 | if (knownRank != null) { 175 | rank = knownRank; 176 | } else { 177 | try { 178 | CodecCapabilities cap = codecInfo 179 | .getCapabilitiesForType(mimeType); 180 | if (cap != null) 181 | rank = RANK_ACCEPTABLE; 182 | else 183 | rank = RANK_LAST_CHANCE; 184 | } catch (Throwable e) { 185 | rank = RANK_LAST_CHANCE; 186 | } 187 | } 188 | } 189 | 190 | IjkMediaCodecInfo candidate = new IjkMediaCodecInfo(); 191 | candidate.mCodecInfo = codecInfo; 192 | candidate.mRank = rank; 193 | candidate.mMimeType = mimeType; 194 | return candidate; 195 | } 196 | 197 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 198 | public void dumpProfileLevels(String mimeType) { 199 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) 200 | return; 201 | 202 | try { 203 | CodecCapabilities caps = mCodecInfo 204 | .getCapabilitiesForType(mimeType); 205 | int maxProfile = 0; 206 | int maxLevel = 0; 207 | if (caps != null) { 208 | if (caps.profileLevels != null) { 209 | for (CodecProfileLevel profileLevel : caps.profileLevels) { 210 | if (profileLevel == null) 211 | continue; 212 | 213 | maxProfile = Math.max(maxProfile, profileLevel.profile); 214 | maxLevel = Math.max(maxLevel, profileLevel.level); 215 | } 216 | } 217 | } 218 | 219 | Log.i(TAG, 220 | String.format(Locale.US, "%s", 221 | getProfileLevelName(maxProfile, maxLevel))); 222 | } catch (Throwable e) { 223 | Log.i(TAG, "profile-level: exception"); 224 | } 225 | } 226 | 227 | public static String getProfileLevelName(int profile, int level) { 228 | return String.format(Locale.US, " %s Profile Level %s (%d,%d)", 229 | getProfileName(profile), getLevelName(level), profile, level); 230 | } 231 | 232 | public static String getProfileName(int profile) { 233 | switch (profile) { 234 | case CodecProfileLevel.AVCProfileBaseline: 235 | return "Baseline"; 236 | case CodecProfileLevel.AVCProfileMain: 237 | return "Main"; 238 | case CodecProfileLevel.AVCProfileExtended: 239 | return "Extends"; 240 | case CodecProfileLevel.AVCProfileHigh: 241 | return "High"; 242 | case CodecProfileLevel.AVCProfileHigh10: 243 | return "High10"; 244 | case CodecProfileLevel.AVCProfileHigh422: 245 | return "High422"; 246 | case CodecProfileLevel.AVCProfileHigh444: 247 | return "High444"; 248 | default: 249 | return "Unknown"; 250 | } 251 | } 252 | 253 | public static String getLevelName(int level) { 254 | switch (level) { 255 | case CodecProfileLevel.AVCLevel1: 256 | return "1"; 257 | case CodecProfileLevel.AVCLevel1b: 258 | return "1b"; 259 | case CodecProfileLevel.AVCLevel11: 260 | return "11"; 261 | case CodecProfileLevel.AVCLevel12: 262 | return "12"; 263 | case CodecProfileLevel.AVCLevel13: 264 | return "13"; 265 | case CodecProfileLevel.AVCLevel2: 266 | return "2"; 267 | case CodecProfileLevel.AVCLevel21: 268 | return "21"; 269 | case CodecProfileLevel.AVCLevel22: 270 | return "22"; 271 | case CodecProfileLevel.AVCLevel3: 272 | return "3"; 273 | case CodecProfileLevel.AVCLevel31: 274 | return "31"; 275 | case CodecProfileLevel.AVCLevel32: 276 | return "32"; 277 | case CodecProfileLevel.AVCLevel4: 278 | return "4"; 279 | case CodecProfileLevel.AVCLevel41: 280 | return "41"; 281 | case CodecProfileLevel.AVCLevel42: 282 | return "42"; 283 | case CodecProfileLevel.AVCLevel5: 284 | return "5"; 285 | case CodecProfileLevel.AVCLevel51: 286 | return "51"; 287 | case 65536: // CodecProfileLevel.AVCLevel52: 288 | return "52"; 289 | default: 290 | return "0"; 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/IjkTimedText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Zheng Yuan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package tv.danmaku.ijk.media.player; 18 | 19 | import android.graphics.Rect; 20 | import java.lang.String; 21 | 22 | public final class IjkTimedText { 23 | 24 | private Rect mTextBounds = null; 25 | private String mTextChars = null; 26 | 27 | public IjkTimedText(Rect bounds, String text) { 28 | mTextBounds = bounds; 29 | mTextChars = text; 30 | } 31 | 32 | public Rect getBounds() { 33 | return mTextBounds; 34 | } 35 | 36 | public String getText() { 37 | return mTextChars; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | public class MediaInfo { 21 | public String mMediaPlayerName; 22 | 23 | public String mVideoDecoder; 24 | public String mVideoDecoderImpl; 25 | 26 | public String mAudioDecoder; 27 | public String mAudioDecoderImpl; 28 | 29 | public IjkMediaMeta mMeta; 30 | } 31 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import android.annotation.TargetApi; 21 | import android.content.Context; 22 | import android.net.Uri; 23 | import android.os.Build; 24 | import android.view.Surface; 25 | import android.view.SurfaceHolder; 26 | 27 | import java.io.FileDescriptor; 28 | import java.io.IOException; 29 | import java.util.Map; 30 | 31 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource; 32 | import tv.danmaku.ijk.media.player.misc.ITrackInfo; 33 | 34 | public class MediaPlayerProxy implements IMediaPlayer { 35 | protected final IMediaPlayer mBackEndMediaPlayer; 36 | 37 | public MediaPlayerProxy(IMediaPlayer backEndMediaPlayer) { 38 | mBackEndMediaPlayer = backEndMediaPlayer; 39 | } 40 | 41 | public IMediaPlayer getInternalMediaPlayer() { 42 | return mBackEndMediaPlayer; 43 | } 44 | 45 | @Override 46 | public void setDisplay(SurfaceHolder sh) { 47 | mBackEndMediaPlayer.setDisplay(sh); 48 | } 49 | 50 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 51 | @Override 52 | public void setSurface(Surface surface) { 53 | mBackEndMediaPlayer.setSurface(surface); 54 | } 55 | 56 | @Override 57 | public void setDataSource(Context context, Uri uri) 58 | throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 59 | mBackEndMediaPlayer.setDataSource(context, uri); 60 | } 61 | 62 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 63 | @Override 64 | public void setDataSource(Context context, Uri uri, Map headers) 65 | throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 66 | mBackEndMediaPlayer.setDataSource(context, uri, headers); 67 | } 68 | 69 | @Override 70 | public void setDataSource(FileDescriptor fd) 71 | throws IOException, IllegalArgumentException, IllegalStateException { 72 | mBackEndMediaPlayer.setDataSource(fd); 73 | } 74 | 75 | @Override 76 | public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 77 | mBackEndMediaPlayer.setDataSource(path); 78 | } 79 | 80 | @Override 81 | public void setDataSource(IMediaDataSource mediaDataSource) { 82 | mBackEndMediaPlayer.setDataSource(mediaDataSource); 83 | } 84 | 85 | @Override 86 | public String getDataSource() { 87 | return mBackEndMediaPlayer.getDataSource(); 88 | } 89 | 90 | @Override 91 | public void prepareAsync() throws IllegalStateException { 92 | mBackEndMediaPlayer.prepareAsync(); 93 | } 94 | 95 | @Override 96 | public void start() throws IllegalStateException { 97 | mBackEndMediaPlayer.start(); 98 | } 99 | 100 | @Override 101 | public void stop() throws IllegalStateException { 102 | mBackEndMediaPlayer.stop(); 103 | } 104 | 105 | @Override 106 | public void pause() throws IllegalStateException { 107 | mBackEndMediaPlayer.pause(); 108 | } 109 | 110 | @Override 111 | public void setScreenOnWhilePlaying(boolean screenOn) { 112 | mBackEndMediaPlayer.setScreenOnWhilePlaying(screenOn); 113 | } 114 | 115 | @Override 116 | public int getVideoWidth() { 117 | return mBackEndMediaPlayer.getVideoWidth(); 118 | } 119 | 120 | @Override 121 | public int getVideoHeight() { 122 | return mBackEndMediaPlayer.getVideoHeight(); 123 | } 124 | 125 | @Override 126 | public boolean isPlaying() { 127 | return mBackEndMediaPlayer.isPlaying(); 128 | } 129 | 130 | @Override 131 | public void seekTo(long msec) throws IllegalStateException { 132 | mBackEndMediaPlayer.seekTo(msec); 133 | } 134 | 135 | @Override 136 | public long getCurrentPosition() { 137 | return mBackEndMediaPlayer.getCurrentPosition(); 138 | } 139 | 140 | @Override 141 | public long getDuration() { 142 | return mBackEndMediaPlayer.getDuration(); 143 | } 144 | 145 | @Override 146 | public void release() { 147 | mBackEndMediaPlayer.release(); 148 | } 149 | 150 | @Override 151 | public void reset() { 152 | mBackEndMediaPlayer.reset(); 153 | } 154 | 155 | @Override 156 | public void setVolume(float leftVolume, float rightVolume) { 157 | mBackEndMediaPlayer.setVolume(leftVolume, rightVolume); 158 | } 159 | 160 | @Override 161 | public int getAudioSessionId() { 162 | return mBackEndMediaPlayer.getAudioSessionId(); 163 | } 164 | 165 | @Override 166 | public MediaInfo getMediaInfo() { 167 | return mBackEndMediaPlayer.getMediaInfo(); 168 | } 169 | 170 | @Override 171 | public void setLogEnabled(boolean enable) { 172 | 173 | } 174 | 175 | @Override 176 | public boolean isPlayable() { 177 | return false; 178 | } 179 | 180 | @Override 181 | public void setOnPreparedListener(OnPreparedListener listener) { 182 | if (listener != null) { 183 | final OnPreparedListener finalListener = listener; 184 | mBackEndMediaPlayer.setOnPreparedListener(new OnPreparedListener() { 185 | @Override 186 | public void onPrepared(IMediaPlayer mp) { 187 | finalListener.onPrepared(MediaPlayerProxy.this); 188 | } 189 | }); 190 | } else { 191 | mBackEndMediaPlayer.setOnPreparedListener(null); 192 | } 193 | } 194 | 195 | @Override 196 | public void setOnCompletionListener(OnCompletionListener listener) { 197 | if (listener != null) { 198 | final OnCompletionListener finalListener = listener; 199 | mBackEndMediaPlayer.setOnCompletionListener(new OnCompletionListener() { 200 | @Override 201 | public void onCompletion(IMediaPlayer mp) { 202 | finalListener.onCompletion(MediaPlayerProxy.this); 203 | } 204 | }); 205 | } else { 206 | mBackEndMediaPlayer.setOnCompletionListener(null); 207 | } 208 | } 209 | 210 | @Override 211 | public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) { 212 | if (listener != null) { 213 | final OnBufferingUpdateListener finalListener = listener; 214 | mBackEndMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() { 215 | @Override 216 | public void onBufferingUpdate(IMediaPlayer mp, int percent) { 217 | finalListener.onBufferingUpdate(MediaPlayerProxy.this, percent); 218 | } 219 | }); 220 | } else { 221 | mBackEndMediaPlayer.setOnBufferingUpdateListener(null); 222 | } 223 | } 224 | 225 | @Override 226 | public void setOnSeekCompleteListener(OnSeekCompleteListener listener) { 227 | if (listener != null) { 228 | final OnSeekCompleteListener finalListener = listener; 229 | mBackEndMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() { 230 | @Override 231 | public void onSeekComplete(IMediaPlayer mp) { 232 | finalListener.onSeekComplete(MediaPlayerProxy.this); 233 | } 234 | }); 235 | } else { 236 | mBackEndMediaPlayer.setOnSeekCompleteListener(null); 237 | } 238 | } 239 | 240 | @Override 241 | public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) { 242 | if (listener != null) { 243 | final OnVideoSizeChangedListener finalListener = listener; 244 | mBackEndMediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() { 245 | @Override 246 | public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) { 247 | finalListener.onVideoSizeChanged(MediaPlayerProxy.this, width, height, sar_num, sar_den); 248 | } 249 | }); 250 | } else { 251 | mBackEndMediaPlayer.setOnVideoSizeChangedListener(null); 252 | } 253 | } 254 | 255 | @Override 256 | public void setOnErrorListener(OnErrorListener listener) { 257 | if (listener != null) { 258 | final OnErrorListener finalListener = listener; 259 | mBackEndMediaPlayer.setOnErrorListener(new OnErrorListener() { 260 | @Override 261 | public boolean onError(IMediaPlayer mp, int what, int extra) { 262 | return finalListener.onError(MediaPlayerProxy.this, what, extra); 263 | } 264 | }); 265 | } else { 266 | mBackEndMediaPlayer.setOnErrorListener(null); 267 | } 268 | } 269 | 270 | @Override 271 | public void setOnInfoListener(OnInfoListener listener) { 272 | if (listener != null) { 273 | final OnInfoListener finalListener = listener; 274 | mBackEndMediaPlayer.setOnInfoListener(new OnInfoListener() { 275 | @Override 276 | public boolean onInfo(IMediaPlayer mp, int what, int extra) { 277 | return finalListener.onInfo(MediaPlayerProxy.this, what, extra); 278 | } 279 | }); 280 | } else { 281 | mBackEndMediaPlayer.setOnInfoListener(null); 282 | } 283 | } 284 | 285 | @Override 286 | public void setOnTimedTextListener(OnTimedTextListener listener) { 287 | if (listener != null) { 288 | final OnTimedTextListener finalListener = listener; 289 | mBackEndMediaPlayer.setOnTimedTextListener(new OnTimedTextListener() { 290 | @Override 291 | public void onTimedText(IMediaPlayer mp, IjkTimedText text) { 292 | finalListener.onTimedText(MediaPlayerProxy.this, text); 293 | } 294 | }); 295 | } else { 296 | mBackEndMediaPlayer.setOnTimedTextListener(null); 297 | } 298 | } 299 | 300 | @Override 301 | public void setAudioStreamType(int streamtype) { 302 | mBackEndMediaPlayer.setAudioStreamType(streamtype); 303 | } 304 | 305 | @Override 306 | public void setKeepInBackground(boolean keepInBackground) { 307 | mBackEndMediaPlayer.setKeepInBackground(keepInBackground); 308 | } 309 | 310 | @Override 311 | public int getVideoSarNum() { 312 | return mBackEndMediaPlayer.getVideoSarNum(); 313 | } 314 | 315 | @Override 316 | public int getVideoSarDen() { 317 | return mBackEndMediaPlayer.getVideoSarDen(); 318 | } 319 | 320 | @Override 321 | public void setWakeMode(Context context, int mode) { 322 | mBackEndMediaPlayer.setWakeMode(context, mode); 323 | } 324 | 325 | @Override 326 | public ITrackInfo[] getTrackInfo() { 327 | return mBackEndMediaPlayer.getTrackInfo(); 328 | } 329 | 330 | @Override 331 | public void setLooping(boolean looping) { 332 | mBackEndMediaPlayer.setLooping(looping); 333 | } 334 | 335 | @Override 336 | public boolean isLooping() { 337 | return mBackEndMediaPlayer.isLooping(); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player; 19 | 20 | import android.annotation.TargetApi; 21 | import android.graphics.SurfaceTexture; 22 | import android.os.Build; 23 | import android.view.Surface; 24 | import android.view.SurfaceHolder; 25 | 26 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 27 | public class TextureMediaPlayer extends MediaPlayerProxy implements IMediaPlayer, ISurfaceTextureHolder { 28 | private SurfaceTexture mSurfaceTexture; 29 | private ISurfaceTextureHost mSurfaceTextureHost; 30 | 31 | public TextureMediaPlayer(IMediaPlayer backEndMediaPlayer) { 32 | super(backEndMediaPlayer); 33 | } 34 | 35 | public void releaseSurfaceTexture() { 36 | if (mSurfaceTexture != null) { 37 | if (mSurfaceTextureHost != null) { 38 | mSurfaceTextureHost.releaseSurfaceTexture(mSurfaceTexture); 39 | } else { 40 | mSurfaceTexture.release(); 41 | } 42 | mSurfaceTexture = null; 43 | } 44 | } 45 | 46 | //-------------------- 47 | // IMediaPlayer 48 | //-------------------- 49 | @Override 50 | public void reset() { 51 | super.reset(); 52 | releaseSurfaceTexture(); 53 | } 54 | 55 | @Override 56 | public void release() { 57 | super.release(); 58 | releaseSurfaceTexture(); 59 | } 60 | 61 | @Override 62 | public void setDisplay(SurfaceHolder sh) { 63 | if (mSurfaceTexture == null) 64 | super.setDisplay(sh); 65 | } 66 | 67 | @Override 68 | public void setSurface(Surface surface) { 69 | if (mSurfaceTexture == null) 70 | super.setSurface(surface); 71 | } 72 | 73 | //-------------------- 74 | // ISurfaceTextureHolder 75 | //-------------------- 76 | 77 | @Override 78 | public void setSurfaceTexture(SurfaceTexture surfaceTexture) { 79 | if (mSurfaceTexture == surfaceTexture) 80 | return; 81 | 82 | releaseSurfaceTexture(); 83 | mSurfaceTexture = surfaceTexture; 84 | if (surfaceTexture == null) { 85 | super.setSurface(null); 86 | } else { 87 | super.setSurface(new Surface(surfaceTexture)); 88 | } 89 | } 90 | 91 | @Override 92 | public SurfaceTexture getSurfaceTexture() { 93 | return mSurfaceTexture; 94 | } 95 | 96 | @Override 97 | public void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost) { 98 | mSurfaceTextureHost = surfaceTextureHost; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.annotations; 19 | 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * is used by the JNI generator to create the necessary JNI 27 | * bindings and expose this method to native code. 28 | */ 29 | @Target(ElementType.FIELD) 30 | @Retention(RetentionPolicy.CLASS) 31 | public @interface AccessedByNative { 32 | } -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.annotations; 19 | 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * is used by the JNI generator to create the necessary JNI 27 | * bindings and expose this method to native code. 28 | */ 29 | @Target(ElementType.METHOD) 30 | @Retention(RetentionPolicy.CLASS) 31 | public @interface CalledByNative { 32 | /* 33 | * If present, tells which inner class the method belongs to. 34 | */ 35 | String value() default ""; 36 | } -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Bilibili 3 | * Copyright (C) 2013-2014 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.exceptions; 19 | 20 | public class IjkMediaException extends Exception { 21 | private static final long serialVersionUID = 7234796519009099506L; 22 | } 23 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java: -------------------------------------------------------------------------------- 1 | package tv.danmaku.ijk.media.player.ffmpeg; 2 | 3 | public class FFmpegApi { 4 | public static native String av_base64_encode(byte in[]); 5 | } 6 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import android.annotation.TargetApi; 21 | import android.media.MediaFormat; 22 | import android.os.Build; 23 | 24 | public class AndroidMediaFormat implements IMediaFormat { 25 | private final MediaFormat mMediaFormat; 26 | 27 | public AndroidMediaFormat(MediaFormat mediaFormat) { 28 | mMediaFormat = mediaFormat; 29 | } 30 | 31 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 32 | @Override 33 | public int getInteger(String name) { 34 | if (mMediaFormat == null) 35 | return 0; 36 | 37 | return mMediaFormat.getInteger(name); 38 | } 39 | 40 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 41 | @Override 42 | public String getString(String name) { 43 | if (mMediaFormat == null) 44 | return null; 45 | 46 | return mMediaFormat.getString(name); 47 | } 48 | 49 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 50 | @Override 51 | public String toString() { 52 | StringBuilder out = new StringBuilder(128); 53 | out.append(getClass().getName()); 54 | out.append('{'); 55 | if (mMediaFormat != null) { 56 | out.append(mMediaFormat.toString()); 57 | } else { 58 | out.append("null"); 59 | } 60 | out.append('}'); 61 | return out.toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import android.annotation.TargetApi; 21 | import android.media.MediaFormat; 22 | import android.media.MediaPlayer; 23 | import android.os.Build; 24 | 25 | public class AndroidTrackInfo implements ITrackInfo { 26 | private final MediaPlayer.TrackInfo mTrackInfo; 27 | 28 | public static AndroidTrackInfo[] fromMediaPlayer(MediaPlayer mp) { 29 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 30 | return fromTrackInfo(mp.getTrackInfo()); 31 | 32 | return null; 33 | } 34 | 35 | private static AndroidTrackInfo[] fromTrackInfo(MediaPlayer.TrackInfo[] trackInfos) { 36 | if (trackInfos == null) 37 | return null; 38 | 39 | AndroidTrackInfo androidTrackInfo[] = new AndroidTrackInfo[trackInfos.length]; 40 | for (int i = 0; i < trackInfos.length; ++i) { 41 | androidTrackInfo[i] = new AndroidTrackInfo(trackInfos[i]); 42 | } 43 | 44 | return androidTrackInfo; 45 | } 46 | 47 | private AndroidTrackInfo(MediaPlayer.TrackInfo trackInfo) { 48 | mTrackInfo = trackInfo; 49 | } 50 | 51 | @TargetApi(Build.VERSION_CODES.KITKAT) 52 | @Override 53 | public IMediaFormat getFormat() { 54 | if (mTrackInfo == null) 55 | return null; 56 | 57 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) 58 | return null; 59 | 60 | MediaFormat mediaFormat = mTrackInfo.getFormat(); 61 | if (mediaFormat == null) 62 | return null; 63 | 64 | return new AndroidMediaFormat(mediaFormat); 65 | } 66 | 67 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 68 | @Override 69 | public String getLanguage() { 70 | if (mTrackInfo == null) 71 | return "und"; 72 | 73 | return mTrackInfo.getLanguage(); 74 | } 75 | 76 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 77 | @Override 78 | public int getTrackType() { 79 | if (mTrackInfo == null) 80 | return MEDIA_TRACK_TYPE_UNKNOWN; 81 | 82 | return mTrackInfo.getTrackType(); 83 | } 84 | 85 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 86 | @Override 87 | public String toString() { 88 | StringBuilder out = new StringBuilder(128); 89 | out.append(getClass().getSimpleName()); 90 | out.append('{'); 91 | if (mTrackInfo != null) { 92 | out.append(mTrackInfo.toString()); 93 | } else { 94 | out.append("null"); 95 | } 96 | out.append('}'); 97 | return out.toString(); 98 | } 99 | 100 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 101 | @Override 102 | public String getInfoInline() { 103 | if (mTrackInfo != null) { 104 | return mTrackInfo.toString(); 105 | } else { 106 | return "null"; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/IAndroidIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bilibili 3 | * Copyright (C) 2016 Raymond Zheng 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import java.io.IOException; 21 | 22 | @SuppressWarnings("RedundantThrows") 23 | public interface IAndroidIO { 24 | int open(String url) throws IOException; 25 | int read(byte[] buffer, int size) throws IOException; 26 | long seek(long offset, int whence) throws IOException; 27 | int close() throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import java.io.IOException; 21 | 22 | @SuppressWarnings("RedundantThrows") 23 | public interface IMediaDataSource { 24 | int readAt(long position, byte[] buffer, int offset, int size) throws IOException; 25 | 26 | long getSize() throws IOException; 27 | 28 | void close() throws IOException; 29 | } 30 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | public interface IMediaFormat { 21 | // Common keys 22 | String KEY_MIME = "mime"; 23 | 24 | // Video Keys 25 | String KEY_WIDTH = "width"; 26 | String KEY_HEIGHT = "height"; 27 | 28 | String getString(String name); 29 | 30 | int getInteger(String name); 31 | } 32 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | public interface ITrackInfo { 21 | int MEDIA_TRACK_TYPE_AUDIO = 2; 22 | int MEDIA_TRACK_TYPE_METADATA = 5; 23 | int MEDIA_TRACK_TYPE_SUBTITLE = 4; 24 | int MEDIA_TRACK_TYPE_TIMEDTEXT = 3; 25 | int MEDIA_TRACK_TYPE_UNKNOWN = 0; 26 | int MEDIA_TRACK_TYPE_VIDEO = 1; 27 | 28 | IMediaFormat getFormat(); 29 | 30 | String getLanguage(); 31 | 32 | int getTrackType(); 33 | 34 | String getInfoInline(); 35 | } 36 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import android.annotation.TargetApi; 21 | import android.os.Build; 22 | import android.text.TextUtils; 23 | 24 | import java.util.HashMap; 25 | import java.util.Locale; 26 | import java.util.Map; 27 | 28 | import tv.danmaku.ijk.media.player.IjkMediaMeta; 29 | 30 | public class IjkMediaFormat implements IMediaFormat { 31 | // Common 32 | public static final String KEY_IJK_CODEC_LONG_NAME_UI = "ijk-codec-long-name-ui"; 33 | public static final String KEY_IJK_CODEC_NAME_UI = "ijk-codec-name-ui"; 34 | public static final String KEY_IJK_BIT_RATE_UI = "ijk-bit-rate-ui"; 35 | 36 | // Video 37 | public static final String KEY_IJK_CODEC_PROFILE_LEVEL_UI = "ijk-profile-level-ui"; 38 | public static final String KEY_IJK_CODEC_PIXEL_FORMAT_UI = "ijk-pixel-format-ui"; 39 | public static final String KEY_IJK_RESOLUTION_UI = "ijk-resolution-ui"; 40 | public static final String KEY_IJK_FRAME_RATE_UI = "ijk-frame-rate-ui"; 41 | 42 | // Audio 43 | public static final String KEY_IJK_SAMPLE_RATE_UI = "ijk-sample-rate-ui"; 44 | public static final String KEY_IJK_CHANNEL_UI = "ijk-channel-ui"; 45 | 46 | // Codec 47 | public static final String CODEC_NAME_H264 = "h264"; 48 | 49 | public final IjkMediaMeta.IjkStreamMeta mMediaFormat; 50 | 51 | public IjkMediaFormat(IjkMediaMeta.IjkStreamMeta streamMeta) { 52 | mMediaFormat = streamMeta; 53 | } 54 | 55 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 56 | @Override 57 | public int getInteger(String name) { 58 | if (mMediaFormat == null) 59 | return 0; 60 | 61 | return mMediaFormat.getInt(name); 62 | } 63 | 64 | @Override 65 | public String getString(String name) { 66 | if (mMediaFormat == null) 67 | return null; 68 | 69 | if (sFormatterMap.containsKey(name)) { 70 | Formatter formatter = sFormatterMap.get(name); 71 | return formatter.format(this); 72 | } 73 | 74 | return mMediaFormat.getString(name); 75 | } 76 | 77 | //------------------------- 78 | // Formatter 79 | //------------------------- 80 | 81 | private static abstract class Formatter { 82 | public String format(IjkMediaFormat mediaFormat) { 83 | String value = doFormat(mediaFormat); 84 | if (TextUtils.isEmpty(value)) 85 | return getDefaultString(); 86 | return value; 87 | } 88 | 89 | protected abstract String doFormat(IjkMediaFormat mediaFormat); 90 | 91 | @SuppressWarnings("SameReturnValue") 92 | protected String getDefaultString() { 93 | return "N/A"; 94 | } 95 | } 96 | 97 | private static final Map sFormatterMap = new HashMap(); 98 | 99 | { 100 | sFormatterMap.put(KEY_IJK_CODEC_LONG_NAME_UI, new Formatter() { 101 | @Override 102 | public String doFormat(IjkMediaFormat mediaFormat) { 103 | return mMediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_LONG_NAME); 104 | } 105 | }); 106 | sFormatterMap.put(KEY_IJK_CODEC_NAME_UI, new Formatter() { 107 | @Override 108 | public String doFormat(IjkMediaFormat mediaFormat) { 109 | return mMediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_NAME); 110 | } 111 | }); 112 | sFormatterMap.put(KEY_IJK_BIT_RATE_UI, new Formatter() { 113 | @Override 114 | protected String doFormat(IjkMediaFormat mediaFormat) { 115 | int bitRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_BITRATE); 116 | if (bitRate <= 0) { 117 | return null; 118 | } else if (bitRate < 1000) { 119 | return String.format(Locale.US, "%d bit/s", bitRate); 120 | } else { 121 | return String.format(Locale.US, "%d kb/s", bitRate / 1000); 122 | } 123 | } 124 | }); 125 | sFormatterMap.put(KEY_IJK_CODEC_PROFILE_LEVEL_UI, new Formatter() { 126 | @Override 127 | protected String doFormat(IjkMediaFormat mediaFormat) { 128 | int profileIndex = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE_ID); 129 | String profile; 130 | switch (profileIndex) { 131 | case IjkMediaMeta.FF_PROFILE_H264_BASELINE: 132 | profile = "Baseline"; 133 | break; 134 | case IjkMediaMeta.FF_PROFILE_H264_CONSTRAINED_BASELINE: 135 | profile = "Constrained Baseline"; 136 | break; 137 | case IjkMediaMeta.FF_PROFILE_H264_MAIN: 138 | profile = "Main"; 139 | break; 140 | case IjkMediaMeta.FF_PROFILE_H264_EXTENDED: 141 | profile = "Extended"; 142 | break; 143 | case IjkMediaMeta.FF_PROFILE_H264_HIGH: 144 | profile = "High"; 145 | break; 146 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_10: 147 | profile = "High 10"; 148 | break; 149 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_10_INTRA: 150 | profile = "High 10 Intra"; 151 | break; 152 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_422: 153 | profile = "High 4:2:2"; 154 | break; 155 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_422_INTRA: 156 | profile = "High 4:2:2 Intra"; 157 | break; 158 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_444: 159 | profile = "High 4:4:4"; 160 | break; 161 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_444_PREDICTIVE: 162 | profile = "High 4:4:4 Predictive"; 163 | break; 164 | case IjkMediaMeta.FF_PROFILE_H264_HIGH_444_INTRA: 165 | profile = "High 4:4:4 Intra"; 166 | break; 167 | case IjkMediaMeta.FF_PROFILE_H264_CAVLC_444: 168 | profile = "CAVLC 4:4:4"; 169 | break; 170 | default: 171 | return null; 172 | } 173 | 174 | StringBuilder sb = new StringBuilder(); 175 | sb.append(profile); 176 | 177 | String codecName = mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_NAME); 178 | if (!TextUtils.isEmpty(codecName) && codecName.equalsIgnoreCase(CODEC_NAME_H264)) { 179 | int level = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CODEC_LEVEL); 180 | if (level < 10) 181 | return sb.toString(); 182 | 183 | sb.append(" Profile Level "); 184 | sb.append((level / 10) % 10); 185 | if ((level % 10) != 0) { 186 | sb.append("."); 187 | sb.append(level % 10); 188 | } 189 | } 190 | 191 | return sb.toString(); 192 | } 193 | }); 194 | sFormatterMap.put(KEY_IJK_CODEC_PIXEL_FORMAT_UI, new Formatter() { 195 | @Override 196 | protected String doFormat(IjkMediaFormat mediaFormat) { 197 | return mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_PIXEL_FORMAT); 198 | } 199 | }); 200 | sFormatterMap.put(KEY_IJK_RESOLUTION_UI, new Formatter() { 201 | @Override 202 | protected String doFormat(IjkMediaFormat mediaFormat) { 203 | int width = mediaFormat.getInteger(KEY_WIDTH); 204 | int height = mediaFormat.getInteger(KEY_HEIGHT); 205 | int sarNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_NUM); 206 | int sarDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_DEN); 207 | 208 | if (width <= 0 || height <= 0) { 209 | return null; 210 | } else if (sarNum <= 0 || sarDen <= 0) { 211 | return String.format(Locale.US, "%d x %d", width, height); 212 | } else { 213 | return String.format(Locale.US, "%d x %d [SAR %d:%d]", width, 214 | height, sarNum, sarDen); 215 | } 216 | } 217 | }); 218 | sFormatterMap.put(KEY_IJK_FRAME_RATE_UI, new Formatter() { 219 | @Override 220 | protected String doFormat(IjkMediaFormat mediaFormat) { 221 | int fpsNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_NUM); 222 | int fpsDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_DEN); 223 | if (fpsNum <= 0 || fpsDen <= 0) { 224 | return null; 225 | } else { 226 | return String.valueOf(((float) (fpsNum)) / fpsDen); 227 | } 228 | } 229 | }); 230 | sFormatterMap.put(KEY_IJK_SAMPLE_RATE_UI, new Formatter() { 231 | @Override 232 | protected String doFormat(IjkMediaFormat mediaFormat) { 233 | int sampleRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAMPLE_RATE); 234 | if (sampleRate <= 0) { 235 | return null; 236 | } else { 237 | return String.format(Locale.US, "%d Hz", sampleRate); 238 | } 239 | } 240 | }); 241 | sFormatterMap.put(KEY_IJK_CHANNEL_UI, new Formatter() { 242 | @Override 243 | protected String doFormat(IjkMediaFormat mediaFormat) { 244 | int channelLayout = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CHANNEL_LAYOUT); 245 | if (channelLayout <= 0) { 246 | return null; 247 | } else { 248 | if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_MONO) { 249 | return "mono"; 250 | } else if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_STEREO) { 251 | return "stereo"; 252 | } else { 253 | return String.format(Locale.US, "%x", channelLayout); 254 | } 255 | } 256 | } 257 | }); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Bilibili 3 | * Copyright (C) 2015 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.misc; 19 | 20 | import android.text.TextUtils; 21 | 22 | import tv.danmaku.ijk.media.player.IjkMediaMeta; 23 | 24 | public class IjkTrackInfo implements ITrackInfo { 25 | private int mTrackType = MEDIA_TRACK_TYPE_UNKNOWN; 26 | private IjkMediaMeta.IjkStreamMeta mStreamMeta; 27 | 28 | public IjkTrackInfo(IjkMediaMeta.IjkStreamMeta streamMeta) { 29 | mStreamMeta = streamMeta; 30 | } 31 | 32 | public void setMediaMeta(IjkMediaMeta.IjkStreamMeta streamMeta) { 33 | mStreamMeta = streamMeta; 34 | } 35 | 36 | @Override 37 | public IMediaFormat getFormat() { 38 | return new IjkMediaFormat(mStreamMeta); 39 | } 40 | 41 | @Override 42 | public String getLanguage() { 43 | if (mStreamMeta == null || TextUtils.isEmpty(mStreamMeta.mLanguage)) 44 | return "und"; 45 | 46 | return mStreamMeta.mLanguage; 47 | } 48 | 49 | @Override 50 | public int getTrackType() { 51 | return mTrackType; 52 | } 53 | 54 | public void setTrackType(int trackType) { 55 | mTrackType = trackType; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return getClass().getSimpleName() + '{' + getInfoInline() + "}"; 61 | } 62 | 63 | @Override 64 | public String getInfoInline() { 65 | StringBuilder out = new StringBuilder(128); 66 | switch (mTrackType) { 67 | case MEDIA_TRACK_TYPE_VIDEO: 68 | out.append("VIDEO"); 69 | out.append(", "); 70 | out.append(mStreamMeta.getCodecShortNameInline()); 71 | out.append(", "); 72 | out.append(mStreamMeta.getBitrateInline()); 73 | out.append(", "); 74 | out.append(mStreamMeta.getResolutionInline()); 75 | break; 76 | case MEDIA_TRACK_TYPE_AUDIO: 77 | out.append("AUDIO"); 78 | out.append(", "); 79 | out.append(mStreamMeta.getCodecShortNameInline()); 80 | out.append(", "); 81 | out.append(mStreamMeta.getBitrateInline()); 82 | out.append(", "); 83 | out.append(mStreamMeta.getSampleRateInline()); 84 | break; 85 | case MEDIA_TRACK_TYPE_TIMEDTEXT: 86 | out.append("TIMEDTEXT"); 87 | out.append(", "); 88 | out.append(mStreamMeta.mLanguage); 89 | break; 90 | case MEDIA_TRACK_TYPE_SUBTITLE: 91 | out.append("SUBTITLE"); 92 | break; 93 | default: 94 | out.append("UNKNOWN"); 95 | break; 96 | } 97 | return out.toString(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Bilibili 3 | * Copyright (C) 2013 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package tv.danmaku.ijk.media.player.pragma; 19 | 20 | import java.util.Locale; 21 | 22 | 23 | import android.util.Log; 24 | 25 | @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) 26 | public class DebugLog { 27 | public static final boolean ENABLE_ERROR = Pragma.ENABLE_VERBOSE; 28 | public static final boolean ENABLE_INFO = Pragma.ENABLE_VERBOSE; 29 | public static final boolean ENABLE_WARN = Pragma.ENABLE_VERBOSE; 30 | public static final boolean ENABLE_DEBUG = Pragma.ENABLE_VERBOSE; 31 | public static final boolean ENABLE_VERBOSE = Pragma.ENABLE_VERBOSE; 32 | 33 | public static void e(String tag, String msg) { 34 | if (ENABLE_ERROR) { 35 | Log.e(tag, msg); 36 | } 37 | } 38 | 39 | public static void e(String tag, String msg, Throwable tr) { 40 | if (ENABLE_ERROR) { 41 | Log.e(tag, msg, tr); 42 | } 43 | } 44 | 45 | public static void efmt(String tag, String fmt, Object... args) { 46 | if (ENABLE_ERROR) { 47 | String msg = String.format(Locale.US, fmt, args); 48 | Log.e(tag, msg); 49 | } 50 | } 51 | 52 | public static void i(String tag, String msg) { 53 | if (ENABLE_INFO) { 54 | Log.i(tag, msg); 55 | } 56 | } 57 | 58 | public static void i(String tag, String msg, Throwable tr) { 59 | if (ENABLE_INFO) { 60 | Log.i(tag, msg, tr); 61 | } 62 | } 63 | 64 | public static void ifmt(String tag, String fmt, Object... args) { 65 | if (ENABLE_INFO) { 66 | String msg = String.format(Locale.US, fmt, args); 67 | Log.i(tag, msg); 68 | } 69 | } 70 | 71 | public static void w(String tag, String msg) { 72 | if (ENABLE_WARN) { 73 | Log.w(tag, msg); 74 | } 75 | } 76 | 77 | public static void w(String tag, String msg, Throwable tr) { 78 | if (ENABLE_WARN) { 79 | Log.w(tag, msg, tr); 80 | } 81 | } 82 | 83 | public static void wfmt(String tag, String fmt, Object... args) { 84 | if (ENABLE_WARN) { 85 | String msg = String.format(Locale.US, fmt, args); 86 | Log.w(tag, msg); 87 | } 88 | } 89 | 90 | public static void d(String tag, String msg) { 91 | if (ENABLE_DEBUG) { 92 | Log.d(tag, msg); 93 | } 94 | } 95 | 96 | public static void d(String tag, String msg, Throwable tr) { 97 | if (ENABLE_DEBUG) { 98 | Log.d(tag, msg, tr); 99 | } 100 | } 101 | 102 | public static void dfmt(String tag, String fmt, Object... args) { 103 | if (ENABLE_DEBUG) { 104 | String msg = String.format(Locale.US, fmt, args); 105 | Log.d(tag, msg); 106 | } 107 | } 108 | 109 | public static void v(String tag, String msg) { 110 | if (ENABLE_VERBOSE) { 111 | Log.v(tag, msg); 112 | } 113 | } 114 | 115 | public static void v(String tag, String msg, Throwable tr) { 116 | if (ENABLE_VERBOSE) { 117 | Log.v(tag, msg, tr); 118 | } 119 | } 120 | 121 | public static void vfmt(String tag, String fmt, Object... args) { 122 | if (ENABLE_VERBOSE) { 123 | String msg = String.format(Locale.US, fmt, args); 124 | Log.v(tag, msg); 125 | } 126 | } 127 | 128 | public static void printStackTrace(Throwable e) { 129 | if (ENABLE_WARN) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | 134 | public static void printCause(Throwable e) { 135 | if (ENABLE_WARN) { 136 | Throwable cause = e.getCause(); 137 | if (cause != null) 138 | e = cause; 139 | 140 | printStackTrace(e); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /ijk/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Bilibili 3 | * Copyright (C) 2013 Zhang Rui 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package tv.danmaku.ijk.media.player.pragma; 18 | 19 | /*- 20 | * configurated by app project 21 | */ 22 | public class Pragma { 23 | public static final boolean ENABLE_VERBOSE = true; 24 | } 25 | -------------------------------------------------------------------------------- /ijk/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ijk 3 | 4 | -------------------------------------------------------------------------------- /ijk/src/test/java/tv/danmaku/ijk/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package tv.danmaku.ijk; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':ijk' 2 | --------------------------------------------------------------------------------