info: added stream info (video, multimedia heic)

This commit is contained in:
Thibault Deckers 2021-04-10 12:53:02 +09:00
parent 52e4f96f7b
commit 4526df5a77
6 changed files with 223 additions and 4 deletions

View file

@ -210,6 +210,9 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
if (mediaDir.isNotEmpty()) { if (mediaDir.isNotEmpty()) {
metadataMap[Metadata.DIR_MEDIA] = mediaDir metadataMap[Metadata.DIR_MEDIA] = mediaDir
} }
// Android's `MediaExtractor` and `MediaPlayer` cannot be used for details
// about embedded images as they do not list them as separate tracks
// and only identify at most one
} }
if (metadataMap.isNotEmpty()) { if (metadataMap.isNotEmpty()) {

View file

@ -0,0 +1,94 @@
// channel layout constants from FFmpeg libavutil/channel_layout.h
class ChannelLayouts {
// ignore_for_file: constant_identifier_names
static const FRONT_LEFT = 0x00000001;
static const FRONT_RIGHT = 0x00000002;
static const FRONT_CENTER = 0x00000004;
static const LOW_FREQUENCY = 0x00000008;
static const BACK_LEFT = 0x00000010;
static const BACK_RIGHT = 0x00000020;
static const FRONT_LEFT_OF_CENTER = 0x00000040;
static const FRONT_RIGHT_OF_CENTER = 0x00000080;
static const BACK_CENTER = 0x00000100;
static const SIDE_LEFT = 0x00000200;
static const SIDE_RIGHT = 0x00000400;
static const TOP_CENTER = 0x00000800;
static const TOP_FRONT_LEFT = 0x00001000;
static const TOP_FRONT_CENTER = 0x00002000;
static const TOP_FRONT_RIGHT = 0x00004000;
static const TOP_BACK_LEFT = 0x00008000;
static const TOP_BACK_CENTER = 0x00010000;
static const TOP_BACK_RIGHT = 0x00020000;
static const STEREO_LEFT = 0x20000000;
static const STEREO_RIGHT = 0x40000000;
static const WIDE_LEFT = 0x0000000080000000;
static const WIDE_RIGHT = 0x0000000100000000;
static const SURROUND_DIRECT_LEFT = 0x0000000200000000;
static const SURROUND_DIRECT_RIGHT = 0x0000000400000000;
static const LOW_FREQUENCY_2 = 0x0000000800000000;
static const LAYOUT_NATIVE = 0x8000000000000000;
static const LAYOUT_MONO = (FRONT_CENTER);
static const LAYOUT_STEREO = (FRONT_LEFT | FRONT_RIGHT);
static const LAYOUT_2POINT1 = (LAYOUT_STEREO | LOW_FREQUENCY);
static const LAYOUT_2_1 = (LAYOUT_STEREO | BACK_CENTER);
static const LAYOUT_SURROUND = (LAYOUT_STEREO | FRONT_CENTER);
static const LAYOUT_3POINT1 = (LAYOUT_SURROUND | LOW_FREQUENCY);
static const LAYOUT_4POINT0 = (LAYOUT_SURROUND | BACK_CENTER);
static const LAYOUT_4POINT1 = (LAYOUT_4POINT0 | LOW_FREQUENCY);
static const LAYOUT_2_2 = (LAYOUT_STEREO | SIDE_LEFT | SIDE_RIGHT);
static const LAYOUT_QUAD = (LAYOUT_STEREO | BACK_LEFT | BACK_RIGHT);
static const LAYOUT_5POINT0 = (LAYOUT_SURROUND | SIDE_LEFT | SIDE_RIGHT);
static const LAYOUT_5POINT1 = (LAYOUT_5POINT0 | LOW_FREQUENCY);
static const LAYOUT_5POINT0_BACK = (LAYOUT_SURROUND | BACK_LEFT | BACK_RIGHT);
static const LAYOUT_5POINT1_BACK = (LAYOUT_5POINT0_BACK | LOW_FREQUENCY);
static const LAYOUT_6POINT0 = (LAYOUT_5POINT0 | BACK_CENTER);
static const LAYOUT_6POINT0_FRONT = (LAYOUT_2_2 | FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER);
static const LAYOUT_HEXAGONAL = (LAYOUT_5POINT0_BACK | BACK_CENTER);
static const LAYOUT_6POINT1 = (LAYOUT_5POINT1 | BACK_CENTER);
static const LAYOUT_6POINT1_BACK = (LAYOUT_5POINT1_BACK | BACK_CENTER);
static const LAYOUT_6POINT1_FRONT = (LAYOUT_6POINT0_FRONT | LOW_FREQUENCY);
static const LAYOUT_7POINT0 = (LAYOUT_5POINT0 | BACK_LEFT | BACK_RIGHT);
static const LAYOUT_7POINT0_FRONT = (LAYOUT_5POINT0 | FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER);
static const LAYOUT_7POINT1 = (LAYOUT_5POINT1 | BACK_LEFT | BACK_RIGHT);
static const LAYOUT_7POINT1_WIDE = (LAYOUT_5POINT1 | FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER);
static const LAYOUT_7POINT1_WIDE_BACK = (LAYOUT_5POINT1_BACK | FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER);
static const LAYOUT_OCTAGONAL = (LAYOUT_5POINT0 | BACK_LEFT | BACK_CENTER | BACK_RIGHT);
static const LAYOUT_HEXADECAGONAL = (LAYOUT_OCTAGONAL | WIDE_LEFT | WIDE_RIGHT | TOP_BACK_LEFT | TOP_BACK_RIGHT | TOP_BACK_CENTER | TOP_FRONT_CENTER | TOP_FRONT_LEFT | TOP_FRONT_RIGHT);
static const LAYOUT_STEREO_DOWNMIX = (STEREO_LEFT | STEREO_RIGHT);
static const names = {
LAYOUT_NATIVE: 'native',
LAYOUT_MONO: 'mono',
LAYOUT_STEREO: 'stereo',
LAYOUT_2POINT1: '2.1',
LAYOUT_2_1: '2_1',
LAYOUT_SURROUND: 'surround',
LAYOUT_3POINT1: '3.1',
LAYOUT_4POINT0: '4.0',
LAYOUT_4POINT1: '4.1',
LAYOUT_2_2: '2_2',
LAYOUT_QUAD: 'quad',
LAYOUT_5POINT0: '5.0',
LAYOUT_5POINT1: '5.1',
LAYOUT_5POINT0_BACK: '5.0 back',
LAYOUT_5POINT1_BACK: '5.1 back',
LAYOUT_6POINT0: '6.0',
LAYOUT_6POINT0_FRONT: '6.0 front',
LAYOUT_HEXAGONAL: 'hexagonal',
LAYOUT_6POINT1: '6.1',
LAYOUT_6POINT1_BACK: '6.1 back',
LAYOUT_6POINT1_FRONT: '6.1 front',
LAYOUT_7POINT0: '7.0',
LAYOUT_7POINT0_FRONT: '7.0 front',
LAYOUT_7POINT1: '7.1',
LAYOUT_7POINT1_WIDE: '7.1 wide',
LAYOUT_7POINT1_WIDE_BACK: '7.1 wide back',
LAYOUT_OCTAGONAL: 'octagonal',
LAYOUT_HEXADECAGONAL: 'hexadecagonal',
LAYOUT_STEREO_DOWNMIX: 'stereo downmix',
};
}

View file

@ -0,0 +1,110 @@
import 'dart:async';
import 'package:aves/model/entry.dart';
import 'package:aves/model/video/channel_layouts.dart';
import 'package:aves/utils/math_utils.dart';
import 'package:fijkplayer/fijkplayer.dart';
class StreamInfo {
static Future<Map> getVideoInfo(AvesEntry entry) async {
final player = FijkPlayer();
await player.setDataSource(entry.uri, autoPlay: false);
final completer = Completer();
void onChange() {
if ([FijkState.prepared, FijkState.error].contains(player.state)) {
completer.complete();
}
}
player.addListener(onChange);
await player.prepareAsync();
await completer.future;
player.removeListener(onChange);
final info = await player.getInfo();
await player.release();
return info;
}
static String formatBitrate(int size, {int round = 2}) {
const divider = 1000;
const symbol = 'bit/s';
if (size < divider) return '$size $symbol';
if (size < divider * divider && size % divider == 0) {
return '${(size / divider).toStringAsFixed(0)} K$symbol';
}
if (size < divider * divider) {
return '${(size / divider).toStringAsFixed(round)} K$symbol';
}
if (size < divider * divider * divider && size % divider == 0) {
return '${(size / (divider * divider)).toStringAsFixed(0)} M$symbol';
}
return '${(size / divider / divider).toStringAsFixed(round)} M$symbol';
}
static Map<String, String> formatStreamInfo(Map stream) {
final dir = <String, String>{};
for (final kv in stream.entries) {
final value = kv.value;
if (value != null) {
final key = kv.key;
switch (key) {
case 'index':
case 'fps_num':
case 'sar_num':
case 'tbr_num':
case 'tbr_den':
break;
case 'bitrate':
dir['Bitrate'] = formatBitrate(value, round: 1);
break;
case 'channel_layout':
dir['Channel Layout'] = ChannelLayouts.names[value] ?? value.toString();
break;
case 'codec_name':
dir['Codec'] = value.toString().toUpperCase().replaceAll('_', ' ');
break;
case 'fps_den':
dir['Frame Rate'] = roundToPrecision(stream['fps_num'] / stream['fps_den'], decimals: 3).toString();
break;
case 'height':
dir['Height'] = '$value pixels';
break;
case 'language':
dir['Language'] = value;
break;
case 'sample_rate':
dir['Sample Rate'] = '$value Hz';
break;
case 'sar_den':
dir['SAR'] = '${stream['sar_num']}:${stream['sar_den']}';
break;
case 'type':
switch (value) {
case 'timedtext':
dir['Type'] = 'timed text';
break;
case 'audio':
case 'video':
case 'metadata':
case 'subtitle':
case 'unknown':
default:
dir['Type'] = value;
}
break;
case 'width':
dir['Width'] = '$value pixels';
break;
default:
dir[key] = value.toString();
}
}
}
return dir;
}
}

View file

@ -1,6 +1,9 @@
import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/video/streams.dart';
import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:aves/services/svg_metadata_service.dart'; import 'package:aves/services/svg_metadata_service.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
@ -139,6 +142,18 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
if (_loadedMetadataUri.value == entry.uri) return; if (_loadedMetadataUri.value == entry.uri) return;
if (isVisible) { if (isVisible) {
final rawMetadata = await (entry.isSvg ? SvgMetadataService.getAllMetadata(entry) : metadataService.getAllMetadata(entry)) ?? {}; final rawMetadata = await (entry.isSvg ? SvgMetadataService.getAllMetadata(entry) : metadataService.getAllMetadata(entry)) ?? {};
if (entry.isVideo || (entry.mimeType == MimeTypes.heif && entry.isMultipage)) {
final info = await StreamInfo.getVideoInfo(entry);
if (info.containsKey('streams')) {
final streams = (info['streams'] as List).cast<Map>();
for (final stream in streams) {
if (stream.containsKey('index')) {
final index = stream['index'] + 1;
rawMetadata['Stream $index'] = StreamInfo.formatStreamInfo(stream);
}
}
}
}
final directories = rawMetadata.entries.map((dirKV) { final directories = rawMetadata.entries.map((dirKV) {
var directoryName = dirKV.key as String ?? ''; var directoryName = dirKV.key as String ?? '';

View file

@ -211,7 +211,7 @@ packages:
description: description:
path: "." path: "."
ref: aves ref: aves
resolved-ref: a4640923c7c6141ff543f676a8cb7d2fe8b0ffba resolved-ref: "118fa08b30a7948df04d2692649b124a09ccb480"
url: "git://github.com/deckerst/fijkplayer.git" url: "git://github.com/deckerst/fijkplayer.git"
source: git source: git
version: "0.8.7" version: "0.8.7"

View file

@ -42,9 +42,6 @@ dependencies:
firebase_analytics: firebase_analytics:
firebase_crashlytics: firebase_crashlytics:
flutter_highlight: flutter_highlight:
# flutter_ijkplayer:
# git:
# url: git://github.com/deckerst/flutter_ijkplayer.git
flutter_localized_locales: flutter_localized_locales:
flutter_map: flutter_map:
flutter_markdown: flutter_markdown: