diff --git a/doc/formats.md b/doc/formats.md index ed70d1e9d..6a4a6c0b1 100644 --- a/doc/formats.md +++ b/doc/formats.md @@ -267,20 +267,21 @@ Decode value as avc_au ### Options -|Name |Default|Description| -|- |- |-| -|`decode_samples`|true |Decode samples| +|Name |Default|Description| +|- |- |-| +|`decode_extended_chunks`|true |Decode extended chunks| +|`decode_samples` |true |Decode samples| ### Examples Decode file using avi options ``` -$ fq -d avi -o decode_samples=true . file +$ fq -d avi -o decode_extended_chunks=true -o decode_samples=true . file ``` Decode value as avi ``` -... | avi({decode_samples:true}) +... | avi({decode_extended_chunks:true,decode_samples:true}) ``` ### Samples @@ -298,6 +299,13 @@ $ fq '.streams[1].samples[] | tobytes' file.avi > stream01.mp3 $ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi ``` +### Speed up decoding by disabling sample and extended chunks decoding + +If your not interested in sample details or extended chunks you can speed up decoding by using: +```sh +$ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi +``` + ### References - [AVI RIFF File Reference](https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference) diff --git a/doc/formats.svg b/doc/formats.svg index 84d957c27..6ea241a61 100644 --- a/doc/formats.svg +++ b/doc/formats.svg @@ -4,2180 +4,2180 @@ - - + + formats - + adts - -adts - -adts_frame + +adts + +adts_frame adts_frame - -adts_frame - -aac_frame + +adts_frame + +aac_frame adts:e->adts_frame:n - - + + aac_frame - -aac_frame + +aac_frame adts_frame:e->aac_frame:n - - + + apev2 - -apev2 - -image + +apev2 + +image image - -image + +image apev2:e->image:n - - + + jpeg - -jpeg - -exif - -icc_profile + +jpeg + +exif + +icc_profile image->jpeg:n - - + + mp4 - -mp4 - -aac_frame - -av1_ccr - -av1_frame - -avc_au - -avc_dcr - -flac_frame - -flac_metadatablocks - -hevc_au - -hevc_dcr - -icc_profile - -id3v2 - -image - -jpeg - -mp3_frame - -mpeg_es - -mpeg_pes_packet - -opus_packet - -png - -prores_frame - -protobuf_widevine - -pssh_playready - -vorbis_packet - -vp9_frame - -vpx_ccr + +mp4 + +aac_frame + +av1_ccr + +av1_frame + +avc_au + +avc_dcr + +flac_frame + +flac_metadatablocks + +hevc_au + +hevc_dcr + +icc_profile + +id3v2 + +image + +jpeg + +mp3_frame + +mpeg_es + +mpeg_pes_packet + +opus_packet + +png + +prores_frame + +protobuf_widevine + +pssh_playready + +vorbis_packet + +vp9_frame + +vpx_ccr image->mp4:n - - + + png - -png - -icc_profile - -exif + +png + +icc_profile + +exif image->png:n - - + + tiff - -tiff - -icc_profile + +tiff + +icc_profile image->tiff:n - - + + webp - -webp - -vp8_frame + +webp + +vp8_frame image->webp:n - - + + gif - -gif + +gif image->gif:n - - + + ar - -ar - -probe + +ar + +probe probe - -probe + +probe ar:e->probe:n - - + + probe->adts:n - - + + probe->ar:n - - + + avi - -avi - -avc_au - -hevc_au - -mp3_frame - -flac_frame + +avi + +avc_au + +hevc_au + +mp3_frame + +flac_frame probe->avi:n - - + + bitcoin_blkdat - -bitcoin_blkdat - -bitcoin_block + +bitcoin_blkdat + +bitcoin_block probe->bitcoin_blkdat:n - - + + bzip2 - -bzip2 - -probe + +bzip2 + +probe probe->bzip2:n - - + + caff - -caff - -probe + +caff + +probe probe->caff:n - - + + flac - -flac - -flac_metadatablocks - -flac_frame + +flac + +flac_metadatablocks + +flac_frame probe->flac:n - - + + gzip - -gzip - -probe + +gzip + +probe probe->gzip:n - - + + probe->jpeg:n - - + + macho_fat - -macho_fat - -macho + +macho_fat + +macho probe->macho_fat:n - - + + macho - -macho + +macho probe->macho:n - - + + matroska - -matroska - -aac_frame - -av1_ccr - -av1_frame - -avc_au - -avc_dcr - -flac_frame - -flac_metadatablocks - -hevc_au - -hevc_dcr - -image - -mp3_frame - -mpeg_asc - -mpeg_pes_packet - -mpeg_spu - -opus_packet - -vorbis_packet - -vp8_frame - -vp9_cfm - -vp9_frame + +matroska + +aac_frame + +av1_ccr + +av1_frame + +avc_au + +avc_dcr + +flac_frame + +flac_metadatablocks + +hevc_au + +hevc_dcr + +image + +mp3_frame + +mpeg_asc + +mpeg_pes_packet + +mpeg_spu + +opus_packet + +vorbis_packet + +vp8_frame + +vp9_cfm + +vp9_frame probe->matroska:n - - + + mp3 - -mp3 - -id3v2 - -id3v1 - -id3v11 - -apev2 - -mp3_frame + +mp3 + +id3v2 + +id3v1 + +id3v11 + +apev2 + +mp3_frame probe->mp3:n - - + + probe->mp4:n - - + + probe->png:n - - + + ogg - -ogg - -ogg_page - -vorbis_packet - -opus_packet - -flac_metadatablock - -flac_frame + +ogg + +ogg_page + +vorbis_packet + +opus_packet + +flac_metadatablock + +flac_frame probe->ogg:n - - + + pcap - -pcap - -link_frame - -tcp_stream - -ipv4_packet + +pcap + +link_frame + +tcp_stream + +ipv4_packet probe->pcap:n - - + + pcapng - -pcapng - -link_frame - -tcp_stream - -ipv4_packet + +pcapng + +link_frame + +tcp_stream + +ipv4_packet probe->pcapng:n - - + + tar - -tar - -probe + +tar + +probe probe->tar:n - - + + probe->tiff:n - - + + wav - -wav - -id3v2 - -id3v1 - -id3v11 + +wav + +id3v2 + +id3v1 + +id3v11 probe->wav:n - - + + probe->webp:n - - + + zip - -zip - -probe + +zip + +probe probe->zip:n - - + + aiff - -aiff + +aiff probe->aiff:n - - + + apple_bookmark - -apple_bookmark + +apple_bookmark probe->apple_bookmark:n - - + + avro_ocf - -avro_ocf + +avro_ocf probe->avro_ocf:n - - + + bplist - -bplist + +bplist probe->bplist:n - - + + elf - -elf + +elf probe->elf:n - - + + probe->gif:n - - + + html - -html + +html probe->html:n - - + + json - -json + +json probe->json:n - - + + jsonl - -jsonl + +jsonl probe->jsonl:n - - + + luajit - -luajit + +luajit probe->luajit:n - - + + moc3 - -moc3 + +moc3 probe->moc3:n - - + + mpeg_ts - -mpeg_ts + +mpeg_ts probe->mpeg_ts:n - - + + opentimestamps - -opentimestamps + +opentimestamps probe->opentimestamps:n - - + + toml - -toml + +toml probe->toml:n - - + + tzif - -tzif + +tzif probe->tzif:n - - + + wasm - -wasm + +wasm probe->wasm:n - - + + xml - -xml + +xml probe->xml:n - - + + yaml - -yaml + +yaml probe->yaml:n - - + + av1_frame - -av1_frame - -av1_obu + +av1_frame + +av1_obu av1_obu - -av1_obu + +av1_obu av1_frame:e->av1_obu:n - - + + avc_annexb - -avc_annexb - -avc_nalu + +avc_annexb + +avc_nalu avc_nalu - -avc_nalu - -avc_sps - -avc_pps - -avc_sei + +avc_nalu + +avc_sps + +avc_pps + +avc_sei avc_annexb:e->avc_nalu:n - - + + avc_sps - -avc_sps + +avc_sps avc_nalu:e->avc_sps:n - - + + avc_pps - -avc_pps + +avc_pps avc_nalu:e->avc_pps:n - - + + avc_sei - -avc_sei + +avc_sei avc_nalu:e->avc_sei:n - - + + avc_au - -avc_au - -avc_nalu + +avc_au + +avc_nalu avc_au:e->avc_nalu:n - - + + avc_dcr - -avc_dcr - -avc_nalu + +avc_dcr + +avc_nalu avc_dcr:e->avc_nalu:n - - + + avi:e->avc_au:n - - + + hevc_au - -hevc_au - -hevc_nalu + +hevc_au + +hevc_nalu avi:e->hevc_au:n - - + + mp3_frame - -mp3_frame - -mp3_frame_tags + +mp3_frame + +mp3_frame_tags avi:e->mp3_frame:n - - + + flac_frame - -flac_frame + +flac_frame avi:e->flac_frame:n - - + + hevc_nalu - -hevc_nalu - -hevc_vps - -hevc_pps - -hevc_sps + +hevc_nalu + +hevc_vps + +hevc_pps + +hevc_sps hevc_au:e->hevc_nalu:n - - + + mp3_frame_tags - -mp3_frame_tags + +mp3_frame_tags mp3_frame:e->mp3_frame_tags:n - - + + bitcoin_block - -bitcoin_block - -bitcoin_transaction + +bitcoin_block + +bitcoin_transaction bitcoin_blkdat:e->bitcoin_block:n - - + + bitcoin_transaction - -bitcoin_transaction - -bitcoin_script + +bitcoin_transaction + +bitcoin_script bitcoin_block:e->bitcoin_transaction:n - - + + bitcoin_script - -bitcoin_script + +bitcoin_script bitcoin_transaction:e->bitcoin_script:n - - + + bsd_loopback_frame - -bsd_loopback_frame - -inet_packet + +bsd_loopback_frame + +inet_packet inet_packet - -inet_packet + +inet_packet bsd_loopback_frame:e->inet_packet:n - - + + ipv4_packet - -ipv4_packet - -ip_packet + +ipv4_packet + +ip_packet inet_packet->ipv4_packet:n - - + + ipv6_packet - -ipv6_packet - -ip_packet + +ipv6_packet + +ip_packet inet_packet->ipv6_packet:n - - + + bzip2:e->probe:n - - + + caff:e->probe:n - - + + ether8023_frame - -ether8023_frame - -inet_packet + +ether8023_frame + +inet_packet ether8023_frame:e->inet_packet:n - - + + flac:e->flac_frame:n - - + + flac_metadatablocks - -flac_metadatablocks - -flac_metadatablock + +flac_metadatablocks + +flac_metadatablock flac:e->flac_metadatablocks:n - - + + flac_metadatablock - -flac_metadatablock - -flac_streaminfo - -flac_picture - -vorbis_comment + +flac_metadatablock + +flac_streaminfo + +flac_picture + +vorbis_comment flac_metadatablocks:e->flac_metadatablock:n - - + + flac_streaminfo - -flac_streaminfo + +flac_streaminfo flac_metadatablock:e->flac_streaminfo:n - - + + flac_picture - -flac_picture - -image + +flac_picture + +image flac_metadatablock:e->flac_picture:n - - + + vorbis_comment - -vorbis_comment - -flac_picture + +vorbis_comment + +flac_picture flac_metadatablock:e->vorbis_comment:n - - + + flac_picture:e->image:n - - + + vorbis_comment:e->flac_picture:n - - + + gzip:e->probe:n - - + + hevc_annexb - -hevc_annexb - -hevc_nalu + +hevc_annexb + +hevc_nalu hevc_annexb:e->hevc_nalu:n - - + + hevc_vps - -hevc_vps + +hevc_vps hevc_nalu:e->hevc_vps:n - - + + hevc_pps - -hevc_pps + +hevc_pps hevc_nalu:e->hevc_pps:n - - + + hevc_sps - -hevc_sps + +hevc_sps hevc_nalu:e->hevc_sps:n - - + + hevc_dcr - -hevc_dcr - -hevc_nalu + +hevc_dcr + +hevc_nalu hevc_dcr:e->hevc_nalu:n - - + + id3v2 - -id3v2 - -image + +id3v2 + +image id3v2:e->image:n - - + + ip_packet - -ip_packet + +ip_packet ipv4_packet:e->ip_packet:n - - + + udp_datagram - -udp_datagram - -udp_payload + +udp_datagram + +udp_payload ip_packet->udp_datagram:n - - + + icmp - -icmp + +icmp ip_packet->icmp:n - - + + icmpv6 - -icmpv6 + +icmpv6 ip_packet->icmpv6:n - - + + tcp_segment - -tcp_segment + +tcp_segment ip_packet->tcp_segment:n - - + + ipv6_packet:e->ip_packet:n - - + + exif - -exif + +exif jpeg:e->exif:n - - + + icc_profile - -icc_profile + +icc_profile jpeg:e->icc_profile:n - - + + macho_fat:e->macho:n - - + + matroska:e->aac_frame:n - - + + matroska:e->image:n - - + + matroska:e->av1_frame:n - - + + matroska:e->avc_au:n - - + + matroska:e->avc_dcr:n - - + + matroska:e->hevc_au:n - - + + matroska:e->mp3_frame:n - - + + matroska:e->flac_frame:n - - + + matroska:e->flac_metadatablocks:n - - + + matroska:e->hevc_dcr:n - - + + av1_ccr - -av1_ccr + +av1_ccr matroska:e->av1_ccr:n - - + + mpeg_asc - -mpeg_asc + +mpeg_asc matroska:e->mpeg_asc:n - - + + mpeg_pes_packet - -mpeg_pes_packet + +mpeg_pes_packet matroska:e->mpeg_pes_packet:n - - + + mpeg_spu - -mpeg_spu + +mpeg_spu matroska:e->mpeg_spu:n - - + + opus_packet - -opus_packet - -vorbis_comment + +opus_packet + +vorbis_comment matroska:e->opus_packet:n - - + + vorbis_packet - -vorbis_packet - -vorbis_comment + +vorbis_packet + +vorbis_comment matroska:e->vorbis_packet:n - - + + vp8_frame - -vp8_frame + +vp8_frame matroska:e->vp8_frame:n - - + + vp9_cfm - -vp9_cfm + +vp9_cfm matroska:e->vp9_cfm:n - - + + vp9_frame - -vp9_frame + +vp9_frame matroska:e->vp9_frame:n - - + + opus_packet:e->vorbis_comment:n - - + + vorbis_packet:e->vorbis_comment:n - - + + mp3:e->apev2:n - - + + mp3:e->mp3_frame:n - - + + mp3:e->id3v2:n - - + + id3v1 - -id3v1 + +id3v1 mp3:e->id3v1:n - - + + id3v11 - -id3v11 + +id3v11 mp3:e->id3v11:n - - + + mp3_frame_vbri - -mp3_frame_vbri + +mp3_frame_vbri mp3_frame_tags->mp3_frame_vbri:n - - + + mp3_frame_xing - -mp3_frame_xing + +mp3_frame_xing mp3_frame_tags->mp3_frame_xing:n - - + + mp4:e->aac_frame:n - - + + mp4:e->image:n - - + + mp4:e->av1_frame:n - - + + mp4:e->avc_au:n - - + + mp4:e->avc_dcr:n - - + + mp4:e->hevc_au:n - - + + mp4:e->mp3_frame:n - - + + mp4:e->flac_frame:n - - + + mp4:e->flac_metadatablocks:n - - + + mp4:e->hevc_dcr:n - - + + mp4:e->id3v2:n - - + + mp4:e->jpeg:n - - + + mp4:e->icc_profile:n - - + + mp4:e->av1_ccr:n - - + + mp4:e->mpeg_pes_packet:n - - + + mp4:e->opus_packet:n - - + + mp4:e->vorbis_packet:n - - + + mp4:e->vp9_frame:n - - + + mpeg_es - -mpeg_es - -mpeg_asc - -vorbis_packet + +mpeg_es + +mpeg_asc + +vorbis_packet mp4:e->mpeg_es:n - - + + mp4:e->png:n - - + + prores_frame - -prores_frame + +prores_frame mp4:e->prores_frame:n - - + + protobuf_widevine - -protobuf_widevine - -protobuf + +protobuf_widevine + +protobuf mp4:e->protobuf_widevine:n - - + + pssh_playready - -pssh_playready + +pssh_playready mp4:e->pssh_playready:n - - + + vpx_ccr - -vpx_ccr + +vpx_ccr mp4:e->vpx_ccr:n - - + + mpeg_es:e->mpeg_asc:n - - + + mpeg_es:e->vorbis_packet:n - - + + png:e->exif:n - - + + png:e->icc_profile:n - - + + protobuf - -protobuf + +protobuf protobuf_widevine:e->protobuf:n - - + + mpeg_pes - -mpeg_pes - -mpeg_pes_packet - -mpeg_spu + +mpeg_pes + +mpeg_pes_packet + +mpeg_spu mpeg_pes:e->mpeg_pes_packet:n - - + + mpeg_pes:e->mpeg_spu:n - - + + ogg:e->flac_frame:n - - + + ogg:e->flac_metadatablock:n - - + + ogg:e->opus_packet:n - - + + ogg:e->vorbis_packet:n - - + + ogg_page - -ogg_page + +ogg_page ogg:e->ogg_page:n - - + + pcap:e->ipv4_packet:n - - + + link_frame - -link_frame + +link_frame pcap:e->link_frame:n - - + + tcp_stream - -tcp_stream + +tcp_stream pcap:e->tcp_stream:n - - + + link_frame->bsd_loopback_frame:n - - + + link_frame->ether8023_frame:n - - + + link_frame->ipv4_packet:n - - + + link_frame->ipv6_packet:n - - + + sll2_packet - -sll2_packet - -inet_packet + +sll2_packet + +inet_packet link_frame->sll2_packet:n - - + + sll_packet - -sll_packet - -inet_packet + +sll_packet + +inet_packet link_frame->sll_packet:n - - + + rtmp - -rtmp - -amf0 - -mpeg_asc + +rtmp + +amf0 + +mpeg_asc tcp_stream->rtmp:n - - + + tls - -tls - -asn1_ber + +tls + +asn1_ber tcp_stream->tls:n - - + + dns_tcp - -dns_tcp + +dns_tcp tcp_stream->dns_tcp:n - - + + pcapng:e->ipv4_packet:n - - + + pcapng:e->link_frame:n - - + + pcapng:e->tcp_stream:n - - + + rtmp:e->mpeg_asc:n - - + + amf0 - -amf0 + +amf0 rtmp:e->amf0:n - - + + sll2_packet:e->inet_packet:n - - + + sll_packet:e->inet_packet:n - - + + tar:e->probe:n - - + + tiff:e->icc_profile:n - - + + asn1_ber - -asn1_ber + +asn1_ber tls:e->asn1_ber:n - - + + udp_payload - -udp_payload + +udp_payload udp_datagram:e->udp_payload:n - - + + dns - -dns + +dns udp_payload->dns:n - - + + wav:e->id3v2:n - - + + wav:e->id3v1:n - - + + wav:e->id3v11:n - - + + webp:e->vp8_frame:n - - + + zip:e->probe:n - - + + bencode - -bencode + +bencode bits - -bits + +bits bson - -bson + +bson bytes - -bytes + +bytes cbor - -cbor + +cbor csv - -csv + +csv fairplay_spc - -fairplay_spc + +fairplay_spc markdown - -markdown + +markdown msgpack - -msgpack + +msgpack pg_btree - -pg_btree + +pg_btree pg_control - -pg_control + +pg_control pg_heap - -pg_heap + +pg_heap diff --git a/format/format.go b/format/format.go index 88a4f3758..b87fa4b21 100644 --- a/format/format.go +++ b/format/format.go @@ -352,7 +352,8 @@ type MP4_In struct { } type AVI_In struct { - DecodeSamples bool `doc:"Decode samples"` + DecodeSamples bool `doc:"Decode samples"` + DecodeExtendedChunks bool `doc:"Decode extended chunks"` } type Zip_In struct { diff --git a/format/riff/avi.go b/format/riff/avi.go index f894111cc..8d2ccc015 100644 --- a/format/riff/avi.go +++ b/format/riff/avi.go @@ -6,7 +6,6 @@ package riff // DV handler https://learn.microsoft.com/en-us/windows/win32/directshow/dv-data-in-the-avi-file-format // palette change // rec groups -// AVIX, multiple RIFF headers? // nested indexes // unknown fields for unreachable chunk header for > 1gb samples // 2fields, field index? @@ -42,7 +41,8 @@ func init() { Description: "Audio Video Interleaved", DecodeFn: aviDecode, DefaultInArg: format.AVI_In{ - DecodeSamples: true, + DecodeSamples: true, + DecodeExtendedChunks: true, }, Dependencies: []decode.Dependency{ {Groups: []*decode.Group{format.AVC_AU}, Out: &aviMpegAVCAUGroup}, @@ -110,8 +110,6 @@ var aviStreamChunkTypeDescriptions = scalar.StrMapDescription{ aviStreamChunkTypeIndex: "Index", } -const aviRiffType = "AVI " - type idx1Sample struct { offset int64 size int64 @@ -220,17 +218,17 @@ func aviDecodeChunkIndex(d *decode.D) []ranges.Range { return rs } -func aviDecode(d *decode.D) any { - var ai format.AVI_In - d.ArgAs(&ai) - - d.Endian = decode.LittleEndian - +func aviDecodeEx(d *decode.D, ai format.AVI_In, extendedChunk bool) { var streams []*aviStream var idx1Samples []idx1Sample var moviListPos int64 // point to first bit after type - var riffType string + requiredRiffType := "AVI " + if extendedChunk { + requiredRiffType = "AVIX" + } + var foundRiffType string + riffDecode( d, nil, @@ -243,7 +241,7 @@ func aviDecode(d *decode.D) any { func(d *decode.D, id string, path path) (bool, any) { switch id { case "RIFF": - riffType = d.FieldUTF8("type", 4, d.StrAssert(aviRiffType)) + foundRiffType = d.FieldUTF8("type", 4, d.StrAssert(requiredRiffType)) return true, nil case "LIST": @@ -536,97 +534,124 @@ func aviDecode(d *decode.D) any { }, ) - if riffType != aviRiffType { - d.Errorf("wrong or no AVI riff type found (%s)", riffType) + if foundRiffType != requiredRiffType { + d.Errorf("wrong or no AVI riff type found (%s)", requiredRiffType) } - d.FieldArray("streams", func(d *decode.D) { - for streamIndex, stream := range streams { + if !extendedChunk { + d.FieldArray("streams", func(d *decode.D) { + for streamIndex, stream := range streams { + + d.FieldStruct("stream", func(d *decode.D) { + d.FieldValueStr("type", stream.typ) + d.FieldValueStr("handler", stream.handler) + switch stream.typ { + case aviStrhTypeAudio: + d.FieldValueUint("format_tag", stream.formatTag, format.WAVTagNames) + case aviStrhTypeVideo: + d.FieldValueStr("compression", stream.compression) + } - d.FieldStruct("stream", func(d *decode.D) { - d.FieldValueStr("type", stream.typ) - d.FieldValueStr("handler", stream.handler) - switch stream.typ { - case aviStrhTypeAudio: - d.FieldValueUint("format_tag", stream.formatTag, format.WAVTagNames) - case aviStrhTypeVideo: - d.FieldValueStr("compression", stream.compression) - } + var streamIndexSampleRanges []ranges.Range + if len(stream.indexes) > 0 { + d.FieldArray("indexes", func(d *decode.D) { + for _, i := range stream.indexes { + d.FieldStruct("index", func(d *decode.D) { + d.RangeFn(i.Start, i.Len, func(d *decode.D) { + d.FieldUTF8("type", 4) + d.FieldU32("cb") + sampleRanges := aviDecodeChunkIndex(d) + streamIndexSampleRanges = append(streamIndexSampleRanges, sampleRanges...) + }) + }) + } + }) + } + + // TODO: palette change + decodeSample := func(d *decode.D, sr ranges.Range) { + d.RangeFn(sr.Start, sr.Len, func(d *decode.D) { + if sr.Len == 0 { + d.FieldRawLen("sample", d.BitsLeft()) + return + } - var streamIndexSampleRanges []ranges.Range - if len(stream.indexes) > 0 { - d.FieldArray("indexes", func(d *decode.D) { - for _, i := range stream.indexes { - d.FieldStruct("index", func(d *decode.D) { - d.RangeFn(i.Start, i.Len, func(d *decode.D) { - d.FieldUTF8("type", 4) - d.FieldU32("cb") - sampleRanges := aviDecodeChunkIndex(d) - streamIndexSampleRanges = append(streamIndexSampleRanges, sampleRanges...) + subSampleSize := int64(stream.sampleSize) * 8 + // TODO: <= no format and <= 8*8 heuristics to not create separate pcm samples + if subSampleSize == 0 || (!stream.hasFormat && subSampleSize <= 8*8) { + subSampleSize = sr.Len + } + + for d.BitsLeft() > 0 { + d.FramedFn(subSampleSize, func(d *decode.D) { + if ai.DecodeSamples && stream.hasFormat { + d.FieldFormat("sample", stream.format, stream.formatInArg) + } else { + d.FieldRawLen("sample", d.BitsLeft()) + } }) - }) - } - }) - } + } + }) + } - // TODO: palette change - decodeSample := func(d *decode.D, sr ranges.Range) { - d.RangeFn(sr.Start, sr.Len, func(d *decode.D) { - if sr.Len == 0 { - d.FieldRawLen("sample", d.BitsLeft()) - return - } - - subSampleSize := int64(stream.sampleSize) * 8 - // TODO: <= no format and <= 8*8 heuristics to not create separate pcm samples - if subSampleSize == 0 || (!stream.hasFormat && subSampleSize <= 8*8) { - subSampleSize = sr.Len - } - - for d.BitsLeft() > 0 { - d.FramedFn(subSampleSize, func(d *decode.D) { - if ai.DecodeSamples && stream.hasFormat { - d.FieldFormat("sample", stream.format, stream.formatInArg) - } else { - d.FieldRawLen("sample", d.BitsLeft()) + // try only add indexed samples once with priority: + // stream index + // ix chunks (might be same as stream index) + // idx1 chunks + if len(streamIndexSampleRanges) > 0 { + d.FieldArray("samples", func(d *decode.D) { + for _, sr := range streamIndexSampleRanges { + decodeSample(d, sr) + } + }) + } else if len(stream.ixSamples) > 0 { + d.FieldArray("samples", func(d *decode.D) { + for _, sr := range stream.ixSamples { + decodeSample(d, sr) + } + }) + } else if len(idx1Samples) > 0 { + d.FieldArray("samples", func(d *decode.D) { + for _, is := range idx1Samples { + if is.streamNr != streamIndex { + continue } - }) - } - }) - } - - // try only add indexed samples once with priority: - // stream index - // ix chunks (might be same as stream index) - // idx chunks - if len(streamIndexSampleRanges) > 0 { - d.FieldArray("samples", func(d *decode.D) { - for _, sr := range streamIndexSampleRanges { - decodeSample(d, sr) - } - }) - } else if len(stream.ixSamples) > 0 { - d.FieldArray("samples", func(d *decode.D) { - for _, sr := range stream.ixSamples { - decodeSample(d, sr) - } - }) - } else if len(idx1Samples) > 0 { - d.FieldArray("samples", func(d *decode.D) { - for _, is := range idx1Samples { - if is.streamNr != streamIndex { - continue + decodeSample(d, ranges.Range{ + Start: moviListPos + is.offset + 32, // +32 skip size field + Len: is.size, + }) } - decodeSample(d, ranges.Range{ - Start: moviListPos + is.offset + 32, // +32 skip size field - Len: is.size, - }) - } - }) + }) + } + }) + } + }) + } +} + +func aviDecode(d *decode.D) any { + var ai format.AVI_In + d.ArgAs(&ai) + + d.Endian = decode.LittleEndian + + aviDecodeEx(d, ai, false) + + if ai.DecodeExtendedChunks { + d.FieldArray("extended_chunks", func(d *decode.D) { + for { + // TODO: other way? spec says check hdrx chunk but there seems to be none? + riff, _ := d.TryPeekBytes(4) + if string(riff) != "RIFF" { + break } - }) - } - }) + + d.FieldStruct("chunk", func(d *decode.D) { + aviDecodeEx(d, ai, true) + }) + } + }) + } return nil } diff --git a/format/riff/avi.md b/format/riff/avi.md index b0bffc3c4..395cddb3b 100644 --- a/format/riff/avi.md +++ b/format/riff/avi.md @@ -13,6 +13,13 @@ $ fq '.streams[1].samples[] | tobytes' file.avi > stream01.mp3 $ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi ``` +### Speed up decoding by disabling sample and extended chunks decoding + +If your not interested in sample details or extended chunks you can speed up decoding by using: +```sh +$ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi +``` + ### References - [AVI RIFF File Reference](https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference) diff --git a/format/riff/testdata/avc.avi.fqtest b/format/riff/testdata/avc.avi.fqtest index 999911f44..33ae50965 100644 --- a/format/riff/testdata/avc.avi.fqtest +++ b/format/riff/testdata/avc.avi.fqtest @@ -490,3 +490,4 @@ $ fq dv avc.avi | | | type: "vids" | | | handler: "H264" | | | compression: "H264" + | | | extended_chunks[0:0]: 0x2442-NA (0) diff --git a/format/riff/testdata/flac.avi.fqtest b/format/riff/testdata/flac.avi.fqtest index 496dbf340..81bfa0354 100644 --- a/format/riff/testdata/flac.avi.fqtest +++ b/format/riff/testdata/flac.avi.fqtest @@ -263,3 +263,4 @@ $ fq dv flac.avi | | | type: "auds" | | | handler: "\x01\x00\x00\x00" | | | format_tag: "flac" (61868) + | | | extended_chunks[0:0]: 0x18cc-NA (0) diff --git a/format/riff/testdata/help_avi.fqtest b/format/riff/testdata/help_avi.fqtest index c94500ae3..2f0bc3b82 100644 --- a/format/riff/testdata/help_avi.fqtest +++ b/format/riff/testdata/help_avi.fqtest @@ -4,7 +4,8 @@ avi: Audio Video Interleaved decoder Options ======= - decode_samples=true Decode samples + decode_extended_chunks=true Decode extended chunks + decode_samples=true Decode samples Decode examples =============== @@ -14,9 +15,9 @@ Decode examples # Decode value as avi ... | avi # Decode file using avi options - $ fq -d avi -o decode_samples=true . file + $ fq -d avi -o decode_extended_chunks=true -o decode_samples=true . file # Decode value as avi - ... | avi({decode_samples:true}) + ... | avi({decode_extended_chunks:true,decode_samples:true}) Samples ======= @@ -31,6 +32,12 @@ Show stream summary =================== $ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi +Speed up decoding by disabling sample and extended chunks decoding +================================================================== +If your not interested in sample details or extended chunks you can speed up decoding by using: + + $ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi + References ========== - AVI RIFF File Reference (https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference) diff --git a/format/riff/testdata/mp3.avi.fqtest b/format/riff/testdata/mp3.avi.fqtest index c4d7985f2..17bbf3c3c 100644 --- a/format/riff/testdata/mp3.avi.fqtest +++ b/format/riff/testdata/mp3.avi.fqtest @@ -537,3 +537,4 @@ $ fq dv mp3.avi | | | type: "auds" | | | handler: "\x01\x00\x00\x00" | | | format_tag: "mp3" (85) + | | | extended_chunks[0:0]: 0x18e8-NA (0) diff --git a/format/riff/testdata/pcm.avi.fqtest b/format/riff/testdata/pcm.avi.fqtest index 593750461..3b5f0ef8a 100644 --- a/format/riff/testdata/pcm.avi.fqtest +++ b/format/riff/testdata/pcm.avi.fqtest @@ -243,3 +243,4 @@ $ fq dv pcm.avi | | | type: "auds" | | | handler: "\x01\x00\x00\x00" | | | format_tag: "pcm_s16le" (1) + | | | extended_chunks[0:0]: 0x390a-NA (0)