1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Java】ffprobe -show_streamsの結果をJSONで出力し、Jacksonでオブジェクトにマップする

Posted at

やることはタイトル通りですが、内容は主にJacksonの使い方の話です。

ffprobeのオプション指定

ffprobeは以下のように動かします。

cat ${ファイル名} | ffprobe -hide_banner -v error -print_format json -show_streams  -i pipe:0

諸々指定していますが、重要なのは以下の2点です。

  • -print_format json -> 処理結果をJSON形式で出力
  • -show_streams -> 音、ビデオ、字幕等の各ストリーム情報を出力

Javaからの呼び出し方は以下の記事を参照してください。

呼び出し結果

呼び出し結果は以下のとおりです(長いので折りたたみます)。

呼び出し結果
{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "752/45075",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1920,
            "height": 1080,
            "coded_width": 1920,
            "coded_height": 1088,
            "has_b_frames": 1,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "16:9",
            "pix_fmt": "yuv420p",
            "level": 41,
            "color_range": "tv",
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "30000/1001",
            "avg_frame_rate": "45075/1504",
            "time_base": "1/30000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 601600,
            "duration": "20.053333",
            "bit_rate": "6653705",
            "bits_per_raw_sample": "8",
            "nb_frames": "601",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "creation_time": "2014-03-30T07:09:03.000000Z",
                "language": "eng",
                "handler_name": "\u001fMainconcept Video Media Handler",
                "encoder": "AVC Coding"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/48000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 962560,
            "duration": "20.053333",
            "bit_rate": "317375",
            "max_bit_rate": "317625",
            "nb_frames": "942",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "creation_time": "2014-03-30T07:09:03.000000Z",
                "language": "eng",
                "handler_name": "#Mainconcept MP4 Sound Media Handler"
            }
        }
    ]
}

Jacksonによるマップ

先程出したprobe結果をオブジェクトにマップしていきます。
出力はビデオ内のstreamの配列とします。

マップ先オブジェクト

今回は以下のオブジェクトにマップしていきます(@Getter@Setterlombokを想定して書いています)。
結果の基底はstreamとした上で、videoaudioか(あるいはそれ以外か)で型を分けて表現しています。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "codec_type", defaultImpl = NoClass.class)
@JsonSubTypes({
        @JsonSubTypes.Type(value = FfprobeStream.VideoStream.class, name = "video"),
        @JsonSubTypes.Type(value = FfprobeStream.AudioStream.class, name = "audio")
})
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter
abstract class FfprobeStream {
    private Double duration;
    private String codecName;

    @Getter @Setter
    static class VideoStream extends FfprobeStream {
        private Integer width;
        private Integer height;
    }

    @Getter @Setter
    static class AudioStream extends FfprobeStream {
        private String channelLayout;
    }
}

解説

以下のアノテーションで、codec_typeの値によってマップ先の型を制御しています。
神オブジェクトを作ってそこにマップする手も無くはないですが、後が大変になりやすいので、マップ先の型の制御はやっておいた方がベターです。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "codec_type", defaultImpl = NoClass.class)
@JsonSubTypes({
        @JsonSubTypes.Type(value = FfprobeStream.VideoStream.class, name = "video"),
        @JsonSubTypes.Type(value = FfprobeStream.AudioStream.class, name = "audio")
})

以下のアノテーションで、マップできないフィールドは無視しています。
今回は結果の内一部フィールドしか用意していないため、これを指定しなければマップできなかった場合にエラーになります。

@JsonIgnoreProperties(ignoreUnknown = true)

probe結果文字列からstreamの配列にマップする

以下のコードで、probe結果をビデオ内のstreamの配列にマップできます。
詳細はコメントの通りです。

static List<FfprobeStream> parseFfprobeResult(String ffprobeResult) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // ffprobeが出力するのはPropertyNamingStrategy.SNAKE_CASEのJSONであるため、そのように指定する
    mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

    // JSON -> Javaオブジェクトの変換を実装
    Map<String, List<FfprobeStream>> map
            = mapper.readValue(ffprobeResult, new TypeReference<Map<String, List<FfprobeStream>>>() {});

    // パース結果はMapで出てくるため、パラメータ指定でGetして返す
    return map.getOrDefault("streams", Collections.emptyList());
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?