|
| 1 | +#region License |
| 2 | +// |
| 3 | +// Copyright 2002-2017 Drew Noakes |
| 4 | +// Ported from Java to C# by Yakov Danilov for Imazen LLC in 2014 |
| 5 | +// |
| 6 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +// you may not use this file except in compliance with the License. |
| 8 | +// You may obtain a copy of the License at |
| 9 | +// |
| 10 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +// |
| 12 | +// Unless required by applicable law or agreed to in writing, software |
| 13 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +// See the License for the specific language governing permissions and |
| 16 | +// limitations under the License. |
| 17 | +// |
| 18 | +// More information about this project is available at: |
| 19 | +// |
| 20 | +// https://github.com/drewnoakes/metadata-extractor-dotnet |
| 21 | +// https://drewnoakes.com/code/exif/ |
| 22 | +// |
| 23 | +#endregion |
| 24 | + |
| 25 | +using System; |
| 26 | +using System.Collections.Generic; |
| 27 | +using System.IO; |
| 28 | +using System.Text; |
| 29 | +using JetBrains.Annotations; |
| 30 | +using MetadataExtractor.Formats.Riff; |
| 31 | +using MetadataExtractor.IO; |
| 32 | + |
| 33 | +namespace MetadataExtractor.Formats.Avi |
| 34 | +{ |
| 35 | + /// <summary> |
| 36 | + /// Implementation of <see cref="IRiffHandler"/> specialising in AVI support. |
| 37 | + /// </summary> |
| 38 | + /// <remarks> |
| 39 | + /// Extracts data from chunk/list types: |
| 40 | + /// <list type="bullet"> |
| 41 | + /// <item><c>"avih"</c>: width, height, streams</item> |
| 42 | + /// <item><c>"strh"</c>: frames/second, samples/second, duration, video codec</item> |
| 43 | + /// </list> |
| 44 | + /// Sources: |
| 45 | + /// http://www.alexander-noe.com/video/documentation/avi.pdf |
| 46 | + /// https://msdn.microsoft.com/en-us/library/ms899422.aspx |
| 47 | + /// https://www.loc.gov/preservation/digital/formats/fdd/fdd000025.shtml |
| 48 | + /// </remarks> |
| 49 | + /// <author>Payton Garland</author> |
| 50 | + public sealed class AviRiffHandler : IRiffHandler |
| 51 | + { |
| 52 | + [NotNull] |
| 53 | + private readonly List<Directory> _directories; |
| 54 | + |
| 55 | + public AviRiffHandler([NotNull] List<Directory> directories) |
| 56 | + { |
| 57 | + _directories = directories; |
| 58 | + } |
| 59 | + |
| 60 | + public bool ShouldAcceptRiffIdentifier(string identifier) => identifier == "AVI "; |
| 61 | + |
| 62 | + public bool ShouldAcceptChunk(string fourCc) => fourCc == "strh" || |
| 63 | + fourCc == "avih"; |
| 64 | + |
| 65 | + public bool ShouldAcceptList(string fourCc) => fourCc == "hdrl" || |
| 66 | + fourCc == "strl" || |
| 67 | + fourCc == "AVI "; |
| 68 | + |
| 69 | + public void ProcessChunk(string fourCc, byte[] payload) |
| 70 | + { |
| 71 | + switch (fourCc) |
| 72 | + { |
| 73 | + case "strh": |
| 74 | + { |
| 75 | + string error = null; |
| 76 | + var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); |
| 77 | + string fccType = null; |
| 78 | + string fccHandler = null; |
| 79 | + float dwScale = 0; |
| 80 | + float dwRate = 0; |
| 81 | + int dwLength = 0; |
| 82 | + try |
| 83 | + { |
| 84 | + fccType = reader.GetString(0, 4, Encoding.ASCII); |
| 85 | + fccHandler = reader.GetString(4, 4, Encoding.ASCII); |
| 86 | + //int dwFlags = reader.GetInt32(8); |
| 87 | + //int wPriority = reader.GetInt16(12); |
| 88 | + //int wLanguage = reader.GetInt16(14); |
| 89 | + //int dwInitialFrames = reader.GetInt32(16); |
| 90 | + dwScale = reader.GetFloat32(20); |
| 91 | + dwRate = reader.GetFloat32(24); |
| 92 | + //int dwStart = reader.GetInt32(28); |
| 93 | + dwLength = reader.GetInt32(32); |
| 94 | + //int dwSuggestedBufferSize = reader.GetInt32(36); |
| 95 | + //int dwQuality = reader.GetInt32(40); |
| 96 | + //int dwSampleSize = reader.GetInt32(44); |
| 97 | + //byte[] rcFrame = reader.GetBytes(48, 2); |
| 98 | + } |
| 99 | + catch (IOException e) |
| 100 | + { |
| 101 | + error = "Exception reading AviRiff chunk 'strh' : " + e.Message; |
| 102 | + } |
| 103 | + |
| 104 | + var directory = new AviDirectory(); |
| 105 | + if (error == null) |
| 106 | + { |
| 107 | + if (fccType == "vids") |
| 108 | + { |
| 109 | + directory.Set(AviDirectory.TAG_FRAMES_PER_SECOND, (dwRate / dwScale)); |
| 110 | + |
| 111 | + double duration = dwLength / (dwRate / dwScale); |
| 112 | + int hours = (int)duration / (int)(Math.Pow(60, 2)); |
| 113 | + int minutes = ((int)duration / (int)(Math.Pow(60, 1))) - (hours * 60); |
| 114 | + int seconds = (int)Math.Round((duration / (Math.Pow(60, 0))) - (minutes * 60)); |
| 115 | + string time = new DateTime(2000, 1, 1, hours, minutes, seconds).ToString("hh:mm:ss"); |
| 116 | + |
| 117 | + directory.Set(AviDirectory.TAG_DURATION, time); |
| 118 | + directory.Set(AviDirectory.TAG_VIDEO_CODEC, fccHandler); |
| 119 | + } |
| 120 | + else |
| 121 | + if (fccType == "auds") |
| 122 | + { |
| 123 | + directory.Set(AviDirectory.TAG_SAMPLES_PER_SECOND, (dwRate / dwScale)); |
| 124 | + } |
| 125 | + } |
| 126 | + else |
| 127 | + directory.AddError(error); |
| 128 | + _directories.Add(directory); |
| 129 | + break; |
| 130 | + } |
| 131 | + case "avih": |
| 132 | + { |
| 133 | + string error = null; |
| 134 | + var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); |
| 135 | + int dwStreams = 0; |
| 136 | + int dwWidth = 0; |
| 137 | + int dwHeight = 0; |
| 138 | + try |
| 139 | + { |
| 140 | + //int dwMicroSecPerFrame = reader.GetInt32(0); |
| 141 | + //int dwMaxBytesPerSec = reader.GetInt32(4); |
| 142 | + //int dwPaddingGranularity = reader.GetInt32(8); |
| 143 | + //int dwFlags = reader.GetInt32(12); |
| 144 | + //int dwTotalFrames = reader.GetInt32(16); |
| 145 | + //int dwInitialFrames = reader.GetInt32(20); |
| 146 | + dwStreams = reader.GetInt32(24); |
| 147 | + //int dwSuggestedBufferSize = reader.GetInt32(28); |
| 148 | + dwWidth = reader.GetInt32(32); |
| 149 | + dwHeight = reader.GetInt32(36); |
| 150 | + //byte[] dwReserved = reader.GetBytes(40, 4); |
| 151 | + } |
| 152 | + catch (IOException e) |
| 153 | + { |
| 154 | + error = "Exception reading AviRiff chunk 'avih' : " + e.Message; |
| 155 | + } |
| 156 | + |
| 157 | + var directory = new AviDirectory(); |
| 158 | + if (error == null) |
| 159 | + { |
| 160 | + directory.Set(AviDirectory.TAG_WIDTH, dwWidth); |
| 161 | + directory.Set(AviDirectory.TAG_HEIGHT, dwHeight); |
| 162 | + directory.Set(AviDirectory.TAG_STREAMS, dwStreams); |
| 163 | + } |
| 164 | + else |
| 165 | + directory.AddError(error); |
| 166 | + _directories.Add(directory); |
| 167 | + break; |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + } |
| 172 | +} |
0 commit comments