From dd09704876173d0feeb9f9bf2b52bfeeddb99dd1 Mon Sep 17 00:00:00 2001 From: xufulong Date: Fri, 25 Feb 2022 18:11:29 +0800 Subject: [PATCH] Add: FFmpegMediaRetriever usage --- .../cpp/metadata/ffmpeg_media_retriever.c | 20 ++-- .../com/frank/ffmpeg/activity/MainActivity.kt | 1 - .../ffmpeg/activity/ProbeFormatActivity.kt | 102 ++++++++++++++++-- app/src/main/res/layout/activity_probe.xml | 24 ++++- app/src/main/res/values-en/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- 6 files changed, 131 insertions(+), 22 deletions(-) diff --git a/app/src/main/cpp/metadata/ffmpeg_media_retriever.c b/app/src/main/cpp/metadata/ffmpeg_media_retriever.c index fb9e231..4dae289 100755 --- a/app/src/main/cpp/metadata/ffmpeg_media_retriever.c +++ b/app/src/main/cpp/metadata/ffmpeg_media_retriever.c @@ -49,8 +49,8 @@ int get_scaled_context(State *s, AVCodecContext *pCodecCtx, int width, int heigh s->scaled_codecCtx->pix_fmt = TARGET_IMAGE_FORMAT; s->scaled_codecCtx->codec_type = AVMEDIA_TYPE_VIDEO; s->scaled_codecCtx->bit_rate = codecP->bit_rate; - s->scaled_codecCtx->time_base.num = s->video_st->codec->time_base.num; - s->scaled_codecCtx->time_base.den = s->video_st->codec->time_base.den; + s->scaled_codecCtx->time_base.num = pCodecCtx->time_base.num; + s->scaled_codecCtx->time_base.den = pCodecCtx->time_base.den; if (!targetCodec || avcodec_open2(s->scaled_codecCtx, targetCodec, NULL) < 0) { LOGE("avcodec_open2() failed\n"); @@ -490,7 +490,6 @@ int get_embedded_picture(State **state_ptr, AVPacket *pkt) { int i = 0; int got_packet = 0; AVFrame *frame = NULL; - State *state = *state_ptr; if (!state || !state->pFormatCtx) { @@ -520,8 +519,9 @@ int get_embedded_picture(State **state_ptr, AVPacket *pkt) { break; } - avcodec_send_packet(state->video_st->codec, pkt); - int ret = avcodec_receive_frame(state->video_st->codec, frame); + AVCodecContext *pCodecContext = state->video_st->codec; + avcodec_send_packet(pCodecContext, pkt); + int ret = avcodec_receive_frame(pCodecContext, frame); if (ret == 0) { got_frame = 1; } else { @@ -534,7 +534,7 @@ int get_embedded_picture(State **state_ptr, AVPacket *pkt) { convertedPkt.size = 0; convertedPkt.data = NULL; - convert_image(state, state->video_st->codec, frame, &convertedPkt, &got_packet, -1, -1); + convert_image(state, pCodecContext, frame, &convertedPkt, &got_packet, -1, -1); av_packet_unref(pkt); av_init_packet(pkt); @@ -556,7 +556,6 @@ int get_embedded_picture(State **state_ptr, AVPacket *pkt) { } av_frame_free(&frame); - if (got_packet) { return SUCCESS; } else { @@ -579,8 +578,9 @@ void decode_frame(State *state, AVPacket *pkt, int *got_frame, int64_t desired_f if (!is_supported_format(codec_id, pix_fmt)) { *got_frame = 0; - avcodec_send_packet(state->video_st->codec, pkt); - int ret = avcodec_receive_frame(state->video_st->codec, frame); + AVCodecContext *pCodecContext = state->video_st->codec; + avcodec_send_packet(pCodecContext, pkt); + int ret = avcodec_receive_frame(pCodecContext, frame); if (ret == 0) { *got_frame = 1; } else { @@ -593,7 +593,7 @@ void decode_frame(State *state, AVPacket *pkt, int *got_frame, int64_t desired_f av_packet_unref(pkt); } av_init_packet(pkt); - convert_image(state, state->video_st->codec, frame, pkt, got_frame, width, height); + convert_image(state, pCodecContext, frame, pkt, got_frame, width, height); break; } } else { diff --git a/app/src/main/java/com/frank/ffmpeg/activity/MainActivity.kt b/app/src/main/java/com/frank/ffmpeg/activity/MainActivity.kt index 7de4bc3..b1555e9 100644 --- a/app/src/main/java/com/frank/ffmpeg/activity/MainActivity.kt +++ b/app/src/main/java/com/frank/ffmpeg/activity/MainActivity.kt @@ -71,7 +71,6 @@ class MainActivity : BaseActivity() { -> intent.setClass(this@MainActivity, ProbeFormatActivity::class.java) 8 //audio effect -> intent.setClass(this@MainActivity, AudioEffectActivity::class.java) -// -> intent.setClass(this@MainActivity, MediaPlayerActivity::class.java) else -> { } } diff --git a/app/src/main/java/com/frank/ffmpeg/activity/ProbeFormatActivity.kt b/app/src/main/java/com/frank/ffmpeg/activity/ProbeFormatActivity.kt index 7db6d39..5f65de0 100644 --- a/app/src/main/java/com/frank/ffmpeg/activity/ProbeFormatActivity.kt +++ b/app/src/main/java/com/frank/ffmpeg/activity/ProbeFormatActivity.kt @@ -1,25 +1,29 @@ package com.frank.ffmpeg.activity import android.annotation.SuppressLint +import android.graphics.Bitmap import android.os.Bundle import android.os.Handler import android.os.Message -import android.text.TextUtils +import android.util.Log import android.view.View +import android.widget.ImageView import android.widget.ProgressBar import android.widget.RelativeLayout import android.widget.TextView import com.frank.ffmpeg.R import com.frank.ffmpeg.handler.FFmpegHandler +import com.frank.ffmpeg.handler.FFmpegHandler.* import com.frank.ffmpeg.model.MediaBean import com.frank.ffmpeg.tool.JsonParseTool import com.frank.ffmpeg.util.FFmpegUtil + import com.frank.ffmpeg.util.FileUtil -import com.frank.ffmpeg.handler.FFmpegHandler.MSG_BEGIN -import com.frank.ffmpeg.handler.FFmpegHandler.MSG_FINISH +import com.frank.ffmpeg.metadata.FFmpegMediaRetriever +import java.lang.StringBuilder /** * Using ffprobe to parse media format data @@ -33,6 +37,10 @@ class ProbeFormatActivity : BaseActivity() { private var layoutProbe: RelativeLayout? = null private var ffmpegHandler: FFmpegHandler? = null + private var view: View? = null + + private val MSG_FRAME = 9099 + @SuppressLint("HandlerLeak") private val mHandler = object : Handler() { override fun handleMessage(msg: Message) { @@ -47,12 +55,22 @@ class ProbeFormatActivity : BaseActivity() { layoutProbe!!.visibility = View.VISIBLE val result = msg.obj?: msg.obj if (result != null) { - val resultFormat = JsonParseTool.stringFormat(result as MediaBean) - if (!TextUtils.isEmpty(resultFormat) && txtProbeFormat != null) { - txtProbeFormat!!.text = resultFormat + val mediaInfo = JsonParseTool.stringFormat(result as MediaBean) + if (!mediaInfo.isNullOrEmpty() && txtProbeFormat != null) { + txtProbeFormat!!.text = mediaInfo } } } + MSG_INFO -> { + val mediaInfo = msg.obj as String + if (!mediaInfo.isNullOrEmpty()) { + txtProbeFormat!!.text = mediaInfo + } + } + MSG_FRAME -> { + val bitmap = msg.obj as Bitmap + findViewById(R.id.img_frame).setImageBitmap(bitmap) + } else -> { } } @@ -73,15 +91,27 @@ class ProbeFormatActivity : BaseActivity() { progressProbe = getView(R.id.progress_probe) layoutProbe = getView(R.id.layout_probe) initViewsWithClick(R.id.btn_probe_format) + initViewsWithClick(R.id.btn_retrieve_format) txtProbeFormat = getView(R.id.txt_probe_format) } override fun onViewClick(view: View) { + this.view = view selectFile() } override fun onSelectedFile(filePath: String) { - doHandleProbe(filePath) + + when (view?.id) { + R.id.btn_probe_format -> { + doHandleProbe(filePath) + } + R.id.btn_retrieve_format -> { + Thread {retrieveMediaMetadata(filePath)}.start() + } + else -> { + } + } } /** @@ -104,4 +134,62 @@ class ProbeFormatActivity : BaseActivity() { mHandler.removeCallbacksAndMessages(null) } + private fun retrieveMediaMetadata(path :String) { + if (path.isEmpty()) { + return + } + + try { + val resultBuilder = StringBuilder() + val retriever = FFmpegMediaRetriever() + retriever.setDataSource(path) + + val duration = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_DURATION) + if (duration != null) + resultBuilder.append("duration:$duration\n") + + val audioCodec = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_AUDIO_CODEC) + if (audioCodec != null) + resultBuilder.append("audioCodec:$audioCodec\n") + + val videoCodec = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_VIDEO_CODEC) + if (videoCodec != null) + resultBuilder.append("videoCodec:$videoCodec\n") + + val width = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_VIDEO_WIDTH) + val height = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_VIDEO_HEIGHT) + if (width != null && height != null) + resultBuilder.append("resolution:$width x $height\n") + + val frameRate = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_FRAME_RATE) + if (frameRate != null) + resultBuilder.append("frameRate:$frameRate\n") + + val sampleRate = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_SAMPLE_RATE) + if (sampleRate != null) + resultBuilder.append("sampleRate:$sampleRate\n") + + val channelCount = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_CHANNEL_COUNT) + if (channelCount != null) + resultBuilder.append("channelCount:$channelCount\n") + + val channelLayout = retriever.extractMetadata(FFmpegMediaRetriever.METADATA_KEY_CHANNEL_LAYOUT) + if (channelLayout != null) + resultBuilder.append("channelLayout:$channelLayout\n") + + mHandler.obtainMessage(MSG_INFO, resultBuilder.toString()).sendToTarget() + + // Retrieve frame with timeUs + val bitmap = retriever.getFrameAtTime(5 * 1000000) + if (bitmap != null) { + Log.e("FFmpegRetriever", "bitmap width=${bitmap.width}--height=${bitmap.height}") + mHandler.obtainMessage(MSG_FRAME, bitmap).sendToTarget() + } + + retriever.release() + } catch (e : Exception) { + Log.e("FFmpegRetriever", "ffmpeg error=$e") + } + } + } diff --git a/app/src/main/res/layout/activity_probe.xml b/app/src/main/res/layout/activity_probe.xml index d90fc38..fd292e4 100644 --- a/app/src/main/res/layout/activity_probe.xml +++ b/app/src/main/res/layout/activity_probe.xml @@ -11,10 +11,21 @@ android:id="@+id/btn_probe_format" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="60dp" + android:layout_marginTop="50dp" android:text="@string/media_probe" android:textColor="@color/colorPrimary" - android:layout_centerHorizontal="true"/> + android:layout_alignParentStart="true" + android:layout_marginStart="20dp"/> + +