=====================================================

最简单的基于FFmpeg的移动端样例系列文章列表:

最简单的基于FFmpeg的移动端样例:Android HelloWorld

最简单的基于FFmpeg的移动端样例:Android 视频解码器

最简单的基于FFmpeg的移动端样例:Android 视频解码器-单个库版

最简单的基于FFmpeg的移动端样例:Android 推流器

最简单的基于FFmpeg的移动端样例:Android 视频转码器

最简单的基于FFmpeg的移动端样例附件:Android 自带播放器

最简单的基于FFmpeg的移动端样例附件:SDL Android HelloWorld

最简单的基于FFmpeg的移动端样例:IOS HelloWorld

最简单的基于FFmpeg的移动端样例:IOS 视频解码器

最简单的基于FFmpeg的移动端样例:IOS 推流器

最简单的基于FFmpeg的移动端样例:IOS 视频转码器

最简单的基于FFmpeg的移动端样例附件:IOS自带播放器

最简单的基于FFmpeg的移动端样例:Windows Phone HelloWorld

=====================================================

本文记录还有一个安卓平台下基于FFmpeg的视频解码器。与前一篇文章记录的解码器不同。本文记录的解码器不再使用libavcodec.so、libavformat.so等类库,而仅仅使用了一个类库——libffmpeg.so。

该视频解码器C语言的源码来自于《最简单的基于FFMPEG+SDL的视频播放器》。相关的概念就不再反复记录了。

FFmpeg类库的打包

记录一下FFmpeg类库打包的方法。Android平台下FFmpeg类库一共包括以下几个:

libavformat-56.so
libavcodec-56.so
libavfilter-5.so
libavdevice-56.so
libavutil-54.so
libpostproc-53.so
libswresample-1.so
libswscale-3.so

因为数目繁多,直接使用这些类库还是比較麻烦的。因此能够将它们合并为一个类库。

详细打包的命令就是以下脚本中“make install”后面的那个命令。

  1. cd ffmpeg
  2.  
  3. make clean
  4.  
  5. export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d
  6. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
  7. export PLATFORM=$NDK/platforms/android-8/arch-arm
  8. export PREFIX=../ff-pure-onelib
  9. build_one(){
  10. ./configure --target-os=linux --prefix=$PREFIX \
  11. --enable-cross-compile \
  12. --enable-runtime-cpudetect \
  13. --disable-asm \
  14. --arch=arm \
  15. --cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \
  16. --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
  17. --disable-stripping \
  18. --nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \
  19. --sysroot=$PLATFORM \
  20. --enable-gpl --enable-static --disable-shared --enable-nonfree --enable-version3 --enable-small \
  21. --enable-zlib --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
  22. --extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"
  23. }
  24.  
  25. build_one
  26.  
  27. make
  28. make install
  29.  
  30. $PREBUILT/linux-x86_64/bin/arm-linux-androideabi-ld -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -L$PREFIX/lib -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $PREFIX/libffmpeg.so libavcodec/libavcodec.a libavfilter/libavfilter.a libswresample/libswresample.a libavformat/libavformat.a libavutil/libavutil.a libswscale/libswscale.a libpostproc/libpostproc.a libavdevice/libavdevice.a -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker $PREBUILT/linux-x86_64/lib/gcc/arm-linux-androideabi/4.8/libgcc.a
  31.  
  32. cd ..
  1.  

须要注意:

(1)与前面记录的脚本不同,这个脚本不再须要改动Configure的内容(生成的是*.a而不是*.so,并没有涉及到版本问题)。

(2)前文记录的脚本里面Configure的时候是“--enable-shared --disable-static”,这个脚本里面Configure的时候设置的是“--enable-static --disable-shared”。

编译完毕后生成的是:

libavcodec.a

libavfilter.a

libswresample.a

libavformat.a

libavutil.a

libswscale.a

libpostproc.a

libavdevice.a

该脚本执行完后。会把上述的*.a文件打包为1个*.so文件:

libffmpeg.so

合并后的类库使用起来和合并前的类库用法没有差别。

源码

项目的文件夹结构如图所看到的。Java源码位于src文件夹,而C代码位于jni文件夹。

 

