需要Android系统8及以上(Api 26、26+)
private void getCreationTimeFromImage() {
String path = Environment.getExternalStorageDirectory().getPath() + "/tieba/熊猫头.jpg";
BasicFileAttributes attr = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
attr = Files.readAttributes(new File(path).toPath(),
BasicFileAttributes.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.d(TAG, "BasicFileAttributes ->creationTime: " + attr.creationTime());
Log.d(TAG, "BasicFileAttributes ->lastModifiedTime: " + attr.lastModifiedTime());
} catch (IOException e) {
Log.e(TAG, "BasicFileAttributes - failed:" + e.toString());
以上是本人的一些拙见,如大兄弟有更好的方法,希望不吝赐教,谢谢
一、Android读取视频metadata的拍摄时间方式一(不符合要求)使用MediaMetadataRetriever获取METADATA_KEY_DATE值;result: 在Android华为9.0手机获取的到值,不过对应不上视频的拍摄时间(1904 UTC),在vivo6.0手机上没获取到;查看了media_jni的一些cpp文件的代码,依然没法解决问题方式二(不符合要求)使...
内建TypeScript定义
在使用此库之前
MusicBrainz要求通过填写。通过传递appName , appVersion , appMail musicbrainz-api可以解决此问题。
提交元数据
如果您打算使用此模块提交元数据,请确保您遵守。
导入模块JavaScript示例,如何导入'musicbrainz-api:
const MusicBrainzApi = require ( 'musicbrainz-api' ) . MusicBrainzApi ;
const mbApi = new MusicBrainzApi ( {
appName
$ git clone https://github.com/autumnchris/file-metadata-api.git
$ cd file-metadata-api
$ npm install
$ npm start
转到http://localhost:3000 。
Clouseau: Blockchain-based Data Integrity for HDFS Clusters
resource:2021 IEEE 37th International Conference on Data Engineering (ICDE)
一些关键概念:
1.HDFS
2.authentication与authorization的区别,前者是身份认证,后者是授权
3.matadata,元数据,简单来说就是描述数据的数据,比如数据的属性、存储位置、摘要等等
研究背景:
获取文件
File media = new File(ConfigSingleton.getInstance().getExternalMartianVideoDir());
if (media.isDirectory()){
File[] files=media.listFiles();
for (int i=0;i<file...
//选择图片、
视频、音频第三方图片选择器,glide 4.5.0将代替glide:3.7.0,需修改写法
implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'
相关代码:
pr...
在Android里获取视频的信息主要依靠MediaMetadataRetriever实现
获取最佳视频预览图
所谓的最佳就是MediaMetadataRetriever自己计算的
修改文件相关日期
下载exiftool.exe 软件,并在相应文件下编辑*.bat文件内容如下:
exiftool.exe “-FileCreateDate=2022:02:10 10:55:45” “C:\Users\Administrator\Desktop\ExifTool\VID_20220210_120445.mp4”
exiftool.exe “-CreateDate=2022:02:10 10:55:45” “C:\Users\Administrator\Desktop\Exi
我们讲多媒体,涉及到的最多的就是MP4文件和MP3文件了,但是我们对这两个文件的格式了解多少呢,它的由有哪些部分部分组成呢?它的核心部件是哪些?它哪些部分是供解码器去解析的呢?带着这些疑问,我们首先来探索下MP4文件。
我们首先用MP4Info这个工具来看下MP4的大貌:
从上图我们可以看到MP4文件中的所有数据都装在box中,也就是说MP4文件由若干个box组成,每个box有类型和
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaExtractor;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Runnable {
private static final String TAG = "MainActivity";
private static final String VIDEO_PATH = "rtsp://192.168.1.1:554/stream";
private static final int MSG_START_PLAY = 0;
private static final int MSG_STOP_PLAY = 1;
private static final int MSG_PLAY_FRAME = 2;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private MediaPlayer mMediaPlayer;
private MediaCodec mMediaCodec;
private MediaExtractor mMediaExtractor;
private ByteBuffer[] mInputBuffers;
private ByteBuffer[] mOutputBuffers;
private MediaFormat mMediaFormat;
private int mVideoTrackIndex;
private boolean mIsPlaying = false;
private boolean mIsCodecConfigured = false;
private long mStartTime;
private long mDuration;
private int mFrameIndex = 0;
private Handler mPlayHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START_PLAY:
startPlay();
break;
case MSG_STOP_PLAY:
stopPlay();
break;
case MSG_PLAY_FRAME:
playFrame();
break;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSurfaceView = findViewById(R.id.surface_view);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mPlayHandler.sendEmptyMessage(MSG_START_PLAY);
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mPlayHandler.sendEmptyMessage(MSG_STOP_PLAY);
@Override
public void run() {
while (mIsPlaying) {
playFrame();
private void startPlay() {
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(getApplicationContext(), Uri.parse(VIDEO_PATH));
mMediaPlayer.setSurface(mSurfaceHolder.getSurface());
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mIsPlaying = true;
mStartTime = System.currentTimeMillis();
mDuration = mediaPlayer.getDuration();
mediaPlayer.start();
new Thread(MainActivity.this).start();
} catch (IOException e) {
e.printStackTrace();
private void stopPlay() {
mIsPlaying = false;
mMediaPlayer.release();
mMediaCodec.stop();
mMediaCodec.release();
private void playFrame() {
if (mMediaExtractor == null) {
mMediaExtractor = new MediaExtractor();
try {
mMediaExtractor.setDataSource(VIDEO_PATH);
} catch (IOException e) {
e.printStackTrace();
mVideoTrackIndex = -1;
for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
MediaFormat format = mMediaExtractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
mVideoTrackIndex = i;
mMediaFormat = format;
break;
mMediaExtractor.selectTrack(mVideoTrackIndex);
try {
mMediaCodec = MediaCodec.createDecoderByType(mMediaFormat.getString(MediaFormat.KEY_MIME));
mMediaCodec.configure(mMediaFormat, mSurfaceHolder.getSurface(), null, 0);
mMediaCodec.start();
mInputBuffers = mMediaCodec.getInputBuffers();
mOutputBuffers = mMediaCodec.getOutputBuffers();
} catch (IOException e) {
e.printStackTrace();
int inputBufferIndex = mMediaCodec.dequeueInputBuffer(10000);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = mInputBuffers[inputBufferIndex];
int sampleSize = mMediaExtractor.readSampleData(inputBuffer, 0);
if (sampleSize < 0) {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, mMediaExtractor.getSampleTime(), 0);
mMediaExtractor.advance();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
if (outputBufferIndex >= 0) {
if (!mIsCodecConfigured) {
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
mIsCodecConfigured = true;
} else {
while (bufferInfo.presentationTimeUs / 1000 > System.currentTimeMillis() - mStartTime) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
mMediaCodec.releaseOutputBuffer(outputBufferIndex, true);
if (mFrameIndex == 0) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(VIDEO_PATH);
String rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
int rotation = Integer.parseInt(rotationString);
if (rotation == 90 || rotation == 270) {
mMediaFormat.setInteger(MediaFormat.KEY_WIDTH, mMediaFormat.getInteger(MediaFormat.KEY_HEIGHT));
mMediaFormat.setInteger(MediaFormat.KEY_HEIGHT, mMediaFormat.getInteger(MediaFormat.KEY_WIDTH));
mFrameIndex++;
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
if (!mIsCodecConfigured) {
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
mIsCodecConfigured = true;
} else {
MediaFormat format = mMediaCodec.getOutputFormat();
if (mFrameIndex == 0) {
String rotationString = format.getString(MediaFormat.KEY_ROTATION);
int rotation = Integer.parseInt(rotationString);
if (rotation == 90 || rotation == 270) {
format.setInteger(MediaFormat.KEY_WIDTH, format.getInteger(MediaFormat.KEY_HEIGHT));
format.setInteger(MediaFormat.KEY_HEIGHT, format.getInteger(MediaFormat.KEY_WIDTH));
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
这个代码会通过RTSP流读取视频流并且可以提前每一帧画面。如果你要使用这个代码,需要将VIDEO_PATH替换为你的RTSP流地址。