1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // A template for a MediaSource encapsulating an audio/video input device |
19 | // |
20 | // NOTE: Sections of this code labeled "%%% TO BE WRITTEN %%%" are incomplete, and need to be written by the programmer |
21 | // (depending on the features of the particular device). |
22 | // Implementation |
23 | |
24 | #include "DeviceSource.hh" |
25 | #include <GroupsockHelper.hh> // for "gettimeofday()" |
26 | |
27 | DeviceSource* |
28 | DeviceSource::createNew(UsageEnvironment& env, |
29 | DeviceParameters params) { |
30 | return new DeviceSource(env, params); |
31 | } |
32 | |
33 | EventTriggerId DeviceSource::eventTriggerId = 0; |
34 | |
35 | unsigned DeviceSource::referenceCount = 0; |
36 | |
37 | DeviceSource::DeviceSource(UsageEnvironment& env, |
38 | DeviceParameters params) |
39 | : FramedSource(env), fParams(params) { |
40 | if (referenceCount == 0) { |
41 | // Any global initialization of the device would be done here: |
42 | //%%% TO BE WRITTEN %%% |
43 | } |
44 | ++referenceCount; |
45 | |
46 | // Any instance-specific initialization of the device would be done here: |
47 | //%%% TO BE WRITTEN %%% |
48 | |
49 | // We arrange here for our "deliverFrame" member function to be called |
50 | // whenever the next frame of data becomes available from the device. |
51 | // |
52 | // If the device can be accessed as a readable socket, then one easy way to do this is using a call to |
53 | // envir().taskScheduler().turnOnBackgroundReadHandling( ... ) |
54 | // (See examples of this call in the "liveMedia" directory.) |
55 | // |
56 | // If, however, the device *cannot* be accessed as a readable socket, then instead we can implement it using 'event triggers': |
57 | // Create an 'event trigger' for this device (if it hasn't already been done): |
58 | if (eventTriggerId == 0) { |
59 | eventTriggerId = envir().taskScheduler().createEventTrigger(deliverFrame0); |
60 | } |
61 | } |
62 | |
63 | DeviceSource::~DeviceSource() { |
64 | // Any instance-specific 'destruction' (i.e., resetting) of the device would be done here: |
65 | //%%% TO BE WRITTEN %%% |
66 | |
67 | --referenceCount; |
68 | if (referenceCount == 0) { |
69 | // Any global 'destruction' (i.e., resetting) of the device would be done here: |
70 | //%%% TO BE WRITTEN %%% |
71 | |
72 | // Reclaim our 'event trigger' |
73 | envir().taskScheduler().deleteEventTrigger(eventTriggerId); |
74 | eventTriggerId = 0; |
75 | } |
76 | } |
77 | |
78 | void DeviceSource::doGetNextFrame() { |
79 | // This function is called (by our 'downstream' object) when it asks for new data. |
80 | |
81 | // Note: If, for some reason, the source device stops being readable (e.g., it gets closed), then you do the following: |
82 | if (0 /* the source stops being readable */ /*%%% TO BE WRITTEN %%%*/) { |
83 | handleClosure(); |
84 | return; |
85 | } |
86 | |
87 | // If a new frame of data is immediately available to be delivered, then do this now: |
88 | if (0 /* a new frame of data is immediately available to be delivered*/ /*%%% TO BE WRITTEN %%%*/) { |
89 | deliverFrame(); |
90 | } |
91 | |
92 | // No new data is immediately available to be delivered. We don't do anything more here. |
93 | // Instead, our event trigger must be called (e.g., from a separate thread) when new data becomes available. |
94 | } |
95 | |
96 | void DeviceSource::deliverFrame0(void* clientData) { |
97 | ((DeviceSource*)clientData)->deliverFrame(); |
98 | } |
99 | |
100 | void DeviceSource::deliverFrame() { |
101 | // This function is called when new frame data is available from the device. |
102 | // We deliver this data by copying it to the 'downstream' object, using the following parameters (class members): |
103 | // 'in' parameters (these should *not* be modified by this function): |
104 | // fTo: The frame data is copied to this address. |
105 | // (Note that the variable "fTo" is *not* modified. Instead, |
106 | // the frame data is copied to the address pointed to by "fTo".) |
107 | // fMaxSize: This is the maximum number of bytes that can be copied |
108 | // (If the actual frame is larger than this, then it should |
109 | // be truncated, and "fNumTruncatedBytes" set accordingly.) |
110 | // 'out' parameters (these are modified by this function): |
111 | // fFrameSize: Should be set to the delivered frame size (<= fMaxSize). |
112 | // fNumTruncatedBytes: Should be set iff the delivered frame would have been |
113 | // bigger than "fMaxSize", in which case it's set to the number of bytes |
114 | // that have been omitted. |
115 | // fPresentationTime: Should be set to the frame's presentation time |
116 | // (seconds, microseconds). This time must be aligned with 'wall-clock time' - i.e., the time that you would get |
117 | // by calling "gettimeofday()". |
118 | // fDurationInMicroseconds: Should be set to the frame's duration, if known. |
119 | // If, however, the device is a 'live source' (e.g., encoded from a camera or microphone), then we probably don't need |
120 | // to set this variable, because - in this case - data will never arrive 'early'. |
121 | // Note the code below. |
122 | |
123 | if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet |
124 | |
125 | u_int8_t* newFrameDataStart = (u_int8_t*)0xDEADBEEF; //%%% TO BE WRITTEN %%% |
126 | unsigned newFrameSize = 0; //%%% TO BE WRITTEN %%% |
127 | |
128 | // Deliver the data here: |
129 | if (newFrameSize > fMaxSize) { |
130 | fFrameSize = fMaxSize; |
131 | fNumTruncatedBytes = newFrameSize - fMaxSize; |
132 | } else { |
133 | fFrameSize = newFrameSize; |
134 | } |
135 | gettimeofday(&fPresentationTime, NULL); // If you have a more accurate time - e.g., from an encoder - then use that instead. |
136 | // If the device is *not* a 'live source' (e.g., it comes instead from a file or buffer), then set "fDurationInMicroseconds" here. |
137 | memmove(fTo, newFrameDataStart, fFrameSize); |
138 | |
139 | // After delivering the data, inform the reader that it is now available: |
140 | FramedSource::afterGetting(this); |
141 | } |
142 | |
143 | |
144 | // The following code would be called to signal that a new frame of data has become available. |
145 | // This (unlike other "LIVE555 Streaming Media" library code) may be called from a separate thread. |
146 | // (Note, however, that "triggerEvent()" cannot be called with the same 'event trigger id' from different threads. |
147 | // Also, if you want to have multiple device threads, each one using a different 'event trigger id', then you will need |
148 | // to make "eventTriggerId" a non-static member variable of "DeviceSource".) |
149 | void signalNewFrameData() { |
150 | TaskScheduler* ourScheduler = NULL; //%%% TO BE WRITTEN %%% |
151 | DeviceSource* ourDevice = NULL; //%%% TO BE WRITTEN %%% |
152 | |
153 | if (ourScheduler != NULL) { // sanity check |
154 | ourScheduler->triggerEvent(DeviceSource::eventTriggerId, ourDevice); |
155 | } |
156 | } |
157 | |