Android程序Java端代码位于src\com\leixiaohua1020\sffmpegandroiddecoder\MainActivity.java,例如以下所看到的。

  1. /**
  2. * 最简单的基于FFmpeg的视频解码器-安卓 - 单库版
  3. * Simplest FFmpeg Android Decoder - One Library
  4. *
  5. * 雷霄骅 Lei Xiaohua
  6. * leixiaohua1020@126.com
  7. * 中国传媒大学/数字电视技术
  8. * Communication University of China / Digital TV Technology
  9. * http://blog.csdn.net/leixiaohua1020
  10. *
  11. * 本程序是安卓平台下最简单的基于FFmpeg的视频解码器。
  12. * 它能够将输入的视频数据解码成YUV像素数据。
  13. *
  14. * This software is the simplest decoder based on FFmpeg in Android.
  15. * It can decode video stream to raw YUV data.
  16. *
  17. */
  18. package com.leixiaohua1020.sffmpegandroiddecoder;
  19.  
  20. import android.os.Bundle;
  21. import android.os.Environment;
  22. import android.app.Activity;
  23. import android.text.Editable;
  24. import android.util.Log;
  25. import android.view.Menu;
  26. import android.view.View;
  27. import android.view.View.OnClickListener;
  28. import android.widget.Button;
  29. import android.widget.EditText;
  30. import android.widget.TextView;
  31.  
  32. public class MainActivity extends Activity {
  33.  
  34. @Override
  35. protected void onCreate(Bundle savedInstanceState) {
  36. super.onCreate(savedInstanceState);
  37. setContentView(R.layout.activity_main);
  38.  
  39. Button startButton = (Button) this.findViewById(R.id.button_start);
  40. final EditText urlEdittext_input= (EditText) this.findViewById(R.id.input_url);
  41. final EditText urlEdittext_output= (EditText) this.findViewById(R.id.output_url);
  42.  
  43. startButton.setOnClickListener(new OnClickListener() {
  44. public void onClick(View arg0){
  45.  
  46. String folderurl=Environment.getExternalStorageDirectory().getPath();
  47.  
  48. String urltext_input=urlEdittext_input.getText().toString();
  49. String inputurl=folderurl+"/"+urltext_input;
  50.  
  51. String urltext_output=urlEdittext_output.getText().toString();
  52. String outputurl=folderurl+"/"+urltext_output;
  53.  
  54. Log.i("inputurl",inputurl);
  55. Log.i("outputurl",outputurl);
  56.  
  57. decode(inputurl,outputurl);
  58.  
  59. }
  60. });
  61. }
  62.  
  63. @Override
  64. public boolean onCreateOptionsMenu(Menu menu) {
  65. // Inflate the menu; this adds items to the action bar if it is present.
  66. getMenuInflater().inflate(R.menu.main, menu);
  67. return true;
  68. }
  69.  
  70. //JNI
  71. public native int decode(String inputurl, String outputurl);
  72.  
  73. static{
  74. System.loadLibrary("ffmpeg");
  75. System.loadLibrary("sffdecoder");
  76. }
  77. }

