asymmetric
asked on
Delphi, DSPack, Directshow, and streaming video Part 2
Part 1 of this is here : https://www.experts-exchange.com/questions/20604284/Delphi-DSPack-Directshow-and-streaming-video-Part-1.html
I suggest you read that question even if you can't answer it, as it provides some background.
Like Part 1, this question is worth 1000 points. I forgot after posting a question that I could up the points, so I'll try to up them to 1000 after this. If that doesn't work, I'll post another 500 point question for the winner when this is over.
To cover the basics, I'm using Delphi 6 and DSPack in a webcam app and want to add video streaming capabilities with TASFWriter; I am open to using something other than TASFWriter if it integrates nicely with what I already have, that will be Part 3.
Question 1 asks how I can disable some of the capabilities of TASFWriter.
Question 2:
I've had no luck coming up with my own profiles for streaming. Nothing I seem to do affects the output over the stream in regards to bitrate or resolution. I know I'm capturing at the proper resolution, as the bitmaps I capture from the samplegrabber are the correct size. The stream though is always some 1/4 size or smaller, at 32kbit/s. I've tried adding a new filter and selecting one of of the existing codecs on my system such as DivX or MPEG, and while they work and generate a compressed stream, it's still the same low bitrate. To answer this question, I'd like to see a working code example of custom profile creation and loading using the ConfigFilterUsingProfile method of the IConfigASFWriter interface of the TASFWriter component, which is available via it's QueryInterface method.
To satisfy my curiosity that this is working properly, the output resolution should (at best) match the resolution of the current device, which I have in my code as a TFilter (DSPack) called "fPreview", and provide a stream bitrate of 384Kbit/s. The stream should be "video only" as I don't want the audio streamed.
I don't have a particular need for those numbers, but the default profiles described by dspack for video only are low-bandwidth only, 28.8 and 56k respectively, and only the 56k one seems to work when assigning a new value to the TASFWriter profile property. I don't want to load a predefined profile, I want to create my own. I have the code implemented to load and edit the profile and I'm pretty sure that much is working properly, It just seems to have no effect when I do : ConfigAsfWriter.ConfigureF ilterUsing Profile(Pr ofile);
ConfigAsfWriter is an IConfigAsfWriter interface, acquired via the QueryInterface method of the TASFWriter component. Profile is a IWMProfile interface. A short list of the steps I'm performing here:
1) Call WMCreateProfileManager to get an IWMProfileManager.
2) Call ProfileManager.CreateEmpty Profile(WM T_VER_7_0, Profile) to setup the IWMProfile.
3) Call Profile.CreateNewStream(WM MEDIATYPE_ VIDEO, stream). Stream is an IWMStreamConfig.
At this point I've messed with setting Stream.Bitrate, as well as creating an IWMMediaProps interface and changing the resolution, all to no avail, usually generating an exception.
Thanks for your answers EE people, I know you'll come through for me again. ;)
I suggest you read that question even if you can't answer it, as it provides some background.
Like Part 1, this question is worth 1000 points. I forgot after posting a question that I could up the points, so I'll try to up them to 1000 after this. If that doesn't work, I'll post another 500 point question for the winner when this is over.
To cover the basics, I'm using Delphi 6 and DSPack in a webcam app and want to add video streaming capabilities with TASFWriter; I am open to using something other than TASFWriter if it integrates nicely with what I already have, that will be Part 3.
Question 1 asks how I can disable some of the capabilities of TASFWriter.
Question 2:
I've had no luck coming up with my own profiles for streaming. Nothing I seem to do affects the output over the stream in regards to bitrate or resolution. I know I'm capturing at the proper resolution, as the bitmaps I capture from the samplegrabber are the correct size. The stream though is always some 1/4 size or smaller, at 32kbit/s. I've tried adding a new filter and selecting one of of the existing codecs on my system such as DivX or MPEG, and while they work and generate a compressed stream, it's still the same low bitrate. To answer this question, I'd like to see a working code example of custom profile creation and loading using the ConfigFilterUsingProfile method of the IConfigASFWriter interface of the TASFWriter component, which is available via it's QueryInterface method.
To satisfy my curiosity that this is working properly, the output resolution should (at best) match the resolution of the current device, which I have in my code as a TFilter (DSPack) called "fPreview", and provide a stream bitrate of 384Kbit/s. The stream should be "video only" as I don't want the audio streamed.
I don't have a particular need for those numbers, but the default profiles described by dspack for video only are low-bandwidth only, 28.8 and 56k respectively, and only the 56k one seems to work when assigning a new value to the TASFWriter profile property. I don't want to load a predefined profile, I want to create my own. I have the code implemented to load and edit the profile and I'm pretty sure that much is working properly, It just seems to have no effect when I do : ConfigAsfWriter.ConfigureF
ConfigAsfWriter is an IConfigAsfWriter interface, acquired via the QueryInterface method of the TASFWriter component. Profile is a IWMProfile interface. A short list of the steps I'm performing here:
1) Call WMCreateProfileManager to get an IWMProfileManager.
2) Call ProfileManager.CreateEmpty
3) Call Profile.CreateNewStream(WM
At this point I've messed with setting Stream.Bitrate, as well as creating an IWMMediaProps interface and changing the resolution, all to no avail, usually generating an exception.
Thanks for your answers EE people, I know you'll come through for me again. ;)
ASKER
Sure.. I can do that... I'll post it tonight or tomorrow. Had that bit of ATX connecter melting fun, have yet to reinstall SourceSafe so I can't get at my code history which has the code I was using in it.
Sorry for te repeated question just know but i have a problem where i am not receiving any emails from xpertxchange on anything...
Dj
Dj
ASKER
Ok, below is the profile I was attempting to load when the user clicked on the "enable stream" button.
I went through a couple of attempts at different things, here is one of them..
//------------------------ ---------- ----
procedure SomeEvent(Sender : TObject);
var
ConfigAsfWriter : IConfigAsfWriter;
ProfileManager : IWMProfileManager;
Profile : IWMProfile;
Stream : IWMStreamConfig;
MediaProps : IWMMediaProps;
pmt : PWMMediaType;
dw : dword;
vih : PVideoInfoHeader;
hr : hresult;
begin
if Succeeded(WMCreateProfileM anager(Pro fileManage r)) then
begin
if Succeeded(ProfileManager.C reateEmpty Profile(WM T_VER_7_0, Profile)) then
begin
Profile.SetName('SC2 Profile (Low)');
Profile.SetDescription('SC 2 Low Bandwidth Profile -- 64kbit/s');
if Succeeded(Profile.CreateNe wStream(WM MEDIATYPE_ Video, Stream)) then
begin
dw := 64 * 1024;
Stream.SetBitrate(dw);
dw := 1000;
Stream.SetBufferWindow(dw) ;
if Succeeded(Stream.QueryInte rface(IID_ IWMMediaPr ops, MediaProps)) then
begin
MediaProps.GetMediaType(ni l, dw);
getmem(pmt, dw);
if Succeeded(MediaProps.GetMe diaType(pm t, dw)) then
begin
pmt^.majortype := WMMEDIATYPE_Video;
pmt^.subtype := WMMEDIASUBTYPE_WMV1;
pmt^.bFixedSizeSamples := false;
pmt^.bTemporalCompression := true;
pmt^.pUnk := nil;
if pmt^.formattype.D1 = $05589f80 then
begin
vih := PVideoInfoHeader(pmt^.pbFo rmat);
vih^.rcSource.Left := 0;
vih^.rcSource.Right := 640;
vih^.rcSource.Top := 0;
vih^.rcSource.Bottom := 480;
vih^.rcTarget.Left := 0;
vih^.rcTarget.Right := 640;
vih^.rcTarget.Top := 0;
vih^.rcTarget.Bottom := 480;
MediaProps.SetMediaType(pm t);
end;
end;
freemem(pmt, dw);
end;
Profile.AddStream(Stream);
end;
end;
end;
if Succeeded(awStream.QueryIn terface(IC onfigAsfWr iter, ConfigAsfWriter)) then
begin
ConfigAsfWriter.ConfigureF ilterUsing Profile(Pr ofile);
ConfigAsfWriter := nil;
end;
end;
I went through a couple of attempts at different things, here is one of them..
//------------------------
procedure SomeEvent(Sender : TObject);
var
ConfigAsfWriter : IConfigAsfWriter;
ProfileManager : IWMProfileManager;
Profile : IWMProfile;
Stream : IWMStreamConfig;
MediaProps : IWMMediaProps;
pmt : PWMMediaType;
dw : dword;
vih : PVideoInfoHeader;
hr : hresult;
begin
if Succeeded(WMCreateProfileM
begin
if Succeeded(ProfileManager.C
begin
Profile.SetName('SC2 Profile (Low)');
Profile.SetDescription('SC
if Succeeded(Profile.CreateNe
begin
dw := 64 * 1024;
Stream.SetBitrate(dw);
dw := 1000;
Stream.SetBufferWindow(dw)
if Succeeded(Stream.QueryInte
begin
MediaProps.GetMediaType(ni
getmem(pmt, dw);
if Succeeded(MediaProps.GetMe
begin
pmt^.majortype := WMMEDIATYPE_Video;
pmt^.subtype := WMMEDIASUBTYPE_WMV1;
pmt^.bFixedSizeSamples := false;
pmt^.bTemporalCompression := true;
pmt^.pUnk := nil;
if pmt^.formattype.D1 = $05589f80 then
begin
vih := PVideoInfoHeader(pmt^.pbFo
vih^.rcSource.Left := 0;
vih^.rcSource.Right := 640;
vih^.rcSource.Top := 0;
vih^.rcSource.Bottom := 480;
vih^.rcTarget.Left := 0;
vih^.rcTarget.Right := 640;
vih^.rcTarget.Top := 0;
vih^.rcTarget.Bottom := 480;
MediaProps.SetMediaType(pm
end;
end;
freemem(pmt, dw);
end;
Profile.AddStream(Stream);
end;
end;
end;
if Succeeded(awStream.QueryIn
begin
ConfigAsfWriter.ConfigureF
ConfigAsfWriter := nil;
end;
end;
ASKER
I've tried another tack, same result so far, but maybe this is another direction to go in. I enumerated via a TSysDevEnum all of the video compression codecs and filled a combobox with them; it's basically the same as getting the device list, code can be seen in the "Compressor" demo with dspack.
Then I have the following code:
//-------------
procedure TfrmPreviewWindow.tbStream _EnableCli ck(Sender: TObject);
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
if frmSettings_Stream.ShowMod al = mrOK then
begin
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + self.Caption + '.asf';
awStream.FilterGraph := fgPreview;
fCompressor.BaseFilter.Mon iker := frmMain.sdeVCompress.GetMo niker(frmS ettings_St ream.cbVid eoCodec.It emIndex);
fCompressor.FilterGraph := fgPreview;
with fgPreview as ICaptureGraphBuilder2 do
begin
// RenderStream(@PIN_CATEGORY _CAPTURE, @WMMEDIATYPE_Video, fPreview as IBaseFilter, nil, awStream as IBaseFilter);
RenderStream(@PIN_CATEGORY _CAPTURE, @WMMEDIATYPE_Video, fPreview as IBaseFilter, fCompressor as IBaseFilter, awStream as IBaseFilter);
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
ServiceProvider := nil;
WriterAdvanced2.GetSink(0, DeletedSink);
WriterAdvanced2.RemoveSink (DeletedSi nk);
end;
end;
fgPreview.Play;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter s;
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
ConnectToDevice;
end;
end;
//-------------
This results in a graph that looks like this:
http://asym.macroshell.com/doom/graphedit.jpg
You can see the problem.. an "avi decompressor" is automatically inserted after the compression codec "fCompressor" that I created in the code above. So now that's my boggle, because I think this will work ok if I can just get rid of that Decompressor.
Maybe something along the lines of your filesink code riet.. removing that thing, then connecting the dangling pins?
Then I have the following code:
//-------------
procedure TfrmPreviewWindow.tbStream
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
if frmSettings_Stream.ShowMod
begin
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + self.Caption + '.asf';
awStream.FilterGraph := fgPreview;
fCompressor.BaseFilter.Mon
fCompressor.FilterGraph := fgPreview;
with fgPreview as ICaptureGraphBuilder2 do
begin
// RenderStream(@PIN_CATEGORY
RenderStream(@PIN_CATEGORY
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
end;
end;
fgPreview.Play;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
ConnectToDevice;
end;
end;
//-------------
This results in a graph that looks like this:
http://asym.macroshell.com/doom/graphedit.jpg
You can see the problem.. an "avi decompressor" is automatically inserted after the compression codec "fCompressor" that I created in the code above. So now that's my boggle, because I think this will work ok if I can just get rid of that Decompressor.
Maybe something along the lines of your filesink code riet.. removing that thing, then connecting the dangling pins?
ASKER
Just thought I'd say, here's the 3rd (and final) thing I've played with.. no love here either..
I can't think of any other ways to do it though.. ack!
//------------
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
ConfigAsfWriter : IConfigAsfWriter;
Profile : IWMProfile;
Stream : IWMStreamConfig;
lwBitrate : longword;
//.......
if Succeeded(awStream.QueryIn terface(IC onfigAsfWr iter, ConfigAsfWriter)) then
begin
ConfigAsfWriter.GetCurrent Profile(Pr ofile);
Profile.GetStream(0, Stream);
Stream.GetBitrate(lwBitrat e);
lwBitrate := lwBitrate * 8;
Stream.SetBitrate(lwBitrat e);
Profile.ReconfigStream(Str eam);
ConfigAsfWriter.ConfigureF ilterUsing Profile(Pr ofile);
ConfigAsfWriter := nil;
end;
//------------
I can't think of any other ways to do it though.. ack!
//------------
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
ConfigAsfWriter : IConfigAsfWriter;
Profile : IWMProfile;
Stream : IWMStreamConfig;
lwBitrate : longword;
//.......
if Succeeded(awStream.QueryIn
begin
ConfigAsfWriter.GetCurrent
Profile.GetStream(0, Stream);
Stream.GetBitrate(lwBitrat
lwBitrate := lwBitrate * 8;
Stream.SetBitrate(lwBitrat
Profile.ReconfigStream(Str
ConfigAsfWriter.ConfigureF
ConfigAsfWriter := nil;
end;
//------------
ASKER
riet.. not sure if you got the notification emails, so I'm dropping another post here to try and trigger an email send to you.
Just drop something to say you're looking at this (or not) if you get the email. If I don't see any posts a few days after this I'll close the Q.
Just drop something to say you're looking at this (or not) if you get the email. If I don't see any posts a few days after this I'll close the Q.
Am getting the mails now, but haven't had the time to look at it yet, are you still needing an answer or have you found another solution to your problem?
ASKER
I'm constantly working on the thing, and I'm persuing other routes, but I'd still like to get this working if I can.
My initial plan was to release a version with working streaming (using this ASFWriter), if I can get this part worked out.. that would give me some "breathing room" to concentrate more on the "Part 3" and eventually ditch the WMP dependant parts.
I think what I'm going to end up doing at this point is messing with your solution to Part 3 to see how it works out, and award the points there if it works since I did say an ActiveX control is ok. Then I'll post another part in the Java area or something once I work out the server-side details for the new method myself.
In the end this thing is going to have to work without ActiveX.. the server/app already has too much dependency built in, such as requiring DX9 and WMP9 to run. I don't intend to keep those requirements on the client side in the long run; plenty of people don't upgrade things like that as a habit, and even if they did it would still rule out other platforms from even being able to view the stream.
So yes, I still need an answer here. :) It's proving quite a pain in my rear, I think I'm getting close, but I'm not sure.
I think 2nd code snippet I posted above just isn't going to work, period. The ASFWriter wants the stream in a certain format, and the filtergraph will insert filters as required to get it inline with that. The key is in changing the filtergraph properties themselves.
The 1st snippet is the one closest to correct, although I've found out from the SDK that the bitrate in the IWMStreamConfig has to match a bitrate you provide elsewhere in the VideoInfo area and/or the bmiHeader, I don't exactly recall.
The 3rd snippet is along the same lines as the 1st, just a cheap attempt to change the bitrate.. but there is more than one place you set it I'm seeing, so that little bit doesn't work.
Putting a bunch of "hr := " (HRESULT) in front of my calls and stepping through with optimizations off points to those that fail, usually at the very end, telling me useful information like "the stream is invalid" -- no help at all. ;)
I was experimenting tonight with just capturing jpegs really fast (ala MJPEG compression) and sending them over the network to a test client. That works reasonably well, but even with mediocre resolution such as 360x240, the JPEGs are about 12KB in size.. adding up to 180KB/s for a stream.. too much data for anything but a LAN.. or a single viewer on a T1.
Anyway, Going with what I have as a base, feel free to try and work with any of the snippets I've provided above, or take your own tack. The base code I'm working with, without any attempt at streaming is this. I've noted the points that I *think* the code should be placed. :
//------------------------ --------
procedure TfrmPreviewWindow.tbStream _EnableCli ck(Sender: TObject);
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
//if frmSettings_Stream.ShowMod al = mrOK then
begin
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.FilterGraph := fgPreview;
//----------- insert code here -------
with fgPreview as ICaptureGraphBuilder2 do
begin
RenderStream(@PIN_CATEGORY _CAPTURE, @WMMEDIATYPE_Video, fPreview as IBaseFilter, nil, awStream as IBaseFilter);
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
ServiceProvider := nil;
WriterAdvanced2.GetSink(0, DeletedSink);
WriterAdvanced2.RemoveSink (DeletedSi nk);
end;
end;
//----------- and/or here -------
fgPreview.Play;
//----------- and/or here -------
AddPropertyItemsToMenu;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter s;
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
DeleteFile(sAppDir + inttostr(self.Handle) + '.asf');
ConnectToDevice;
end;
end;
//------------------------ --------
My initial plan was to release a version with working streaming (using this ASFWriter), if I can get this part worked out.. that would give me some "breathing room" to concentrate more on the "Part 3" and eventually ditch the WMP dependant parts.
I think what I'm going to end up doing at this point is messing with your solution to Part 3 to see how it works out, and award the points there if it works since I did say an ActiveX control is ok. Then I'll post another part in the Java area or something once I work out the server-side details for the new method myself.
In the end this thing is going to have to work without ActiveX.. the server/app already has too much dependency built in, such as requiring DX9 and WMP9 to run. I don't intend to keep those requirements on the client side in the long run; plenty of people don't upgrade things like that as a habit, and even if they did it would still rule out other platforms from even being able to view the stream.
So yes, I still need an answer here. :) It's proving quite a pain in my rear, I think I'm getting close, but I'm not sure.
I think 2nd code snippet I posted above just isn't going to work, period. The ASFWriter wants the stream in a certain format, and the filtergraph will insert filters as required to get it inline with that. The key is in changing the filtergraph properties themselves.
The 1st snippet is the one closest to correct, although I've found out from the SDK that the bitrate in the IWMStreamConfig has to match a bitrate you provide elsewhere in the VideoInfo area and/or the bmiHeader, I don't exactly recall.
The 3rd snippet is along the same lines as the 1st, just a cheap attempt to change the bitrate.. but there is more than one place you set it I'm seeing, so that little bit doesn't work.
Putting a bunch of "hr := " (HRESULT) in front of my calls and stepping through with optimizations off points to those that fail, usually at the very end, telling me useful information like "the stream is invalid" -- no help at all. ;)
I was experimenting tonight with just capturing jpegs really fast (ala MJPEG compression) and sending them over the network to a test client. That works reasonably well, but even with mediocre resolution such as 360x240, the JPEGs are about 12KB in size.. adding up to 180KB/s for a stream.. too much data for anything but a LAN.. or a single viewer on a T1.
Anyway, Going with what I have as a base, feel free to try and work with any of the snippets I've provided above, or take your own tack. The base code I'm working with, without any attempt at streaming is this. I've noted the points that I *think* the code should be placed. :
//------------------------
procedure TfrmPreviewWindow.tbStream
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
//if frmSettings_Stream.ShowMod
begin
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.FilterGraph := fgPreview;
//----------- insert code here -------
with fgPreview as ICaptureGraphBuilder2 do
begin
RenderStream(@PIN_CATEGORY
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
end;
end;
//----------- and/or here -------
fgPreview.Play;
//----------- and/or here -------
AddPropertyItemsToMenu;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
DeleteFile(sAppDir + inttostr(self.Handle) + '.asf');
ConnectToDevice;
end;
end;
//------------------------
ASKER
Oh, one more thing of note. If you need (for the bmiHeader or whatever) the current bitmap settings as I'm getting from the device, there is a samplegrabber connected and running at the start of the event above, which it could be grabbed from.
When this event is called, I already have a running filtergraph (fgPreview) connected to a device (fPreview), going through a samplegrabber (sgPreview), and displaying in a videowindow (vwPreview).
sgPreview.GetBitmap() can give you an image that represents the frames coming from the device if you need information on height/width, color depth, etc, as I think you will to fill out the bmiHeader of the VideoInfo structure properly... such as for MPEG2 compression.
The commented out showmodal line above was opening a form to allow the user to pick from a list of installed video codecs. Since I'm ignoring that for testing and concentrating on getting one codec (any codec!) working, I decided to get rid of that just so the dumb form didn't keep popping up.
If you have any other questions, I'm here.. thanks. ;)
When this event is called, I already have a running filtergraph (fgPreview) connected to a device (fPreview), going through a samplegrabber (sgPreview), and displaying in a videowindow (vwPreview).
sgPreview.GetBitmap() can give you an image that represents the frames coming from the device if you need information on height/width, color depth, etc, as I think you will to fill out the bmiHeader of the VideoInfo structure properly... such as for MPEG2 compression.
The commented out showmodal line above was opening a form to allow the user to pick from a list of installed video codecs. Since I'm ignoring that for testing and concentrating on getting one codec (any codec!) working, I decided to get rid of that just so the dumb form didn't keep popping up.
If you have any other questions, I'm here.. thanks. ;)
The problem with moving to an other solution is that there is always the need for an install, if you were to use Java for example then people will need to have the Java media framework installed, for it to be of any use..
Any other solution would probably be an activex so you have the same dependencies as width WMP.. I would bet more on making the stream of a different format then asf so you might achieve compatability with different players..
Dj
ASKER
Well, I don't know a whole lot of Java, but I was under the impression that all I'd have to do would be make the class libraries available on the (internal to my app) webserver, and the browser would download them as needed to run the applet..
ActiveX has similar problems in that 1) if the control isn't signed, the default action is to ignore it and not notify the user, and 2) if the control is signed, but not by a "well known" issuing authority such as verisign, there will still be security warnings in the browser.
Signing the control at all would prove rather difficult since the signature must match the domain name to avoid even more warnings, and I won't know until runtime just what the domain name the app is running on is.. and perhaps not even then, if reverse dns isn't working or I get "tricked" by netbios/hosts file naming.
The only way I can see around this is to do it the same way MS does, which is to put the activex control on my own server, and everyone gets it from there regardless of where the clients/servers are. That's definitely a route to consider, but not one that's currently open to me.
The java solution on the other hand, I thought would "just work" so long as the server had all the redistributable files available.. I didn't think that I'd need the clientside users to actively install anything. That's the way most of the live cameras on the internet that I've seen work anyway; just a java applet that didn't require me to do any more work than sit there while it downloaded.
ActiveX has similar problems in that 1) if the control isn't signed, the default action is to ignore it and not notify the user, and 2) if the control is signed, but not by a "well known" issuing authority such as verisign, there will still be security warnings in the browser.
Signing the control at all would prove rather difficult since the signature must match the domain name to avoid even more warnings, and I won't know until runtime just what the domain name the app is running on is.. and perhaps not even then, if reverse dns isn't working or I get "tricked" by netbios/hosts file naming.
The only way I can see around this is to do it the same way MS does, which is to put the activex control on my own server, and everyone gets it from there regardless of where the clients/servers are. That's definitely a route to consider, but not one that's currently open to me.
The java solution on the other hand, I thought would "just work" so long as the server had all the redistributable files available.. I didn't think that I'd need the clientside users to actively install anything. That's the way most of the live cameras on the internet that I've seen work anyway; just a java applet that didn't require me to do any more work than sit there while it downloaded.
Well you might be right, i mean you should be right because that's how java is supposed to work, but i had a look at the downloads, and all of them specify specific installation files with optimization packs for different platforms.. The samples on the web site don't just work they require you to have the framework installed.. I'll have a more specific look later on.. i still think creating a standard stream readable by different players is your best bet.. It looks like people have to install something anyway.. and it might as well be a standard player from a renowned company..
ASKER
I'm looking into this as well, but... that's supposed to be for part 3, anyway. ;)
This is the part 2 thread.. dealing with the current issue of getting a dang profile loaded into the ASFPlayer.. I don't suppose you've had any luck in that regard. ;)
I can't seem to do anything to get a nice video-only stream out of the thing.. the two default "video only" profiles do work, but they're horribly downsampled, designed for dialup.
This is the part 2 thread.. dealing with the current issue of getting a dang profile loaded into the ASFPlayer.. I don't suppose you've had any luck in that regard. ;)
I can't seem to do anything to get a nice video-only stream out of the thing.. the two default "video only" profiles do work, but they're horribly downsampled, designed for dialup.
ASKER
hah! got it! :D
procedure TfrmPreviewWindow.SetStrea mProfile;
var
hr : hresult;
ConfigAsfWriter : IConfigAsfWriter;
Profile : IWMProfile;
i : integer;
lwStreamCount : longword;
Stream : IWMStreamConfig;
dwBitrate : longword;
MediaProps : IWMMediaProps;
VMediaProps : IWMVideoMediaProps;
dwMPSize : longword;
PWMT : PWMMediaType;
PVI : PVideoInfo;
begin
if Succeeded(awStream.QueryIn terface(IC onfigAsfWr iter, ConfigAsfWriter)) then
begin
hr := ConfigAsfWriter.GetCurrent Profile(Pr ofile);
hr := Profile.GetStreamCount(lwS treamCount );
for i := 1 to lwStreamCount do
begin
hr := Profile.GetStream(i - 1, Stream);
dwBitrate := 384 * 1024; // 384kbit/s
hr := Stream.SetBitrate(dwBitrat e);
if Succeeded(Stream.QueryInte rface(IWMM ediaProps, MediaProps)) then
begin
hr := MediaProps.GetMediaType(ni l, dwMPSize);
getmem(PWMT, dwMPSize);
hr := MediaProps.GetMediaType(PW MT, dwMPSize);
if PWMT^.formattype.D1 = FORMAT_VideoInfo.D1 then
begin
PVI := PVideoInfo(PWMT^.pbFormat) ;
PVI^.rcSource.Left := 0;
PVI^.rcSource.Top := 0;
PVI^.rcSource.Right := bmpCurrent.Width;
PVI^.rcSource.Bottom := bmpCurrent.Height;
PVI^.rcTarget.Left := 0;
PVI^.rcTarget.Top := 0;
PVI^.rcTarget.Right := bmpCurrent.Width;
PVI^.rcTarget.Bottom := bmpCurrent.Height;
PVI^.dwBitRate := dwBitrate;
PVI^.bmiHeader.biWidth := bmpCurrent.Width;
PVI^.bmiHeader.biHeight := bmpCurrent.Height;
end;
hr := MediaProps.SetMediaType(PW MT);
end;
hr := Profile.ReconfigStream(Str eam);
end;
hr := ConfigAsfWriter.ConfigureF ilterUsing Profile(Pr ofile);
ConfigAsfWriter := nil;
end;
end;
procedure TfrmPreviewWindow.SetStrea
var
hr : hresult;
ConfigAsfWriter : IConfigAsfWriter;
Profile : IWMProfile;
i : integer;
lwStreamCount : longword;
Stream : IWMStreamConfig;
dwBitrate : longword;
MediaProps : IWMMediaProps;
VMediaProps : IWMVideoMediaProps;
dwMPSize : longword;
PWMT : PWMMediaType;
PVI : PVideoInfo;
begin
if Succeeded(awStream.QueryIn
begin
hr := ConfigAsfWriter.GetCurrent
hr := Profile.GetStreamCount(lwS
for i := 1 to lwStreamCount do
begin
hr := Profile.GetStream(i - 1, Stream);
dwBitrate := 384 * 1024; // 384kbit/s
hr := Stream.SetBitrate(dwBitrat
if Succeeded(Stream.QueryInte
begin
hr := MediaProps.GetMediaType(ni
getmem(PWMT, dwMPSize);
hr := MediaProps.GetMediaType(PW
if PWMT^.formattype.D1 = FORMAT_VideoInfo.D1 then
begin
PVI := PVideoInfo(PWMT^.pbFormat)
PVI^.rcSource.Left := 0;
PVI^.rcSource.Top := 0;
PVI^.rcSource.Right := bmpCurrent.Width;
PVI^.rcSource.Bottom := bmpCurrent.Height;
PVI^.rcTarget.Left := 0;
PVI^.rcTarget.Top := 0;
PVI^.rcTarget.Right := bmpCurrent.Width;
PVI^.rcTarget.Bottom := bmpCurrent.Height;
PVI^.dwBitRate := dwBitrate;
PVI^.bmiHeader.biWidth := bmpCurrent.Width;
PVI^.bmiHeader.biHeight := bmpCurrent.Height;
end;
hr := MediaProps.SetMediaType(PW
end;
hr := Profile.ReconfigStream(Str
end;
hr := ConfigAsfWriter.ConfigureF
ConfigAsfWriter := nil;
end;
end;
ASKER
Well it needs to be polished a little but it's working without errors, and the asf files it saves (when I disable the code from part 1) come out at the proper bitrate.
wooohoo.
wooohoo.
You beat me to it i was just going down the same route. My big question is if you can now manage to come up with a profile which is compatable with older versions, as it still writes asf's.. Anyway congratulations..
Dj
Dj
ASKER
I'm starting to work on that right now.. asf shouldn't be a problem, so long as I can switch to a WMP8 or WMP7 subtype, which I think I'll be able to do..
Thanks for your input on all the Q's so far.. there's bound to be another one before long..
Tell you what, I'll give you the 500 from this Q here and keep the other 500 for myself, call it even. ;)
Feel free to keep up the posts in Part 3.. I'm leaning towards your post there anyway, as I don't really have the time or motivation to mess around with the other option presented me there trying to get it to work, only to have a result which will be basically the same as embedding WMP except without all the support from MS.
Thanks for your input on all the Q's so far.. there's bound to be another one before long..
Tell you what, I'll give you the 500 from this Q here and keep the other 500 for myself, call it even. ;)
Feel free to keep up the posts in Part 3.. I'm leaning towards your post there anyway, as I don't really have the time or motivation to mess around with the other option presented me there trying to get it to work, only to have a result which will be basically the same as embedding WMP except without all the support from MS.
You beat me to it i was just going down the same route. My big question is if you can now manage to come up with a profile which is compatable with older versions, as it still writes asf's.. Anyway congratulations..
Dj
Dj
ASKER
Ok, one last question in this topic, then I'll payout the points.. ;)
With that new code added in, here's the code for the eventhandler of the enable/disable stream toggle button..
//-----------
procedure TfrmPreviewWindow.tbStream _EnableCli ck(Sender: TObject);
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
//if frmSettings_Stream.ShowMod al = mrOK then
begin
GetRawBitmap;
awStream.FilterGraph := fgPreview;
SetStreamProfile;
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.WriterAdvanced2.S etLiveSour ce(True);
with fgPreview as ICaptureGraphBuilder2 do
begin
RenderStream(@PIN_CATEGORY _CAPTURE, @WMMEDIATYPE_Video, fPreview as IBaseFilter, nil, awStream as IBaseFilter);
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
ServiceProvider := nil;
WriterAdvanced2.GetSink(0, DeletedSink);
WriterAdvanced2.RemoveSink (DeletedSi nk);
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
end;
fgPreview.Play;
AddPropertyItemsToMenu;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter s;
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
DeleteFile(sAppDir + inttostr(self.Handle) + '.asf');
ConnectToDevice;
end;
end;
//-----------
For some reason or another, the file gets created, but the network stream doesn't, the first time I click this button.
I have to click it three times to get the stream to come up.. (initial state off) -> (on) -> (off) -> (on).
I've been up all night.. having no luck figuring out why that's happening now. I tried moving all the code into another function, and setting a flag on the formcreate to call it three times the first time it's clicked instead of once, with no luck.
Opening a command prompt and looking at netstat -na confirms that there is no listening socket the first time.. every time after that it works fine. I'm tired.. so tired. ;)
With that new code added in, here's the code for the eventhandler of the enable/disable stream toggle button..
//-----------
procedure TfrmPreviewWindow.tbStream
var
WriterAdvanced2 : IWMWriterAdvanced2;
ServiceProvider : IServiceProvider;
DeletedSink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if bStreamVideo then
begin
//if frmSettings_Stream.ShowMod
begin
GetRawBitmap;
awStream.FilterGraph := fgPreview;
SetStreamProfile;
fgPreview.Stop;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.WriterAdvanced2.S
with fgPreview as ICaptureGraphBuilder2 do
begin
RenderStream(@PIN_CATEGORY
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
end;
fgPreview.Play;
AddPropertyItemsToMenu;
end;
end
else
begin
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.DisconnectFilter
sgPreview.FilterGraph := fgPreview;
fPreview.FilterGraph := fgPreview;
vwPreview.FilterGraph := fgPreview;
awStream.FilterGraph := nil;
DeleteFile(sAppDir + inttostr(self.Handle) + '.asf');
ConnectToDevice;
end;
end;
//-----------
For some reason or another, the file gets created, but the network stream doesn't, the first time I click this button.
I have to click it three times to get the stream to come up.. (initial state off) -> (on) -> (off) -> (on).
I've been up all night.. having no luck figuring out why that's happening now. I tried moving all the code into another function, and setting a flag on the formcreate to call it three times the first time it's clicked instead of once, with no luck.
Opening a command prompt and looking at netstat -na confirms that there is no listening socket the first time.. every time after that it works fine. I'm tired.. so tired. ;)
Why not try to get you hand on the IWMWriterNetworkSink interface, that gives you access to the open and close functions and saves you from having to delete the sink every time..
Sorry, that was a bit short.. basically start with the network on, so that the asfwriter component creates the networksink then retrieve it and use the open and close functions..
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hmm ok, what I did was set the filtergraph at design time, and render both streams instead of just the preview. After I render the stream, I run the code you provided previously to stop the file from being created.
After that's done, I've experimented with putting the above code before and after the graph is started, but it seems like no matter where I put it I get an access violation.
Right after the render starts on both streams I need to close the port, as streaming isn't enabled by default.
The AV is coming up right now on the line:
IWMWriterNetworkSink(sink) .Close;
My startup code, in the ConnectToDevice method, has this..
//----------
result := false;
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.Active := False;
fPreview.BaseFilter.Monike r := frmMain.sdeDevices.GetMoni ker(PTNDev iceInfo(tt nMe.Data)^ .iItemInde x);
fgPreview.Active := True;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.WriterAdvanced2.S etLiveSour ce(True);
with fgPreview as ICaptureGraphBuilder2 do
begin
hrPreview := RenderStream(@PIN_CATEGORY _PREVIEW, @WMMEDIATYPE_Video, fPreview as IBaseFilter, sgPreview as IBaseFilter, vwPreview as IbaseFilter);
RenderStream(@PIN_CATEGORY _CAPTURE, @WMMEDIATYPE_Video, fPreview as IBaseFilter, nil, awStream as IBaseFilter);
end;
with fgPreview as ICaptureGraphBuilder2 do
begin
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
ServiceProvider := nil;
WriterAdvanced2.GetSink(0, DeletedSink);
WriterAdvanced2.RemoveSink (DeletedSi nk);
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
end;
//----------
After that it just messes with the forms caption and makes sure hrPreview returned success, and starts the graph if it's all ok.
After slight modification of the code you gave me above, it looks like this, in the enable/disable stream event handler
//----------
procedure TfrmPreviewWindow.tbStream _EnableCli ck(Sender: TObject);
var
ServiceProvider : IServiceProvider;
WriterAdvanced2 : IWMWriterAdvanced2;
sink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
ServiceProvider := nil;
WriterAdvanced2.GetSink(0, sink);
if bStreamVideo then
begin
GetRawBitmap;
SetStreamProfile;
IWMWriterNetworkSink(sink) .Open(lwSt reamPort);
end
else
begin
IWMWriterNetworkSink(sink) .Close;
end;
end;
end;
//----------
After that's done, I've experimented with putting the above code before and after the graph is started, but it seems like no matter where I put it I get an access violation.
Right after the render starts on both streams I need to close the port, as streaming isn't enabled by default.
The AV is coming up right now on the line:
IWMWriterNetworkSink(sink)
My startup code, in the ConnectToDevice method, has this..
//----------
result := false;
fgPreview.Stop;
fgPreview.ClearGraph;
fgPreview.Active := False;
fPreview.BaseFilter.Monike
fgPreview.Active := True;
awStream.MaxUsers := iStreamMaxUsers;
awStream.Port := lwStreamPort;
awStream.FileName := sAppDir + inttostr(self.Handle) + '.asf';
awStream.WriterAdvanced2.S
with fgPreview as ICaptureGraphBuilder2 do
begin
hrPreview := RenderStream(@PIN_CATEGORY
RenderStream(@PIN_CATEGORY
end;
with fgPreview as ICaptureGraphBuilder2 do
begin
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
end;
//----------
After that it just messes with the forms caption and makes sure hrPreview returned success, and starts the graph if it's all ok.
After slight modification of the code you gave me above, it looks like this, in the enable/disable stream event handler
//----------
procedure TfrmPreviewWindow.tbStream
var
ServiceProvider : IServiceProvider;
WriterAdvanced2 : IWMWriterAdvanced2;
sink : iWMWriterSink;
begin
bStreamVideo := tbStream_Enable.Down;
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
if bStreamVideo then
begin
GetRawBitmap;
SetStreamProfile;
IWMWriterNetworkSink(sink)
end
else
begin
IWMWriterNetworkSink(sink)
end;
end;
end;
//----------
ASKER
Is this "Getsink(0," in both places going to cause problems? I wish I knew more about this, but that just looks off to me.. deleting it then trying to use it later. ;)
ASKER
Hmm I should be more careful posting before I look into everything. As suspected, the "sink" variable in the 2nd getsink call is nil..
ASKER
hmmm ok.. doing this instead... fixed the problems.
//--------
var
NetSink : IWMWriterNetworkSink;
//--------
if Succeeded(awStream.QueryIn terface(IS erviceProv ider, ServiceProvider)) then
begin
ServiceProvider.QueryServi ce(IID_IWM WriterAdva nced2, IID_IWMWriterAdvanced2, WriterAdvanced2);
WriterAdvanced2.GetSink(0, DeletedSink);
WriterAdvanced2.RemoveSink (DeletedSi nk);
WMCreateWriterNetworkSink( NetSink);
NetSink.SetMaximumClients( iStreamMax Users);
WriterAdvanced2.AddSink(Ne tSink);
awStream.WriterNetworkSink := NetSink;
NetSink := nil;
ServiceProvider := nil;
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
//--------
Then a slight modification of what you had, I believe I have to stop/start the graph to have changed settings take effect.
Thanks!
//--------
var
NetSink : IWMWriterNetworkSink;
//--------
if Succeeded(awStream.QueryIn
begin
ServiceProvider.QueryServi
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
WMCreateWriterNetworkSink(
NetSink.SetMaximumClients(
WriterAdvanced2.AddSink(Ne
awStream.WriterNetworkSink
NetSink := nil;
ServiceProvider := nil;
DeletedSink := nil;
WriterAdvanced2 := nil;
end;
//--------
Then a slight modification of what you had, I believe I have to stop/start the graph to have changed settings take effect.
Thanks!
I am going nuts here, i just noticed something.. i used the basic asfwriter sample that comes with DSPack as a basis when trying out different things.. yesterday i put the function in you provided above to change the bitrate.. and after that i put in code to start and stop the network stream in there as well i tried it again this morning because of your posting and the program now does everything including NOT creating a file.. and starting and stopping the network without having to stop the graph.. If you take the asf demo with the Asfwrite file, port and maxusers set to "c:\test.asf" 3333 and 8 and replace the unit code with the code below then that should work....
<unit>
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DSUtil, StdCtrls, DSPack, DirectShow9, Menus, ExtCtrls,ActiveX, WMF9;
type
TVideoForm = class(TForm)
FilterGraph: TFilterGraph;
MainMenu1: TMainMenu;
Devices: TMenuItem;
OpenDialog: TOpenDialog;
VideoWindow: TVideoWindow;
Filter: TFilter;
ASFWriter: TASFWriter;
StartStop1: TMenuItem;
Start1: TMenuItem;
Stop1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure SetStreamProfile;
procedure Start1Click(Sender: TObject);
procedure Stop1Click(Sender: TObject);
private
{ Diclarations privies }
public
{ Diclarations publiques }
procedure OnSelectDevice(sender: TObject);
end;
var
VideoForm: TVideoForm;
SysDev: TSysDevEnum;
implementation
uses Math;
{$R *.dfm}
procedure TVideoForm.FormCreate(Send
var
i: integer;
Device: TMenuItem;
begin
SysDev:= TSysDevEnum.Create(CLSID_V
if SysDev.CountFilters > 0 then
for i := 0 to SysDev.CountFilters - 1 do
begin
Device := TMenuItem.Create(Devices);
Device.Caption := SysDev.Filters[i].Friendly
Device.Tag := i;
Device.OnClick := OnSelectDevice;
Devices.Add(Device);
end;
end;
procedure TVideoForm.SetStreamProfil
var
hr : hresult;
ConfigAsfWriter : IConfigAsfWriter;
Profile : IWMProfile;
i : integer;
lwStreamCount : longword;
Stream : IWMStreamConfig;
dwBitrate : longword;
MediaProps : IWMMediaProps;
VMediaProps : IWMVideoMediaProps;
dwMPSize : longword;
PWMT : PWMMediaType;
PVI : PVideoInfo;
begin
if Succeeded(AsfWriter.QueryI
begin
hr := ConfigAsfWriter.GetCurrent
hr := Profile.GetStreamCount(lwS
for i := 1 to lwStreamCount do
begin
hr := Profile.GetStream(i - 1, Stream);
dwBitrate := 512 * 1024 ; // 384kbit/s
hr := Stream.SetBitrate(dwBitrat
if Succeeded(Stream.QueryInte
begin
hr := MediaProps.GetMediaType(ni
getmem(PWMT, dwMPSize);
hr := MediaProps.GetMediaType(PW
if PWMT^.formattype.D1 = FORMAT_VideoInfo.D1 then
begin
PVI := PVideoInfo(PWMT^.pbFormat)
PVI^.rcSource.Left := 0;
PVI^.rcSource.Top := 0;
PVI^.rcSource.Right := 480;
PVI^.rcSource.Bottom := 300;
PVI^.rcTarget.Left := 0;
PVI^.rcTarget.Top := 0;
PVI^.rcTarget.Right := 480;
PVI^.rcTarget.Bottom := 300;
PVI^.dwBitRate := dwBitrate;
PVI^.bmiHeader.biWidth := 480;
PVI^.bmiHeader.biHeight := 300;
end;
hr := MediaProps.SetMediaType(PW
end;
hr := Profile.ReconfigStream(Str
end;
hr := ConfigAsfWriter.ConfigureF
ConfigAsfWriter := nil;
end;
end;
procedure TVideoForm.OnSelectDevice(
var
ServiceProvider: IServiceProvider;
WriterAdvanced2 : IWMWriterAdvanced2;
sink : iWMWriterSink;
FAsfConfig: IConfigAsfWriter2;
FileSinkFilter2: IFileSinkFilter2;
FFileName : WideString;
i : IInterface;
v : IWMProfile;
p : IWMStreamConfig;
c : dword;
begin
FilterGraph.ClearGraph;
FilterGraph.Active := false;
Filter.BaseFilter.Moniker := SysDev.GetMoniker(TMenuIte
FilterGraph.Active := true;
SetStreamProfile;
if Succeeded(ASFWriter.QueryI
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
WriterAdvanced2.RemoveSink
end;
with FilterGraph as ICaptureGraphBuilder2 do
begin
CheckDSError(RenderStream(
CheckDSError(RenderStream(
end;
FilterGraph.Play;
end;
procedure TVideoForm.FormCloseQuery(
begin
SysDev.Free;
FilterGraph.ClearGraph;
FilterGraph.Active := false;
end;
procedure TVideoForm.Start1Click(Sen
var
ServiceProvider: IServiceProvider;
WriterAdvanced2 : IWMWriterAdvanced2;
sink : iWMWriterSink;
port : DWord;
begin
if Succeeded(ASFWriter.QueryI
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
port := 3333;
IWMWriterNetworkSink(sink)
end;
end;
procedure TVideoForm.Stop1Click(Send
var
ServiceProvider: IServiceProvider;
WriterAdvanced2 : IWMWriterAdvanced2;
sink : iWMWriterSink;
begin
if Succeeded(ASFWriter.QueryI
begin
ServiceProvider.QueryServi
ServiceProvider := nil;
WriterAdvanced2.GetSink(0,
IWMWriterNetworkSink(sink)
end;
end;
end.
</unit>
Oh no i was wrong it is still creating the asf at 0 bytes.. but the networking is starting and stopping without problems..
ASKER
Hmm looking for something else.. specifically, the writers OnStatus callback, so I can keep track of how many people are connected to the stream, I came across a file included with the demos.
In the .\DSPack\Demos\wmvnetwrite directory there is a file, NetWrite.pas which implements another class. I'm not sure why this is a demo instead of being included as one of the components on the palette, but it looks like it might have some interesting properties..
1. The OnStatus event I'm after. I've no idea how to implement that currently with the code we've been discussing.
2. It looks like this is a network-only WMV streaming object, which is exactly what I've been after. Go figure that I only find it now that I've got a working solution that I'm pretty happy with.
Just thought I'd pop that in there. If you can come up with an answer to #1 though I can drop more points in another Q. ;)
In the .\DSPack\Demos\wmvnetwrite
1. The OnStatus event I'm after. I've no idea how to implement that currently with the code we've been discussing.
2. It looks like this is a network-only WMV streaming object, which is exactly what I've been after. Go figure that I only find it now that I've got a working solution that I'm pretty happy with.
Just thought I'd pop that in there. If you can come up with an answer to #1 though I can drop more points in another Q. ;)
ASKER
Got a helpful pointer from the progdigy forums on #1.. here it is if you're interested..
//.....
TfrmPreviewWindow = class(TForm, IWMStatusCallback)
//.....
public // could be in private I imagine
function OnStatus(Status: TWMTStatus; hr: HRESULT; dwType: TWMTAttrDataType; pValue: PBYTE; pvContext: Pointer): HRESULT; stdcall;
//.....after your renderstream
if Succeeded(NetSink.QueryInt erface(IID _IWMRegist erCallback , StatusCall)) then
begin
StatusCall.Advise(self as IWMStatusCallback, nil)
end;
//.....
function TfrmPreviewWindow.OnStatus (Status: TWMTStatus; hr: HRESULT; dwType: TWMTAttrDataType; pValue: PBYTE; pvContext: Pointer): HRESULT;
begin
//just some sample code.
if (Status in [WMT_CLIENT_CONNECT, WMT_CLIENT_DISCONNECT]) then
begin
case Status of
WMT_CLIENT_CONNECT : inc(iStreamViewers);
WMT_CLIENT_DISCONNECT : dec(iStreamViewers);
end;
sbPreview.Panels.Items[1]. Text := inttostr(iStreamViewers) + ' viewers';
end;
end;
//.....
TfrmPreviewWindow = class(TForm, IWMStatusCallback)
//.....
public // could be in private I imagine
function OnStatus(Status: TWMTStatus; hr: HRESULT; dwType: TWMTAttrDataType; pValue: PBYTE; pvContext: Pointer): HRESULT; stdcall;
//.....after your renderstream
if Succeeded(NetSink.QueryInt
begin
StatusCall.Advise(self as IWMStatusCallback, nil)
end;
//.....
function TfrmPreviewWindow.OnStatus
begin
//just some sample code.
if (Status in [WMT_CLIENT_CONNECT, WMT_CLIENT_DISCONNECT]) then
begin
case Status of
WMT_CLIENT_CONNECT : inc(iStreamViewers);
WMT_CLIENT_DISCONNECT : dec(iStreamViewers);
end;
sbPreview.Panels.Items[1].
end;
end;
I presume you have that sorted now?, sorry but have been very busy last couple of days so haven't had much time to look at things..
ASKER
Yeah, that last post was the code I'm using to get it to work.
That's changed as well, as I've implemented a "current viewers" list for the streams, much as I've done for the HTTP side.. but yes, it's working.
Probably not much more to post in this thread, I just thought I'd drop a line in case you were interested. The app is really coming along and I'm quite happy with it, this is probably the most complete and useful thing I've written outside of the stuff I do when I'm working.
Definitely my best piece of work done "for myself" and released to the public, the first one I'm thinking might actually be worthy of a spot on a store shelf somewhere. ;)
Thanks for all your input and help. I'm still deciding what to do with Part 3 of the question.. I really thought some Java people would respond.. oh well.
That's changed as well, as I've implemented a "current viewers" list for the streams, much as I've done for the HTTP side.. but yes, it's working.
Probably not much more to post in this thread, I just thought I'd drop a line in case you were interested. The app is really coming along and I'm quite happy with it, this is probably the most complete and useful thing I've written outside of the stuff I do when I'm working.
Definitely my best piece of work done "for myself" and released to the public, the first one I'm thinking might actually be worthy of a spot on a store shelf somewhere. ;)
Thanks for all your input and help. I'm still deciding what to do with Part 3 of the question.. I really thought some Java people would respond.. oh well.
Good to hear everything is working out..
As far as the java side, i do that quite a bit as well and can tell you that unless you depend on the media framework (of which a java-only version is available, but that would still mean quite a nasty download for the user..) the code for a player would be hell to write (at least within the scope of my knowledge..)
Anyway goodluck and maybe will meet on another thread..
Dj
Hello all,
I have tried to implement the OnStatus interface with TASFWriter but I am having trouble getting it to work. I receive an OnStatus event when starting to write the ASF but that's it.
I do not get an event when a client connects.
Please help ! I tried to use the code above but still I do not get the events when a client connects/disconnects
Thank you for any help that yo can provide.
delphi_tip
I have tried to implement the OnStatus interface with TASFWriter but I am having trouble getting it to work. I receive an OnStatus event when starting to write the ASF but that's it.
I do not get an event when a client connects.
Please help ! I tried to use the code above but still I do not get the events when a client connects/disconnects
Thank you for any help that yo can provide.
delphi_tip
Wow, this is looking a bit more of an issue, would you have a problem sending me some code showing the problem so i have a starting point?
Cheers,
Dj