Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Video thumbnai 2

Status
Not open for further replies.

tedsmith

Programmer
Nov 23, 2000
1,762
AU
How would I implement GDI+ (or other method) to make a separate thumbnail of the first frame of an AVI or mpg4 video file? (If this is possible)
Does this put the frame into an image or picture box or create a picture of it's own making?
I need each thumbnail as a separate small piece of data rather than just a shrunk version of the original on a screen.
I want to send say 5 thumbnails showing on a page with a different layout to a remote computer on a LAN so the thumbnails need to be in effect 5 separate small BMPs or some other form that can be shown on a remote image box rather than only create them on the screen.
 
On second thoughts I would rather have it generate a separate small file containing an thumbnail image of the first movie frame.
This file would be always set to the remote workstation with the original movie file so when the remote user was faced with the choice of selecting a movie they would automatically see this thumbnail file instead of the movie. Eg MyMovie.avi would also have MyMovie.tbb in the same folder. This would save the remote app to have thumbnail generating capability every time it was used.
(a bit like the thumbs.db file for Windows Explorer but in a separate tiny file for each movie)

Like in the Adobe video editor DVD menu generator, it would also be nice to be able to select a frame in from the start so that movies that have a fade up from black don't just show a black thumbnail.

Hoping this is not too difficult for vb6!
 
I tried feeding the avi to a picbox1 using mcisendstring
then transferring it to another picbox2 with BltBit but the picture box has to be the same size as the video (autosize doesn't work) so I cant use it on different size videos. (using GDI32)
then shrinking using paintpicture but it seems I only get the top corner of the pic the same size.
Once I execute Picture2.picture=Picture1.picture once, MCIsendstring curiously keeps sending the right hand half of the pic to the second box box even though I have no code running to do this and seems to overrides paintpicture even when the movie is paused.
 
>BltBit

Try StretchBlt instead (which we have previously mentioned to you)
 
I've had some luck playing around with this. I just took the... "easy road" [wink] of retrieving thumbnails via Shell Automation objects.

Video thumbnails are accessible as one of the ExtendedProperty values of ShellFolderItem objects. The tough part is that these are returned as PROPVARIANT values, and in particular the SCID_THUMBNAILSTREAM (an argument you pass to ExtendedProperty) returns one with a VT_STREAM making it a bit troublesome in that VB6 cannot convert it to a standard Variant.

Code:
'Based on Windows SDK header file propkey.h:
Private Const SCID_THUMBNAILSTREAM As String = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9},27"

That can be done by calling PropVariantToVariant() and that gives you a Variant IStream object as an IUnknown.

This particular stream is designed for consumption by GDI+/WindowsCodecs, and isn't a simple PNG or JPEG in a stream. But GdipCreateBitmapFromStream() can read it just fine, and from there you are on your way.


The first issue is that this isn't speedy. I'm not sure what Shell32 is going through to get thethumbnails even though I had assumed they were retrieved using typical OLE Storage & Streams mechanisms. It almost seems too slow for that though.

The second issue is that depending on the video encoding format and codecs you have installed you might get nice results or a junk image back. I was almost ready to give it up until I realized I had VB6.exe in ffdshow's blacklist on my development PC. But testing the compiled EXE and allowing ffdshow use returned good images from everything but some DIVX formats.


I don't see a problem with quality, given the proper codec support. There also doesn't seem to be any problem with all-black thumbnails even with videos that have quite a few frames of black lead-in. Of course manually choosing a frame to capture as a thumbnail does have advantages... as long as somebody wants to sit through that process.
 
Thanks dil** - I'd have to think long about that one!
Can you point me to a simple code example of how to put that all together?