C语言端源码位于jni/simplest_ffmpeg_decoder.c,例如以下所看到的。

  1. /**
  2. * 最简单的基于FFmpeg的视频解码器-安卓 - 单库版
  3. * Simplest FFmpeg Android Decoder - One Library
  4. *
  5. * 雷霄骅 Lei Xiaohua
  6. * leixiaohua1020@126.com
  7. * 中国传媒大学/数字电视技术
  8. * Communication University of China / Digital TV Technology
  9. * http://blog.csdn.net/leixiaohua1020
  10. *
  11. * 本程序是安卓平台下最简单的基于FFmpeg的视频解码器。
  12. * 它能够将输入的视频数据解码成YUV像素数据。
  13. *
  14. * This software is the simplest decoder based on FFmpeg in Android.
  15. * It can decode video stream to raw YUV data.
  16. *
  17. */
  18.  
  19. #include <stdio.h>
  20. #include <time.h>
  21.  
  22. #include "libavcodec/avcodec.h"
  23. #include "libavformat/avformat.h"
  24. #include "libswscale/swscale.h"
  25. #include "libavutil/log.h"
  26.  
  27. #ifdef ANDROID
  28. #include <jni.h>
  29. #include <android/log.h>
  30. #define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
  31. #define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, "(^_^)", format, ##__VA_ARGS__)
  32. #else
  33. #define LOGE(format, ...) printf("(>_<) " format "\n", ##__VA_ARGS__)
  34. #define LOGI(format, ...) printf("(^_^) " format "\n", ##__VA_ARGS__)
  35. #endif
  36.  
  37. //Output FFmpeg's av_log()
  38. void custom_log(void *ptr, int level, const char* fmt, va_list vl){
  39. FILE *fp=fopen("/storage/emulated/0/av_log.txt","a+");
  40. if(fp){
  41. vfprintf(fp,fmt,vl);
  42. fflush(fp);
  43. fclose(fp);
  44. }
  45. }
  46.  
  47. JNIEXPORT jint JNICALL Java_com_leixiaohua1020_sffmpegandroiddecoder_MainActivity_decode
  48. (JNIEnv *env, jobject obj, jstring input_jstr, jstring output_jstr)
  49. {
  50. AVFormatContext *pFormatCtx;
  51. int i, videoindex;
  52. AVCodecContext *pCodecCtx;
  53. AVCodec *pCodec;
  54. AVFrame *pFrame,*pFrameYUV;
  55. uint8_t *out_buffer;
  56. AVPacket *packet;
  57. int y_size;
  58. int ret, got_picture;
  59. struct SwsContext *img_convert_ctx;
  60. FILE *fp_yuv;
  61. int frame_cnt;
  62. clock_t time_start, time_finish;
  63. double time_duration = 0.0;
  64.  
  65. char input_str[500]={0};
  66. char output_str[500]={0};
  67. char info[1000]={0};
  68. sprintf(input_str,"%s",(*env)->GetStringUTFChars(env,input_jstr, NULL));
  69. sprintf(output_str,"%s",(*env)->GetStringUTFChars(env,output_jstr, NULL));
  70.  
  71. //FFmpeg av_log() callback
  72. av_log_set_callback(custom_log);
  73.  
  74. av_register_all();
  75. avformat_network_init();
  76. pFormatCtx = avformat_alloc_context();
  77.  
  78. if(avformat_open_input(&pFormatCtx,input_str,NULL,NULL)!=0){
  79. LOGE("Couldn't open input stream.\n");
  80. return -1;
  81. }
  82. if(avformat_find_stream_info(pFormatCtx,NULL)<0){
  83. LOGE("Couldn't find stream information.\n");
  84. return -1;
  85. }
  86. videoindex=-1;
  87. for(i=0; i<pFormatCtx->nb_streams; i++)
  88. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
  89. videoindex=i;
  90. break;
  91. }
  92. if(videoindex==-1){
  93. LOGE("Couldn't find a video stream.\n");
  94. return -1;
  95. }
  96. pCodecCtx=pFormatCtx->streams[videoindex]->codec;
  97. pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  98. if(pCodec==NULL){
  99. LOGE("Couldn't find Codec.\n");
  100. return -1;
  101. }
  102. if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
  103. LOGE("Couldn't open codec.\n");
  104. return -1;
  105. }
  106.  
  107. pFrame=av_frame_alloc();
  108. pFrameYUV=av_frame_alloc();
  109. out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1));
  110. av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
  111. AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
  112.  
  113. packet=(AVPacket *)av_malloc(sizeof(AVPacket));
  114.  
  115. img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
  116. pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  117.  
  118. sprintf(info, "[Input ]%s\n", input_str);
  119. sprintf(info, "%s[Output ]%s\n",info,output_str);
  120. sprintf(info, "%s[Format ]%s\n",info, pFormatCtx->iformat->name);
  121. sprintf(info, "%s[Codec ]%s\n",info, pCodecCtx->codec->name);
  122. sprintf(info, "%s[Resolution]%dx%d\n",info, pCodecCtx->width,pCodecCtx->height);
  123.  
  124. fp_yuv=fopen(output_str,"wb+");
  125. if(fp_yuv==NULL){
  126. printf("Cannot open output file.\n");
  127. return -1;
  128. }
  129.  
  130. frame_cnt=0;
  131. time_start = clock();
  132.  
  133. while(av_read_frame(pFormatCtx, packet)>=0){
  134. if(packet->stream_index==videoindex){
  135. ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
  136. if(ret < 0){
  137. LOGE("Decode Error.\n");
  138. return -1;
  139. }
  140. if(got_picture){
  141. sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
  142. pFrameYUV->data, pFrameYUV->linesize);
  143.  
  144. y_size=pCodecCtx->width*pCodecCtx->height;
  145. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
  146. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
  147. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
  148. //Output info
  149. char pictype_str[10]={0};
  150. switch(pFrame->pict_type){
  151. case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;
  152. case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;
  153. case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;
  154. default:sprintf(pictype_str,"Other");break;
  155. }
  156. LOGI("Frame Index: %5d. Type:%s",frame_cnt,pictype_str);
  157. frame_cnt++;
  158. }
  159. }
  160. av_free_packet(packet);
  161. }
  162. //flush decoder
  163. //FIX: Flush Frames remained in Codec
  164. while (1) {
  165. ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
  166. if (ret < 0)
  167. break;
  168. if (!got_picture)
  169. break;
  170. sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
  171. pFrameYUV->data, pFrameYUV->linesize);
  172. int y_size=pCodecCtx->width*pCodecCtx->height;
  173. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
  174. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
  175. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
  176. //Output info
  177. char pictype_str[10]={0};
  178. switch(pFrame->pict_type){
  179. case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;
  180. case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;
  181. case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;
  182. default:sprintf(pictype_str,"Other");break;
  183. }
  184. LOGI("Frame Index: %5d. Type:%s",frame_cnt,pictype_str);
  185. frame_cnt++;
  186. }
  187. time_finish = clock();
  188. time_duration=(double)(time_finish - time_start);
  189.  
  190. sprintf(info, "%s[Time ]%fms\n",info,time_duration);
  191. sprintf(info, "%s[Count ]%d\n",info,frame_cnt);
  192.  
  193. sws_freeContext(img_convert_ctx);
  194.  
  195. fclose(fp_yuv);
  196.  
  197. av_frame_free(&pFrameYUV);
  198. av_frame_free(&pFrame);
  199. avcodec_close(pCodecCtx);
  200. avformat_close_input(&pFormatCtx);
  201.  
  202. return 0;
  203. }

