/// <summary> /// Generate a 15-bit video fingerprint from the input YUV244 buffer /// This is currently unused but could be used to regenerate timecode on the destination video /// if this has been lost in the trancode process. There are many other potential uses for this fingerprint data. /// /// The algorithm divides the frame into 16 blocks (4x4) and applies the Hadamard masks to divide these blocks /// into two sets in 15 different and orthogonal ways. The relative brightness of each pair of sets yields one /// bit of the fingerprint. The entire fingerprint is comprised of 15 bits; one from each Hadamard mask. /// The whole algorithm is resilient to changes of resolution, aspect, brightness, contrast and gamma that might occur in the /// transcode process. It would be a simple matter to automatically detect letterboxing/pillarboxing and to only analyse the /// active video area if this were required. /// Testing with transcoded broadcast video has shown the fingerprint to be robust and reliable. /// </summary> /// <param name="videoFrame"></param> /// <param name="sums"></param> /// <returns></returns> private unsafe int VideoFingerprint(IDeckLinkVideoInputFrame videoFrame, int[] sums) { var qheight = videoFrame.GetHeight() >> 2; var qwidth = videoFrame.GetWidth() >> 2; IntPtr buffer; videoFrame.GetBytes(out buffer); var ptr = (byte *)buffer + 1; // Skip over first U byte for (var r = 0; r < qheight; r++) { for (var c = 0; c < qwidth; c++) { sums[0] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[1] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[2] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[3] += *ptr; ptr += 2; } } for (var r = 0; r < qheight; r++) { for (var c = 0; c < qwidth; c++) { sums[4] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[5] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[6] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[7] += *ptr; ptr += 2; } } for (var r = 0; r < qheight; r++) { for (var c = 0; c < qwidth; c++) { sums[8] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[9] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[10] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[11] += *ptr; ptr += 2; } } for (var r = 0; r < qheight; r++) { for (var c = 0; c < qwidth; c++) { sums[12] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[13] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[14] += *ptr; ptr += 2; } for (var c = 0; c < qwidth; c++) { sums[15] += *ptr; ptr += 2; } } for (var i = 0; i < 16; i++) { sums[i] /= qwidth * qheight; } var videoFingerprint = 0; var bit = 1; for (var i = 0; i < 15; i++) { var sum = 0; for (var j = 0; j < 16; j++) { sum += sums[j] * masks[i, j]; } if (sum > 0) { videoFingerprint |= bit; } bit <<= 1; } return(videoFingerprint); }
void IDeckLinkInputCallback.VideoInputFrameArrived(IDeckLinkVideoInputFrame videoFrame, IDeckLinkAudioInputPacket audioPacket) { if (videoFrame != null) { try { var frameFlags = videoFrame.GetFlags(); bool inputSignal = frameFlags.HasFlag(_BMDFrameFlags.bmdFrameHasNoInputSource); if (inputSignal != validInputSignal) { validInputSignal = inputSignal; InputSignalChanged?.Invoke(validInputSignal); } else { int width = videoFrame.GetWidth(); int height = videoFrame.GetHeight(); int stride = videoFrame.GetRowBytes(); var format = videoFrame.GetPixelFormat(); var bufferLength = stride * height; videoFrame.GetBytes(out IntPtr pBuffer); VideoDataArrived?.Invoke(pBuffer, bufferLength, 0); //var f = File.Create(@"d:\testBMP2\" + DateTime.Now.ToString("HH_mm_ss_fff") + " " + width + "x" + height + "_" + format + ".raw"); //byte[] data = new byte[bufferLength]; //Marshal.Copy(pBuffer, data, 0, data.Length); //f.Write(data, 0, data.Length); //f.Close(); } } finally { Marshal.ReleaseComObject(videoFrame); } } if (audioPacket != null) { try { long packetTime = 0; //audioPacket.GetPacketTime(out packetTime, 30000); int sampleSize = ((int)AudioSampleType / 8); //32bit int samplesCount = audioPacket.GetSampleFrameCount(); int dataLength = sampleSize * AudioChannelsCount * samplesCount; if (dataLength > 0) { audioPacket.GetBytes(out IntPtr pBuffer); if (pBuffer != IntPtr.Zero) { byte[] data = new byte[dataLength]; Marshal.Copy(pBuffer, data, 0, data.Length); AudioDataArrived?.Invoke(data, packetTime); } } } finally { Marshal.ReleaseComObject(audioPacket); } } }