Can you explain why when I have two picture boxes that are half the size of the original movie, when I use MCIsendstring to play to the first box (and get the top left quarter of the picture, then execute Picture2.picture=Picture1.picture once, I get the right half of the movie showing in the second box even when they are separated?
If I halt the app in the IDE, the video continues to run the way Windows Media Player control does.
But why does the right half of the movie keep running and not a copy of the left half in the first box?
 
I couldn't find a single example anywhere on the Web. The closest I came were some .Net code fragments where the made use of the Framework's hobo-handouts and thus didn't need to deal with the gory details of how things really work. That of course made even those snippets useless to a VB6 programmer, even if they had (a.) actually worked and (b.) been complete. All that I saw were non-working code accompanied by unanswered pleas for help.

I sometimes wonder if the world wouldn't be at the Star Trek transporter and food replicator level of technology now if Microsoft hadn't killed VB and diverted their resources into .Net! [wink]


Sounds like the old Windows Multimedia renders into a Device Context and your PictureBox controls end up sharing the same one, possibly due in part to their AutoRedraw settings. Since I don't understand what's going on there I guess I'm not sure why one stops and the other continues once you have "halted" the program (IDE Pause button?).
 
What I meant is that the two half size boxes continue to show the moving video.
The left hand box shows the cropped left side of the video with the right hand box showing the right hand half of the video. Since I initially set the second box .picture = first box .picture, I thought they should show the same picture!
When I use MCIsendstring to pause the video, both boxes pause.
Looks like MCISendstring overrides any attempt to use GDI
 
Well I had the idea that underneath it wasn't using GDI at all and just renders into the window, so that would make some sense.
 
Another holy mystery?

I was able to use StretchBlt to create a thumbnail of a graphic but no luck with a video frame showing in a Picturebox (using MCISendstring to fill the picturebox)
so I came to the conclusion that I didn't really need a reduced size thumbnail but rather a graphic with the same pixels as the first video frame frame. I can show it in a reduced size imagebox in the final ap.

Even though GetFormat shows the 'picture" of the video to be a Bitmap, MCISendString really seems to send the picture to the screen underneath because if you set the initial picturebox smaller than the video frame so the frame is cropped then set another Picturebox or Imagebox to the first picturebox.picture and place them where the remaining parts of the picture would be, they fill in the missing parts of the picture exactly in the right place they would have been if the first box was big enough. It looks like you are looking through an old fashioned multi-pane glass window at the video running underneath!

Playing the movie shows all a moving picture in all three windows without any code updating the boxes pictures. In this case VN6 controls seem only used to reveal an underlying picture of a movie and not contain a picture of their own as intuition would suggest. GetFormat shows the picture to be a bitmap or a device independent bitmap.

Therefore Savepicture of this frame from any of the picture boxes or clipboard or form with a movie does not work giving only a white frame. I also cant get the video picture to feed to StretchBlt either if I wanted to reduce it.

Does this mean that the picture is not really in the box and the boxes are only revealing what is underneath? I understand MCISendString is using DirectDraw.

So how do I save this underlying "picture" or feed it to StretchBlt?
This would solve my problem.
This is the simple code without StretchBlt that I used with 6 command buttons and a picturebox

Code:
Option Explicit
Private Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
Dim filename As String
 
Private Sub LOADIT_Click()
CommonDialog1.ShowOpen
filename = Chr(34) & CommonDialog1.filename & Chr(34)
End Sub

Private Sub OPENIT_Click()
mciSendString "close avi", 0, 0, 0
filename = "c:\BusStation\Messages\Test.avi"
mciSendString "open " & filename & " type mpegvideo alias avi parent " & Picture1.hWnd & " style child", 0, 0, 0
End Sub

Private Sub PAUSEIT_Click()
mciSendString "pause AVI", 0, 0, 0
End Sub

Private Sub PLAYIT_Click()
mciSendString "play avi", 0, 0, 0
End Sub

Private Sub SAVEIT_Click()
SavePicture Picture1.Image, Left(filename, (Len(filename) - 3)) & "bmp"
[COLOR=#EF2929]'****** This doesn't work
'Saves a blank picture instead[/color]

End Sub

Private Sub STOPIT_Click()
mciSendString "stop AVI", 0, 0, 0
End Sub
 
Does this mean that the picture is not really in the box and the boxes are only revealing what is underneath? I understand MCISendString is using DirectDraw.
I think it is rendering into a private child window added to the PictureBox

So how do I save this underlying "picture" or feed it to StretchBlt?
No idea. Not sure you can.

I think normally you would grab a frame using a capture filter T'ed with the preview graph.
 
I don't think you can capture any video frame in that way if hardware acceleration is enabled.

If you are looking at creating thumbnail files, then you can use ffmpeg to extract any frame you want with a simple shell command:

ffmpeg.exe -i "D:\video file.flv" -an -ss 00:00:00 -an -r 1 -vframes 1 -y -s 320x240 "D:\video file thumbnail.bmp"

Most of it is straight forward. The 00:00:00 id hour:minute:second of where to grab from, 320x240 is the output image size.
You can grab to bmp, jpg or png.

When I do this kind of thing, I usually write the command out to a temp bat file, then run it using the VB Shell command and the OpenProcess, WaitForSingleObject and CloseHandle API's.

Heaven doesn't want me, and Hell's afraid I'll take over!
 
I'm not sure that works for him either.

He seems to be creating a full-time job for a thumnail monkey to sit and have throusands of videos played at him each day, batting at a "feed me" lever when some desired image appears.
 
Thanks.
I will try ffmpeg.exe.

Sorry I didn't explain the reason for the thumbnail which is much more simple that the "monkey" concept!

I envisage a public advertising info display (like in a shop window) that the shop owner can select from a menu of 15 prearranged messages. These messages are pre-stored as files. Any message can be Custom richtext, SWF, JPG or a short movie file.

I want to provide a screen with fifteen 1/6 size thumbnail of each message to make it easier to select the message they want to use. They just click on the thumbnail to show it on the external screen.

I can easily generate a thumbnail from the full size files for all the other types except Movies. WindowsMediaPlayer only allows 1 thumbnail at a time.

That's why I want the movie thumbnails in an imagebox or picturebox.

Hopefully ffmpeg.exe will be the answer, creating and storing the movie thumbnails as BMP files when the original is imported.
 
But the trick is knowing how far "in" to grab a frame, isn't it? So somebody will need to view each video and decide where to do a capture.

Otherwise you could just use the ones that the Windows Shell will fetch for you.
 
<ahem> .. Given the now slightly clarified requirements, I think you'll find it is all possible with a picturebox and mciSendString ... here's some example code (which requires a form with two picture boxes and a command button called Main):

Code:
[blue]Option Explicit

Private Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long

Private Const SRCCOPY = &HCC0020

Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Private Boundingbox As RECT
 
Private Sub Main_Click()
    ThumbnailFrame 60, "c:\downloads\video1.avi", "C:\Downloads\test.bmp" [green]' By default MCI is working in frames rather than time[/green]
End Sub

Private Sub ThumbnailFrame(frame As Long, strVideoFile As String, strThumbname As String)
    GetVideo strVideoFile
    MoveToFrame frame [green]' actually, this will be nearest keyframe under default settings [/green]
    ThumbNail strThumbname
End Sub

Private Sub GetVideo(strFilename As String)
    
mciSendString "close avi", 0, 0, 0
mciSendString "open " & strFilename & " type mpegvideo alias avi parent " & Picture1.hwnd & " style child", 0, 0, 0
mciSendString "put avi client at " & RECTtoStr(Boundingbox), 0, 0, 0 ' Scale it

End Sub

Private Sub MoveToFrame(frame As Long)
    mciSendString "play avi from " & frame & " to " & frame, 0, 0, 0
End Sub

Private Sub ThumbNail(strFilename)
    Dim framehwnd As Long
   
    Dim result As String
    Dim chars As Long
    result = Space(255)
    chars = 255
    
    mciSendString "status avi window handle", result, chars, 0& [green]' get handle to window displaying frame buffer[/green]
    framehwnd = Val(result)
    
    If framehwnd Then
        With Boundingbox
            BitBlt Picture2.hdc, .Top, .Left, .Right, .Bottom, GetDC(framehwnd), 0, 0, SRCCOPY [green]' since we resized the input we do not need stretchblt[/green]
        End With
    End If
    
    SavePicture Picture2.Image, strFilename
End Sub

Private Sub Form_Load()
    ' Some simple initialisation
    'filename = "C:\Downloads\video1.avi"
    With Boundingbox
        .Left = 0
        .Top = 0
        .Right = 100
        .Bottom = 100
    End With
    Picture2.BorderStyle = 0
    Picture2.Move Picture1.Left + Picture1.Width, Picture1.Top, 100 * Screen.TwipsPerPixelX, 100 * Screen.TwipsPerPixelY
End Sub

Private Function RECTtoStr(srcRect As RECT) As String
    RECTtoStr = srcRect.Left & " " & srcRect.Top & " " & srcRect.Right & " " & srcRect.Bottom
End Function[/blue]
 
But once again you need to know what frame you want. Just hard-coding a guess wouldn't be much better than Shell's guess.

However this would be another way to avoid running a 3rd party utility.
 
I tested strongm's code with the following results:

1. You need to set PictureBox2.AutoRedraw = True (otherwise I get a blank bitmap in the default ButtonFace color.)
2. It only works with hardware acceleration disabled (otherwise I get a blank bitmap that's all black.)


Heaven doesn't want me, and Hell's afraid I'll take over!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top