int main() { try { cout << "Creating script environment 1..." << endl; IScriptEnvironment* env1 = CreateScriptEnvironment(3); cout << "Creating script environment 2..." << endl; IScriptEnvironment* env2 = CreateScriptEnvironment(3); cout << "Deleting script environment 1..." << endl; delete env1; cout << "Invoking BlankClip on env 2..." << endl; AVSValue ret = env2->Invoke("BlankClip", AVSValue(), 0); PClip clp = ret.AsClip(); cout << "Reading frame 0 from env2..." << endl; PVideoFrame frm = clp->GetFrame(0, env2); } catch (AvisynthError &e) { cerr << "AvisynthError: " << e.msg << endl; return -1; } catch (...) { cerr << "unknown error" << endl; return -1; } return 0; }
PVideoFrame __stdcall ShapeMask::GetFrame(int n, IScriptEnvironment* env) { int colorspace; if (vi.IsRGB24()) colorspace = RGB24; else if (vi.IsRGB32()) colorspace = RGB32; else if (vi.IsYUY2()) colorspace = YUV2; else if (vi.IsYV12()) colorspace = YV12; else raiseError(env, "Unsupported color space, must be one of RGB24, RGB32, YUV2 or YV12"); PClip srcClip = toGrayScale(env, child); PVideoFrame src = srcClip->GetFrame(n, env); PVideoFrame dst = env->NewVideoFrame(vi); const uchar* srcp = src->GetReadPtr(); const int src_pitch = src->GetPitch(); const int bpp = vi.BitsPerPixel(); uchar* retp; // No change to the source pixels in the process steps, so ok to cast to non-const // returns a 1 channel gray scale image which needs to be converted to whatever format the source clip is in. retp = process_frame((uchar*)srcp, vi.width, vi.height, src_pitch, colorspace, threshold, minarea, rectonly); if (vi.IsPlanar()) copyPlanar(retp, dst, bpp); else if (vi.IsYUY2()) copyYUY2(retp, dst); else copyRGB(retp, dst, bpp); delete retp; return dst; }
PVideoFrame __stdcall SimpleSample::GetFrame(int n, IScriptEnvironment* env) { // This is the implementation of the GetFrame function. // See the header definition for further info. PVideoFrame src = child->GetFrame(n, env); // Request frame 'n' from the child (source) clip. PVideoFrame window = WindowVideo->GetFrame(n, env); // Request frame "'n" from the WindowVideo clip PVideoFrame dst = env->NewVideoFrame(vi); // Construct a frame based on the information of the current frame // contained in the "vi" struct. /* GstAVSynth: copy timestamp from source to destination buffer * without modifying it */ dst->SetTimestamp (src->GetTimestamp ()); const unsigned char* srcp = src->GetReadPtr(); // Request a Read pointer from the source frame. // This will return the position of the upperleft pixel in YUY2 images, // and return the lower-left pixel in RGB. // RGB images are stored upside-down in memory. // You should still process images from line 0 to height. unsigned char* dstp = dst->GetWritePtr(); // Request a Write pointer from the newly created destination image. // You can request a writepointer to images that have just been // created by NewVideoFrame. If you recieve a frame from PClip->GetFrame(...) // you must call env->MakeWritable(&frame) be recieve a valid write pointer. const int dst_pitch = dst->GetPitch(); // Requests pitch (length of a line) of the destination image. // For more information on pitch see: http://www.avisynth.org/index.php?page=WorkingWithImages // (short version - pitch is always equal to or greater than width to allow for seriously fast assembly code) const int dst_width = dst->GetRowSize(); // Requests rowsize (number of used bytes in a line. // See the link above for more information. const int dst_height = dst->GetHeight(); // Requests the height of the destination image. const int src_pitch = src->GetPitch(); const int src_width = src->GetRowSize(); const int src_height = src->GetHeight(); const unsigned char* windowp=window->GetReadPtr(); const int window_pitch = window->GetPitch(); const int window_width = window->GetRowSize(); const int window_height = window->GetHeight(); // Get info on the Windowed Clip (see src definitions for more information) int w, h; // This version of SimpleSample is intended to show how to utilise information from 2 clips in YUY2 // colourspace only. The original V1.6 code has been left in place fro all other // colourspaces. // It is designed purely for clarity and not as good or clever code :-) if (vi.IsRGB24()) { // The code just deals with RGB24 colourspace where each pixel is represented by // 3 bytes, Blue, Green and Red. // Although this colourspace is the easiest to understand, it is very rarely used because // a 3 byte sequence (24bits) cannot be processed easily using normal 32 bit registers. /* for (h=0; h < src_height;h++) { // Loop from bottom line to top line. for (w = 0; w < src_width; w+=3) { // Loop from left side of the image to the right side 1 pixel (3 bytes) at a time // stepping 3 bytes (a pixel width in RGB24 space) *(dstp + w) = *(srcp + w); // Copy each Blue byte from source to destination. *(dstp + w + 1) = *(srcp + w + 1); // Copy Green. *(dstp + w + 2) = *(srcp + w + 2); // Copy Red } srcp = srcp + src_pitch; // Add the pitch (note use of pitch and not width) of one line (in bytes) to the source pointer dstp = dstp + dst_pitch; // Add the pitch to the destination pointer. } */ env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr(), src->GetPitch(), src->GetRowSize(), src->GetHeight()); // end copy src to dst //Now draw a white square in the middle of the frame // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(); // reset the destination pointer to the bottom, left pixel. (RGB colourspaces only) dstp = dstp + (dst_height/2 - SquareSize/2)*dst_pitch; // move pointer to SquareSize/2 lines from the middle of the frame; for (h=0; h < SquareSize;h++) { // only scan 100 lines for (w = dst_width/2 - SquareSize*3/2; w < dst_width/2 + SquareSize*3/2; w+=3) { // only scans the middle SquareSize pixels of a line *(dstp + w) = 255; // Set Blue to maximum value. *(dstp + w + 1) = 255; // and Green. *(dstp + w + 2) = 255; // and Red - therefore the whole pixel is now white. } dstp = dstp + dst_pitch; } } if (vi.IsRGB32()) { // This code deals with RGB32 colourspace where each pixel is represented by // 4 bytes, Blue, Green and Red and "spare" byte that could/should be used for alpha // keying but usually isn't. // Although this colourspace isn't memory efficient, code end ups running much // quicker than RGB24 as you can deal with whole 32bit variables at a time // and easily work directly and quickly in assembler (if you know how to that is :-) env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr(), src->GetPitch(), src->GetRowSize(), src->GetHeight()); // end copy src to dst //Now draw a white square in the middle of the frame // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(); // reset the destination pointer to the bottom, left pixel. (RGB colourspaces only) dstp = dstp + (dst_height/2 - SquareSize/2)*dst_pitch; // move pointer to SquareSize/2 lines from the middle of the frame; int woffset = dst_width/8 - SquareSize/2; // lets precalulate the width offset like we do for the lines. for (h=0; h < SquareSize;h++) { // only scan SquareSize number of lines for (w = 0; w < SquareSize; w+=1) { // only scans the middle SquareSize pixels of a line *((unsigned int *)dstp + woffset + w) = 0x00FFFFFF; // Set Red,Green and Blue to maximum value in 1 instruction. // LSB = Blue, MSB = "spare" byte } dstp = dstp + dst_pitch; } } if (vi.IsYUY2()) { // This code deals with YUY2 colourspace where each 4 byte sequence represents // 2 pixels, (Y1, U, Y2 and then V). // This colourspace is more memory efficient than RGB32 but can be more awkward to use sometimes. // However, it can still be manipulated 32bits at a time depending on the // type of filter you are writing // There is no difference in code for this loop and the RGB32 code due to a coincidence :-) // 1) YUY2 frame_width is half of an RGB32 one // 2) But in YUY2 colourspace, a 32bit variable holds 2 pixels instead of the 1 in RGB32 colourspace. env->BitBlt(dst->GetWritePtr(), dst_pitch, src->GetReadPtr(), src_pitch, src_width, src_height); // end copy src to dst //Now draw the other clip inside a square in the middle of the frame // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(); // reset the destination pointer to the top, left pixel. (YUY2 colourspace only) dstp = dstp + (dst_height/2 - SquareSize/2)*dst_pitch + dst_width/2 - SquareSize; // move pointer to SquareSize/2 lines from the middle of the frame; windowp = window->GetReadPtr(); env->BitBlt(dstp, dst_pitch, windowp, window_pitch, SquareSize*2, SquareSize); } if (vi.IsYV12()) { // This code deals with YV12 colourspace where the Y, U and V information are // stored in completely separate memory areas // This colourspace is the most memory efficient but usually requires 3 separate loops // However, it can actually be easier to deal with than YUY2 depending on your filter algorithim // So first of all deal with the Y Plane for (h=0; h < src_height;h++) { // Loop from top line to bottom line (Sames as YUY2. for (w = 0; w < src_width; w++) // Loop from left side of the image to the right side. *(dstp + w) = *(srcp + w); // Copy each byte from source to destination. srcp = srcp + src_pitch; // Add the pitch (note use of pitch and not width) of one line (in bytes) to the source image. dstp = dstp + dst_pitch; // Add the pitch of one line (in bytes) to the destination. } // end copy Y Plane src to dst //Now set the Y plane bytes to maximum in the middle of the frame // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(); // reset the destination pointer to the top, left pixel. dstp = dstp + (dst_height/2 - SquareSize/2)*dst_pitch; // move pointer to SquareSize/2 lines from the middle of the frame; int woffset = dst_width/2 - SquareSize/2; // lets precalulate the width offset like we do for the lines. for (h=0; h < SquareSize;h++) { // only scan SquareSize number of lines for (w = 0; w < SquareSize; w+=1) { // only scans the middle SquareSize pixels of a line *(dstp + woffset + w) = 235; // Set Y values to maximum } dstp = dstp + dst_pitch; } // end of Y plane Code // This section of code deals with the U and V planes of planar formats (e.g. YV12) // So first of all we have to get the additional info on the U and V planes const int dst_pitchUV = dst->GetPitch(PLANAR_U); // The pitch,height and width information const int dst_widthUV = dst->GetRowSize(PLANAR_U); // is guaranted to be the same for both const int dst_heightUV = dst->GetHeight(PLANAR_U); // the U and V planes so we only the U const int src_pitchUV = src->GetPitch(PLANAR_U); // plane values and use them for V as const int src_widthUV = src->GetRowSize(PLANAR_U); // well const int src_heightUV = src->GetHeight(PLANAR_U); // //Copy U plane src to dst srcp = src->GetReadPtr(PLANAR_U); dstp = dst->GetWritePtr(PLANAR_U); for (h=0; h < src_heightUV;h++) { for (w = 0; w < src_widthUV; w++) *(dstp + w) = *(srcp + w); srcp = srcp + src_pitchUV; dstp = dstp + dst_pitchUV; } // end copy U plane src to dst //Now set the U plane bytes to no colour in the middle of the frame // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(PLANAR_U); // reset the destination pointer to the top, left pixel. dstp = dstp + (dst_heightUV/2 - SquareSize/4)*dst_pitchUV; // note change in how much we dived SquareSize by // as the U plane height is half the Y plane woffset = dst_widthUV/2 - SquareSize/4; // And the divisor changes here as well compared to Y plane code. for (h=0; h < SquareSize/2;h++) { // only scan SquareSize/2 number of lines (because the U plane height is half the Y) for (w = 0; w < SquareSize/2; w+=1) { // only scans the middle SquareSize/2 bytes of a line because ... U=Y/2 :-) *(dstp + woffset + w) = 128; // Set U Value to no colour } dstp = dstp + dst_pitchUV; } // end of U plane Code //Copy V plane src to dst srcp = src->GetReadPtr(PLANAR_V); dstp = dst->GetWritePtr(PLANAR_V); for (h=0; h < src_heightUV;h++) { for (w = 0; w < src_widthUV; w++) *(dstp + w) = *(srcp + w); srcp = srcp + src_pitchUV; dstp = dstp + dst_pitchUV; } // end copy V plane src to dst //Now set the V plane bytes to no colour in the middle of the frame // the code is identical to the code for U plane apart from getting the frame start pointer. // Normally you'd do this code within the loop above but here it is in a separate loop for clarity; dstp = dst->GetWritePtr(PLANAR_V); // reset the destination pointer to the top, left pixel. dstp = dstp + (dst_heightUV/2 - SquareSize/4)*dst_pitchUV; // note change in how much we dived SquareSize by // as the V plane height is half the Y plane woffset = dst_widthUV/2 - SquareSize/4; // And the divisor changes here as well compared to Y plane code. for (h=0; h < SquareSize/2;h++) { // only scan SquareSize/2 number of lines (because the V plane height is half the Y) for (w = 0; w < SquareSize/2; w+=1) { // only scans the middle SquareSize/2 bytes of a line because ... V=Y/2 :-) *(dstp + woffset + w) = 128; // Set V Value to no colour } dstp = dstp + dst_pitchUV; } // end of U plane Code } // As we now are finished processing the image, we return the destination image. return dst; }
int AvxContext::OutputVideo() { FILE *sink; unsigned char *writeBuffer = NULL; sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN); if (launchMPlayer) { char command[1024]; if (MPlayerCommandVideo(command)) return -1; AVXLOG_INFO("MPlayer command line: %s", command); sink = popen(command, "w"); if (!sink) { AVXLOG_ERROR("%s", "Error starting mplayer"); return -1; } } else { sink = stdout; } writeBuffer = (unsigned char *)malloc(vi.RowSize() * vi.height); if (!writeBuffer) { AVXLOG_ERROR("%s", "Unable to allocate memory"); goto fail; } try { for (int i = 0; i < vi.num_frames; ++i) { if (launchMPlayer && (feof(sink) || ferror(sink))) { AVXLOG_ERROR("%s", "mplayer process exited"); break; } PVideoFrame frame = clip->GetFrame(i, avx_library.env); if (vi.IsPlanar()) { // Check plane count in 2.6. int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U}; for (int j = 0; j < 3; ++j) { int plane = planes[j]; int src_pitch = frame->GetPitch(plane); int row_size = frame->GetRowSize(plane); int height = frame->GetHeight(plane); const unsigned char *srcp = frame->GetReadPtr(plane); avx_library.env->BitBlt(writeBuffer, row_size, srcp, src_pitch, row_size, height); fwrite(writeBuffer, 1, row_size * height, sink); } } else { int src_pitch = frame->GetPitch(); int row_size = frame->GetRowSize(); int height = frame->GetHeight(); const unsigned char *srcp = frame->GetReadPtr(); avx_library.env->BitBlt(writeBuffer, row_size, srcp, src_pitch, row_size, height); fwrite(writeBuffer, 1, row_size * height, sink); } } } catch (AvisynthError &e) { AVXLOG_ERROR("AvisynthError: %s", e.msg); goto fail; } free(writeBuffer); if (launchMPlayer) pclose(sink); signal(SIGPIPE, old_sigpipe); return 0; fail: if (writeBuffer) free(writeBuffer); if (launchMPlayer) pclose(sink); signal(SIGPIPE, old_sigpipe); return -1; };
int main(int argc, TCHAR* argv[]) { SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); printf("Usage: filmtester <avs filename> [duplicates_maxlength=2]\n"); printf("The program plays the AVS file and tests for frame duplicates\n\n"); int duplicates_maxlength = 2; if (argc < 2) { printf("No filename specified.\n\n"); return -1; } if (argc > 2) { duplicates_maxlength = _ttoi(argv[2]); printf("INFO: duplicates_maxlength set to %d\n", duplicates_maxlength); } IScriptEnvironment *env = CreateScriptEnvironment(); _tprintf(_T("Loading \"%s\" ...\n"), argv[1]); LPCSTR arg_names[1] = { nullptr }; AVSValue arg_vals[1] = { (LPCSTR)argv[1] }; clip = env->Invoke("import", AVSValue(arg_vals,1), arg_names).AsClip(); printf("AVS file loaded successfully.\n\n"); VideoInfo vi = clip->GetVideoInfo(); printf("VideoInfo:\n"); printf("-----------\n"); if (vi.HasVideo()) { printf("width x height: %dx%d\n", vi.width, vi.height); printf("num_frames: %d\n", vi.num_frames); printf("fps: %d/%d\n", vi.fps_numerator, vi.fps_denominator); std::string colorspace; if (vi.pixel_type & VideoInfo::CS_BGR) colorspace += "BGR, "; if (vi.pixel_type & VideoInfo::CS_YUV) colorspace += "YUV, "; if (vi.pixel_type & VideoInfo::CS_INTERLEAVED) colorspace += "INTERLEAVED, "; if (vi.pixel_type & VideoInfo::CS_PLANAR) colorspace += "PLANAR, "; if (colorspace.length() > 0) colorspace.erase(colorspace.length()-2); printf("colorspace: %s\n", colorspace.c_str()); std::string colorformat; if (vi.pixel_type & VideoInfo::CS_BGR24) colorformat += "BGR24, "; if (vi.pixel_type & VideoInfo::CS_BGR32) colorformat += "BGR32, "; if (vi.pixel_type & VideoInfo::CS_YUY2) colorformat += "YUY2, "; if (vi.pixel_type & VideoInfo::CS_YV12) colorformat += "YV12, "; if (vi.pixel_type & VideoInfo::CS_I420) colorformat += "I420 (IYUV), "; if (colorformat.length() > 0) colorformat.erase(colorformat.length()-2); else colorformat = "UNKNOWN"; printf("colorformat: %s\n", colorformat.c_str()); std::string imagetype; if (vi.image_type & VideoInfo::IT_BFF) imagetype += "BFF, "; if (vi.image_type & VideoInfo::IT_TFF) imagetype += "TFF, "; if (vi.image_type & VideoInfo::IT_FIELDBASED) imagetype += "FIELDBASED, "; if (imagetype.length() > 0) imagetype.erase(imagetype.length()-2); else imagetype = "UNKNOWN"; printf("image_type: %s\n", imagetype.c_str()); printf("bits per pixel: %d\n", vi.BitsPerPixel()); } else printf("NO VIDEO\n"); if (vi.HasAudio()) { printf("audio channels: %d\n", vi.nchannels); printf("sample_type: %x\n", vi.sample_type); printf("samples per second: %d\n", vi.audio_samples_per_second); printf("bytes per channel sample: %d\n", vi.BytesPerChannelSample()); printf("bytes per audio sample: %d\n", vi.BytesPerAudioSample()); printf("num_audio_samples: %lld\n", vi.num_audio_samples); } else printf("NO AUDIO\n"); printf("-----------\n\n"); if (!vi.HasVideo()) { printf("Can't start video playback for the sequence without video.\n\n"); return -1; } printf("Starting playback ...\n"); prev_frame = clip->GetFrame(0, env); int framesize = prev_frame->GetFrameBuffer()->GetDataSize(); printf("INFO: framesize = %d bytes.\n\n", framesize); InitializeCriticalSection(&cs); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); int error_count = 0; int dup_start_frame = 0; bool flag_dup = false; std::vector<std::pair<int, int>> duplicates; for(int i=1; i<vi.num_frames; ++i) { EnterCriticalSection(&cs); dst = clip->GetFrame(i, env); const BYTE *src_ptr = prev_frame->GetFrameBuffer()->GetReadPtr(); const BYTE *dst_ptr = dst->GetFrameBuffer()->GetReadPtr(); if (!memcmp(src_ptr, dst_ptr, framesize)) { if (!flag_dup) { flag_dup = true; dup_start_frame = i-1; } } else if (flag_dup) { int length = (i-1) - dup_start_frame; if (length >= duplicates_maxlength) { printf("\rfilmtester: duplication interval: %d..%d" SPACES "\n", dup_start_frame, i-1); duplicates.push_back(std::make_pair(dup_start_frame, i-1)); error_count++; } flag_dup = false; } prev_frame = dst; LeaveCriticalSection(&cs); printf("\r[%5.1f%%] [%d errors] %d/%d frame processing", (float)((i+1)*100)/vi.num_frames, error_count, i+1, vi.num_frames); } EnterCriticalSection(&cs); if (flag_dup) { int i = vi.num_frames; int length = (i-1) - dup_start_frame; if (length >= duplicates_maxlength) { printf("\rfilmtester: duplication interval: %d..%d" SPACES "\n", dup_start_frame, i-1); duplicates.push_back(std::make_pair(dup_start_frame, i-1)); error_count++; } flag_dup = false; } printf("\rProcessing completed." SPACES "\n\n"); printf("%d errors\n", error_count); printf("\n"); if (error_count > 0) { printf("Erroneous intervals (%d):\n", duplicates.size()); for(auto it = duplicates.begin(); it != duplicates.end(); ++it) printf("%5d .. %d\n", it->first, it->second); printf("\n"); } dst = nullptr; prev_frame = nullptr; clip = nullptr; LeaveCriticalSection(&cs); DeleteCriticalSection(&cs); return error_count; }