Android.mk文件位于jni/Android.mk,例如以下所看到的。

  1. # Android.mk for FFmpeg
  2. #
  3. # Lei Xiaohua 雷霄骅
  4. # leixiaohua1020@126.com
  5. # http://blog.csdn.net/leixiaohua1020
  6. #
  7.  
  8. LOCAL_PATH := $(call my-dir)
  9.  
  10. # FFmpeg library
  11. include $(CLEAR_VARS)
  12. LOCAL_MODULE := ffmpeg
  13. LOCAL_SRC_FILES := libffmpeg.so
  14. include $(PREBUILT_SHARED_LIBRARY)
  15.  
  16. # Program
  17. include $(CLEAR_VARS)
  18. LOCAL_MODULE := sffdecoder
  19. LOCAL_SRC_FILES :=simplest_ffmpeg_decoder.c
  20. LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
  21. LOCAL_LDLIBS := -llog -lz
  22. LOCAL_SHARED_LIBRARIES := ffmpeg
  23. include $(BUILD_SHARED_LIBRARY)

执行结果

App在手机上执行后的结果例如以下图所看到的。

 

注意须要把等待解码的视频文件拷贝至存储卡对应的文件夹中。比如对于上述截图的情况。须要将sintel.mp4拷贝至存储卡的根文件夹中。

单击“Start”button就能够将存储卡根文件夹中的视频文件解码为YUV文件(须要等待一段时间完毕解码)。注意解码后的YUV文件体积巨大,可能会占用大量的存储卡空间。

 

下载

simplest ffmpeg mobile

项目主页

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_mobile

开源中国:https://git.oschina.net/leixiaohua1020/simplest_ffmpeg_mobile

SourceForge:https://sourceforge.net/projects/simplestffmpegmobile/

CSDN工程下载地址:http://download.csdn.net/detail/leixiaohua1020/8924391

本解决方式包括了使用FFmpeg在移动端处理多媒体的各种样例:

