After a stream has been created, it can be written to freely by either your plug-in or Netscape's Navigator. In most cases, the data flow direction of a stream is from the Navigator and to the plug-in.
This chapter covers the methods used for stream reading and writing. In this chapter, you will learn the difference between processing a file in a streaming manner or waiting for the complete file download. APIs for pulling data from the server and allowing the server to push data to your plug-in are introduced.
The following APIs are covered in this chapter:
NPP_WriteReady
NPP_Write
NPP_StreamAsFile
NPN_RequestRead
NPN_Write
The plug-in API provides the methods NPP_WriteReady and NPP_Write to write streamed data to your plug-in. Both of these methods are plug-in implemented, which means that they are methods in your plug-in that are called from the Navigator.
After a stream has been created, your plug-in sits patiently waiting for data. This wait can be long or short, depending on your connection's speed. When the data arrives, the first call made to the plug-in is NPP_WriteReady. This API lets the plug-in know that data is on the way and asks how much data the plug-in can handle in one chunk. The size of this chunk can be anywhere from 1 to over 2 billion bytes (the maximum size of a 32-bit signed digit). Generally, your plug-in should request buffer sizes that make sense for a given data type. For example, if you are streaming compressed audio data and sending 4096-byte buffers to the CODEC, it makes sense to ask Netscape for the same size buffer. In most cases, Navigator passes your plug-in data in chunks no larger than 8KB buffers. You also might return 0 to Navigator to temporarily stop the data flow. Navigator tries again at regular intervals with calls to NPP_WriteReady to see whether your plug-in is again ready for data.
Following the call to NPP_WriteReady is a call to another plug-in method, NPP_Write. The parameters passed to NPP_Write contain a pointer to the actual stream data for processing and the length of that data. You can immediately process the data or copy it for later use. The stream data provided by the Navigator is only valid for the duration of the NPP_Write call. If you plan to access any of this data at a later time, your plug-in must copy it to a separate buffer.
It is important to realize that the return value from NPP_WriteReady
is the amount that your plug-in must consume on each call to NPP_Write,
not the upper limit of data written by Navigator during NPP_Write.
For example, if a plug-in returns 8KB from NPP_WriteReady,
Navigator might respond with a 10KB write with NPP_Write.
Normally, this is not a problem; just copy the data you want (8KB,
in this case), and return the amount of data consumed (again 8KB).
Just make sure you don't key your code from the Netscape buffer
size and expect it to be less than or equal to the size you specified
in NPP_WriteReady.
| Note |
In some plug-ins, a secondary buffer management scheme is needed to handle real-time data streaming. Data from NPP_Write is saved to establish a low water mark to begin processing. A scheme such as this is used in Chapter 17, "A Streaming Audio Sample." The source code for this example is available on this book's CD-ROM. |
Another interesting parameter passed to NPP_Write is offset. Use this value to see how far you are into the stream. In most cases, this offset is incremented by the size of each buffer. One exception to this rule is the case of a seekable stream as indicated by the NP_SEEK definition. A seekable stream is random access in nature and is not really a true data stream. Therefore, the offset still relates to file position, but it is not necessarily incremented during each call to NPP_Write.
At some time in the life of a plug-in, it might be necessary to kill a stream before it is complete. This is accomplished by returning a negative value from NPP_Write. Consider a case in which your plug-in cannot handle a specific data type within its MIME type. The file's header can be read and the stream can be aborted, followed by an error message to the user. This technique avoids a long wait for an unsupported file format.
Killing a stream in this manner from within your plug-in is the
same as having a user click the Stop button on the browser.
| Note |
Returning a negative value from NPP_Write with the Macintosh Navigator 2.x creates problems and should be avoided on this platform and the Navigator version. |
In some cases, your plug-in might get less data in the NPP_Write call than was requested in NPP_WriteReady. The most common case is during the end of the stream when it might not end on an even buffer boundary. If you are getting 4KB buffers and the file that is being streamed is not divisible by 4KB, of course you will get a remainder. This scenario is usually not a problem, considering that your buffer will be larger.
A mechanism exists to let your plug-in process a file as a whole file rather than a stream of data. If possible, this practice should be avoided because it makes the user wait for the complete file to download before any processing can begin. In some data types, this is unavoidable, but many can be processed in a streaming manner.
When your plug-in requires a complete file, a plug-in indicates this to the Navigator by setting *stype to NP_ASFILE in your NPP_NewStream method. If you do this, a call is made to your NPP_StreamAsFile method when the file has completed downloading. NPP_StreamAsFile is not called for NP_SEEK or NP_NORMAL. The Navigator 3.0 Plug-in API introduces a new type called NP_ASFILEONLY. This new type is similar to NP_ASFILE, but it is more efficient because it intelligently avoids streaming local files. Consult Chapter 10, "Stream Creation and Destruction," for more information on *stype values.
NPP_StreamAsFile is passed
a pointer to a fully qualified path of the file on the local system.
This file might have originated on the local machine, and perhaps
it was opened through the File|Open File in Browser menu item.
In this case, the file lives on the local machine and the path
to it is the actual fully qualified filename. Otherwise, the file
might have been downloaded from an HTTP server and resides in
Netscape's cache directory with an obscure filename. The real
filename can always be found by parsing the URL in the NPStream
structure.
| Tip |
| Even when you use a stream of type NP_ASFILE and the NPP_StreamAsFile method, data is still streamed through the Navigator. Although, in many cases, this is not a
problem, consider a CD-ROM or file server with very large video files. You certainly don't want to read the file twice; once through Netscape and once with your plug-in is too much. You can avoid this problem by using NP_ASFILEONLY if your plug-in is running under Navigator 3.0 and above.
To avoid this scenario in Navigator 2.x, follow these steps:
Feel free to use this technique for all local file processing. Remember that you can avoid this process by using NP_ASFILEONLY with the Navigator Plug-in API 3.0 and above (Plug-in API Major Version of 0, Plug-in API Minor Version greater than or equal to 8). |
Another interesting thing that a stream of type NP_ASFILE can do is create a local file within Netscape's cache while still letting the plug-in process the file as a stream. Stream types of NP_ASFILE let your plug-in process stream buffers normally through the use of NPP_WriteReady and NPP_Write.
The advantage of this technique is that it allows you to continue
processing the file in Netscape's cache after the stream is complete.
The name of this cached file is passed to your plug-in during
the NPP_StreamAsFile method.
Of course, you can always cache downloaded files yourself either
in memory or on disk.
| Note |
Netscape caches files in a subdirectory called Cache. The directory and cache size can be changed via the Options|Network Preferences menu item. As a plug-in developer, you should be aware that using Netscape's file caching for large files tends to blow away a lot of little files such as HTML code. Because the caching scheme is First In First Out (FIFO), your big files squeeze other files out. In addition, a file is not cached unless it has been completely streamed and, in the case of plug-ins, is type NP_ASFILE. |
One of the more interesting APIs of Netscape's plug-in design
is NPN_RequestRead. This
method allows a plug-in to read specific portions of a file without
downloading it. This isn't a big deal for local files, but what
if you could pick through large files on an HTTP server? Imagine
being able to scan through a large audio file. Perhaps, you might
listen to half of a speech tonight and the other half tomorrow
night. The second time around, instead of downloading the entire
first half again, a plug-in simply starts in the middle of the
file. In the world of streams, this random access is called seeking.
| Warning |
Before you get too excited, at the time of this writing there are no HTTP servers that can handle file seeking through what are known as byte range requests. Because the implementation for this on the server side is very straightforward, you should see this feature very soon-if not by the time you are reading this book. |
NPN_RequestRead can be called on seekable streams only. To determine whether a stream supports seeks, simply check to see whether the seekable parameter of NPP_NewStream is set to TRUE. If it is, be sure to set stype of the same API to the stream type NP_SEEK.
To seek within a stream, you must first build a linked list of byte range structures. If you are only requesting one contiguous range of bytes, your list only consists of one member. The structure to build the list is called NPByteRange and is defined as follows:
typedef struct _NPByteRange
{
int32 offset;
uint32 length;
struct _NPByteRange* next;
} NPByteRange;
Fill the structure with the byte offset of the beginning of the byte range. Positive values are from the first byte in the file, and negative values are from the last byte. The next pointer references the following NPByteRange structure, if any. To add another request, simply allocate another structure and attach it to the next pointer. As with most linked lists, be sure to set next to NULL for the last structure.
As with a normal stream, a successful return of data from an NPN_RequestRead comes through the NPP_WriteReady and NPP_Write methods. You might remember from NPP_Write that there was an odd little parameter called offset. In a nonseeking stream, this parameter is not very exciting because it simply increments along with each buffer. With seeking streams, its task is more important. If you've made a multiple-byte request call with NPN_RequestRead, you can match each request by the offset values in both NPP_Write and the NPByteRange structure.
Navigator 3.0 introduces NPN_Write to be used in conjunction with NPN_NewStream and NPN_DestroyStream. NPN_Write allows your plug-in to write data to a stream for consumption by Netscape. Although, at the time of this writing, this method is not working properly, it should work when Navigator 3.0 is out of beta. See Chapter 10 for an example of how you might use these new methods.
The following sections give in-depth information on the APIs used in this chapter.
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX, Navigator 2.0 and above. |
| API Type | Plug-in implemented method. |
| Purpose | Determines whether your plug-in is ready to receive data, and how much data it is willing to receive. |
| Syntax | int32 NPP_WriteReady (NPP instance, NPStream* stream)
NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPStream* stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. |
| Includes | #include <npapi.h> |
| Description | NPP_WriteReady is called by the Navigator to ask your plug-in how much data it is willing to receive. This amount is indicated by the return value. NPP_WriteReady is usually, but not always, followed closely by a call to NPP_Write with the actual data. Your plug-in can get a larger data buffer than requested during NPP_Write, but it is only required to consume the amount returned by the previous call to NPP_WriteReady.
Even if you are processing a stream as NP_ASFILE, a call is made to NPP_WriteReady. In this case, you should simply return with a very large value to indicate to the Navigator that your plug-in will not process data in a streaming manner and doesn't care how much is written at one time. Don't be afraid to return a value of zero to prevent Navigator from sending more data. This is a stream overrun and a valid case. Navigator will try again later. |
| Returns | int32 bytes: Data length in bytes that the plug-in is ready to receive. |
| See Also | NPP_Write |
| Example | //
// NPP_WriteReady // int32 NP_LOADDS NPP_WriteReady (NPP instance, NPStream *stream) { if (instance == NULL) return 0; // Retrieve instance data MyInstance* myinstance = (MyInstance *)instance->pdata; // Retrieve per stream data MyStream* mystream = (MyStream *)stream->pdata; // Take any action during this time if (mystream) return mystream->PrepareForWrite (); else return 0; } |
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX, Navigator 2.0 and above. |
| API Type | Plug-in implemented method. |
| Purpose | Called from the Navigator to give your plug-in its requested buffer from NPP_WriteReady. |
| Syntax | int32 NPP_Write (NPP instance,
NPStream* stream, int32 offset, int32 len, void* buffer) NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPStream* stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. int32 offset: A byte offset within the data stream. Useful for verifying range requests from NPN_RequestRead or verifying stream progress. int32 len: The length in bytes of the following buffer. void* buffer: A pointer to the buffer containing stream data. |
| Includes | #include <npapi.h> |
| Description | NPP_Write is called to give your plug-in the data that it requested from NPP_WriteReady. Although the length of this
data can be more or less than the requested amount, your plug-in must consume at least the amount requested from NPP_WriteReady or the entire buffer.
The offset parameter verifies how many bytes have been streamed. In a nonseeking stream, this value grows according to the size of each buffer written. In a seeking stream that uses byte range requests, offset can be used to verify read requests from NPN_RequestRead. The plug-in returns the number of bytes written, which is the amount of data that the plug-in consumed during the NPP_Write method. A negative value can be returned to request the destruction of the stream. |
| Returns | int32 bytes: The number of bytes written or a negative value to destroy the stream. |
| See Also | NPP_WriteReady |
| Example | //
// NPP_Write // int32 NP_LOADDS NPP_Write (NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) if (instance == NULL) return -1; // Retrieve instance data MyInstance* myinstance = (MyInstance *)instance->pdata;
// Retrieve per stream data
MyStream* mystream = (MyStream *)stream->pdata;
// Take any action during this time |
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX, Navigator 2.0 and above. |
| API Type | Plug-in implemented method. |
| Purpose | Provides a filename for a file-based plug-in of type NP_ASFILE, or for Navigator 3.0 and above it provides NP_ASFILEONLY. |
| Syntax | void NP_LOADDS NPP_StreamAsFile (NPP instance,
NPStream* stream, const char* fname) NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPStream* stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. const char* fname: A pointer to a buffer containing a fully qualified path of the filename on the client. |
| Includes | #include <npapi.h> |
| Description | If you set *stype in NPP_NewStream to NP_ASFILE or NP_ASFILEONLY for Navigator 3.0, NPP_StreamAsFile is called upon completion of the stream and gives you a fully qualified path to the local filename.
NPP_StreamAsFile and NPP_DestroyStream are very similar methods. Both are called on completion of the stream. For local files, NPP_DestroyStream could be used, instead of NPP_StreamAsFile, by retrieving the local filename from the given URL. In the case of a remote file that was downloaded by the Navigator, you must use NPP_StreamAsFile to get the filename within Netscape's file cache. It is interesting to note that although you might have indicated that your plug-in will wait for the end of stream with NP_ASFILE, NPP_WriteReady and NPP_Write are still called in the normal fashion, allowing you to see the data as it is streamed. This method is not called for streams of type NP_NORMAL. |
| Returns | None. |
| See Also | NPP_DestroyStream, NPP_Write, NPP_WriteReady |
| Example | //
// NPP_StreamAsFile // // void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname) { // Check for bogus filename if (fname == NULL) return; if (fname[0] == NULL) return; if (instance == NULL) return; // Retrieve instance data MyInstance* myinstance = (MyInstance *)instance->pdata; // Retrieve per stream data
MyStream* mystream = (MyStream *)stream->pdata; |
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX, Navigator 2.0 and above. |
| API Type | Navigator implemented method. |
| Purpose | Requests a range of bytes from a seekable stream that will be written to the plug-in in subsequent NPP_Write calls. |
| Syntax | NPError NPN_RequestRead (NPStream* stream, NPByteRange* rangeList)
NPStream* stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. NPByteRange* rangeList: A pointer to the head of a linked list of NPByteRange structures. Each structure corresponds to a separate byte range request. |
| Includes | #include <npapi.h> |
| Description | NPN_RequestRead requests one or more byte range reads that result in calls to NPP_WriteReady and NPP_Write. For multiple requests, a linked list of NPByteRange structures is created, with each structure representing a separate request.
The stream used in the call must be valid and must be of type NP_SEEK, which is set in the method NPP_NewStream. |
| Returns | NPERR_NO_ERROR: The call was successful.
NPERR_GENERIC_ERROR: An error occurred. |
| See Also | NPP_WriteReady |
| Example | //
// NPN_RequestRead // // Build a linked list of ranges for three reads. NPByteRange range1, range2, range3; range.offset = 55000; range.length = 1045; range.next = &range2; range2.offset = 245000; range2.length = 4000; range2.next = &range3; range3.offset = 500000; range3.length = 100000; range3.next = NULL; NPError rc = NPN_RequestRead (stream, &range1); . . . |
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX, Navigator 3.0 and above. |
| API Type | Navigator implemented method. |
| Purpose | Called by your plug-in to write to a stream for consumption by the Navigator. This stream was created by a previous call to NPN_NewStream. |
| Syntax | int32 NPN_Write (NPP instance, NPStream* stream, int32 len, void* buffer)
NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPStream* stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. * stream: A pointer to structure NPStream that references both the plug-in's and Netscape's stream instance data. This structure also contains the URL, last modification date, and content length in bytes. int32 len: The length in bytes of the buffer to be written. void* buffer: A pointer to the buffer to be written. |
| Includes | #include <npapi.h> |
| Description | Use NPN_Write to write data to a stream for consumption by the Navigator. This new Navigator 3.0 API should be used in conjunction with NPN_NewStream and NPN_DestroyStream. See Chapter 10 for an example including these three methods.
If NPN_Write returns a negative value indicating an error, you should terminate the stream immediately with a call to NPN_DestroyStream. |
| Returns | int32 bytes: The number of bytes actually written.
int32 (negative value): An error occurred. |
| See Also | NPP_WriteReady, NPP_Write |
| Example | //
// NPN_Write // . . . // Write some HTML code to the Navigator char Buffer[] = "<html>\n<body>\n\n<h2 align=center>Stuff</h2>\n\n</bod Ây>\n</html>" int32 bytes = NPN_Write (instance, stream, strlen(Buffer), Buffer); . . |
This chapter has shown you how to read and write streams. In most cases, a plug-in uses a server push model in which the HTTP server pumps data to the Navigator, which in turn calls your plug-in's API NPP_Write. A client pull technique was also discussed using the API NPN_RequestRead. This API is used strictly for seekable streams.
The next chapter discusses Netscape's memory management methods for your plug-in. You'll learn why you need to use them to avoid memory leaks from your plug-in code module. The NPN_MemAlloc, NPN_MemFlush, and NPN_MemFree APIs are covered.