[Android]
simplest_android_player: 基于安卓接口的视频播放器
simplest_ffmpeg_android_helloworld: 安卓平台下基于FFmpeg的HelloWorld程序
simplest_ffmpeg_android_decoder: 安卓平台下最简单的基于FFmpeg的视频解码器
simplest_ffmpeg_android_decoder_onelib: 安卓平台下最简单的基于FFmpeg的视频解码器-单库版
simplest_ffmpeg_android_streamer: 安卓平台下最简单的基于FFmpeg的推流器
simplest_ffmpeg_android_transcoder: 安卓平台下移植的FFmpeg命令行工具
simplest_sdl_android_helloworld: 移植SDL到安卓平台的最简单程序
[IOS]
simplest_ios_player: 基于IOS接口的视频播放器
simplest_ffmpeg_ios_helloworld: IOS平台下基于FFmpeg的HelloWorld程序
simplest_ffmpeg_ios_decoder: IOS平台下最简单的基于FFmpeg的视频解码器
simplest_ffmpeg_ios_streamer: IOS平台下最简单的基于FFmpeg的推流器
simplest_ffmpeg_ios_transcoder: IOS平台下移植的ffmpeg.c命令行工具
simplest_sdl_ios_helloworld: 移植SDL到IOS平台的最简单程序

最简单的基于FFmpeg的移动端样例:Android 视频解码器-单个库版的更多相关文章

  1. 最简单的基于FFmpeg的移动端例子:IOS 视频解码器-保存

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  2. 最简单的基于FFmpeg的移动端样例:IOS 视频解码器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  3. 最简单的基于FFmpeg的移动端样例附件:Android 自带播放器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  4. 最简单的基于FFmpeg的移动端样例附件:SDL Android HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  5. 最简单的基于FFmpeg的移动端样例:IOS 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  6. 最简单的基于FFmpeg的移动端样例:Android HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  7. 最简单的基于FFmpeg的移动端样例:Windows Phone HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  8. 最简单的基于FFmpeg的移动端样例:IOS 推流器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  9. 最简单的基于FFmpeg的移动端样例:Android 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

随机推荐

  1. 10个你必须知道的jQueryMobile代码片段(转)

    1.在列表项和按钮上禁用文本截断      如果你的列表项或者按钮上是一个很长的文本,它将会被jQuery Mobile自动截断,要禁用这个截断设置,需要在CSS选择器上添加属性"white ...

  2. Fusion Tables 图层用于呈现 Google Fusion Tables 中包含的数据

    Google Maps API 允许您使用 FusionTablesLayer 对象将 Google Fusion Tables 中包含的数据呈现为地图上的图层.Google Fusion Table ...

  3. 整理mysql的28个知识点(转)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/weixin_39220472/article/details/80247011整理mysql28个知 ...

  4. python学习笔记——多进程二 进程的退出

    1 进程的退出函数的基础语法 1.1 进程的退出函数 进程的退出含有有os._exit([status])和sys.exit([status])两种,从数据包来看,该退出模块仅在linux或者unix ...

  5. python练习笔记——完全数(1000以内的)

    完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数.它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身.如果一个数恰好等于它的因子之和,则称该数为“完 ...

  6. [Android开发那点破事]解决android.os.NetworkOnMainThreadException

    [Android开发那点破事]解决android.os.NetworkOnMainThreadException 昨天和女朋友换了手机,我的iPhone 4S 换了她得三星I9003.第一感觉就是好卡 ...

  7. 进程在Linux内核中的角色扮演

    在Linux内核中,内核将进程.线程和内核线程一视同仁,即内核使用唯一的数据结构task_struct来分别表示他们:内核使用相同的调度算法对这三者进行调度:并且内核也使用同一个函数do_fork() ...

  8. linux内存回收机制

    无论计算机上有多少内存都是不够的,因而linux kernel需要回收一些很少使用的内存页面来保证系统持续有内存使用.页面回收的方式有页回写.页交换和页丢弃三种方式:如果一个很少使用的页的后备存储器是 ...

  9. html中如何让table显示的更好

    在html文件编写中,经常使用到table来做一些表格.如何让它显示的更像一张表格?接下来为你讲解. 基本格式 <div> <th>我的一张表格</th> < ...

  10. 怎么使用 bat 使用日期时间重命名文件名

    d: rename A.txt "A%date:~0,4%-%date:~5,2%-%date:~8,2%_%time:~0,2%-%time:~3,2%-%time:~6,2%_backu ...