├── LICENSE ├── Makefile ├── README.md ├── dump.c ├── dump.h └── h264.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Gabriel Llamas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = h264 2 | 3 | CC = gcc 4 | CFLAGS = -DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS \ 5 | -DTARGET_POSIX -D_LINUX -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE \ 6 | -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -DHAVE_LIBOPENMAX=2 -DOMX \ 7 | -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX \ 8 | -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -fPIC \ 9 | -ftree-vectorize -pipe -Werror -g -Wall 10 | LDFLAGS = -L/opt/vc/lib -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread 11 | INCLUDES = -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads \ 12 | -I/opt/vc/include/interface/vmcs_host/linux 13 | 14 | SRC = $(BIN).c dump.c 15 | OBJS = $(BIN).o dump.o 16 | 17 | all: $(BIN) $(SRC) 18 | 19 | %.o: %.c 20 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ -Wno-deprecated-declarations 21 | 22 | $(BIN): $(OBJS) 23 | $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic 24 | 25 | .PHONY: clean rebuild 26 | 27 | clean: 28 | rm -f $(BIN) $(BIN).o dump.o video.h264 29 | 30 | rebuild: 31 | make clean && make -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | openmax-h264 2 | ============ 3 | 4 | #### An OpenMAX IL example that records an H.264 video with a Raspberry Pi #### 5 | 6 | The Multi-Media Abstraction Layer (MMAL) library provided by Broadcom is not documented, so it's nearly impossible to understand how to use it. Furthermore, it's a library that wraps all the OpenMAX IL specification, so if you simply need to record an H.264 video like in this example, you can just ignore that extra layer and talk directly to OpenMAX IL. Once you understand OpenMAX IL, it's affordable to try to write your own programs without MMAL. In other words, if you are new to the OpenMAX world, it's better to learn OpenMAX IL than MMAL, which is not documented and only works with Broadcom devices. 7 | 8 | This example records a 3 second video in H.264 format. The components involved are: `camera`, `video_encode` and `null_sink`. 9 | 10 | If you want to play the video, you'll need to wrap it with a container format (some video players couldn't require this step), e.g. Matroska. You can download Matroska tools from [here](http://www.bunkus.org/videotools/mkvtoolnix). Then, convert the H.264 file to a Matroska file: 11 | 12 | ``` 13 | $ mkvmerge --default-duration 0:25p -o video.mkv video.h264 14 | ``` 15 | 16 | Where `25p` is the encoding framerate of the h264 video. For example, if you record 640x480 @30fps, then you should encode the matroska file with `--default-duration 0:30p`. 17 | 18 | Build steps: 19 | 20 | - Download and install the `gcc` and `make` programs. 21 | - Download this repository. 22 | - Compile and execute: `make && ./h264` 23 | 24 | Useful documentation: 25 | 26 | - [OpenMAX IL Specification v1.1.2](https://www.khronos.org/registry/omxil/specs/OpenMAX_IL_1_1_2_Specification.pdf) 27 | - [Understanding OpenMAX IL](http://www.slideshare.net/pchethan/understanding-open-max-il-18376762) 28 | - [Broadcom OpenMAX IL components](https://github.com/raspberrypi/firmware/tree/master/documentation/ilcomponents) 29 | - [OpenMAX IL header files](https://github.com/raspberrypi/firmware/tree/master/opt/vc/include/IL) -------------------------------------------------------------------------------- /dump.c: -------------------------------------------------------------------------------- 1 | #include "dump.h" 2 | 3 | #define DUMP_CASE(x) case x: return #x; 4 | 5 | const char* dump_OMX_COLOR_FORMATTYPE (OMX_COLOR_FORMATTYPE color){ 6 | switch (color){ 7 | DUMP_CASE (OMX_COLOR_FormatUnused) 8 | DUMP_CASE (OMX_COLOR_FormatMonochrome) 9 | DUMP_CASE (OMX_COLOR_Format8bitRGB332) 10 | DUMP_CASE (OMX_COLOR_Format12bitRGB444) 11 | DUMP_CASE (OMX_COLOR_Format16bitARGB4444) 12 | DUMP_CASE (OMX_COLOR_Format16bitARGB1555) 13 | DUMP_CASE (OMX_COLOR_Format16bitRGB565) 14 | DUMP_CASE (OMX_COLOR_Format16bitBGR565) 15 | DUMP_CASE (OMX_COLOR_Format18bitRGB666) 16 | DUMP_CASE (OMX_COLOR_Format18bitARGB1665) 17 | DUMP_CASE (OMX_COLOR_Format19bitARGB1666) 18 | DUMP_CASE (OMX_COLOR_Format24bitRGB888) 19 | DUMP_CASE (OMX_COLOR_Format24bitBGR888) 20 | DUMP_CASE (OMX_COLOR_Format24bitARGB1887) 21 | DUMP_CASE (OMX_COLOR_Format25bitARGB1888) 22 | DUMP_CASE (OMX_COLOR_Format32bitBGRA8888) 23 | DUMP_CASE (OMX_COLOR_Format32bitARGB8888) 24 | DUMP_CASE (OMX_COLOR_FormatYUV411Planar) 25 | DUMP_CASE (OMX_COLOR_FormatYUV411PackedPlanar) 26 | DUMP_CASE (OMX_COLOR_FormatYUV420Planar) 27 | DUMP_CASE (OMX_COLOR_FormatYUV420PackedPlanar) 28 | DUMP_CASE (OMX_COLOR_FormatYUV420SemiPlanar) 29 | DUMP_CASE (OMX_COLOR_FormatYUV422Planar) 30 | DUMP_CASE (OMX_COLOR_FormatYUV422PackedPlanar) 31 | DUMP_CASE (OMX_COLOR_FormatYUV422SemiPlanar) 32 | DUMP_CASE (OMX_COLOR_FormatYCbYCr) 33 | DUMP_CASE (OMX_COLOR_FormatYCrYCb) 34 | DUMP_CASE (OMX_COLOR_FormatCbYCrY) 35 | DUMP_CASE (OMX_COLOR_FormatCrYCbY) 36 | DUMP_CASE (OMX_COLOR_FormatYUV444Interleaved) 37 | DUMP_CASE (OMX_COLOR_FormatRawBayer8bit) 38 | DUMP_CASE (OMX_COLOR_FormatRawBayer10bit) 39 | DUMP_CASE (OMX_COLOR_FormatRawBayer8bitcompressed) 40 | DUMP_CASE (OMX_COLOR_FormatL2) 41 | DUMP_CASE (OMX_COLOR_FormatL4) 42 | DUMP_CASE (OMX_COLOR_FormatL8) 43 | DUMP_CASE (OMX_COLOR_FormatL16) 44 | DUMP_CASE (OMX_COLOR_FormatL24) 45 | DUMP_CASE (OMX_COLOR_FormatL32) 46 | DUMP_CASE (OMX_COLOR_FormatYUV420PackedSemiPlanar) 47 | DUMP_CASE (OMX_COLOR_FormatYUV422PackedSemiPlanar) 48 | DUMP_CASE (OMX_COLOR_Format18BitBGR666) 49 | DUMP_CASE (OMX_COLOR_Format24BitARGB6666) 50 | DUMP_CASE (OMX_COLOR_Format24BitABGR6666) 51 | DUMP_CASE (OMX_COLOR_Format32bitABGR8888) 52 | DUMP_CASE (OMX_COLOR_Format8bitPalette) 53 | DUMP_CASE (OMX_COLOR_FormatYUVUV128) 54 | DUMP_CASE (OMX_COLOR_FormatRawBayer12bit) 55 | DUMP_CASE (OMX_COLOR_FormatBRCMEGL) 56 | DUMP_CASE (OMX_COLOR_FormatBRCMOpaque) 57 | DUMP_CASE (OMX_COLOR_FormatYVU420PackedPlanar) 58 | DUMP_CASE (OMX_COLOR_FormatYVU420PackedSemiPlanar) 59 | default: return "unknown OMX_COLOR_FORMATTYPE"; 60 | } 61 | } 62 | 63 | const char* dump_OMX_OTHER_FORMATTYPE (OMX_OTHER_FORMATTYPE format){ 64 | switch (format){ 65 | DUMP_CASE (OMX_OTHER_FormatTime); 66 | DUMP_CASE (OMX_OTHER_FormatPower); 67 | DUMP_CASE (OMX_OTHER_FormatStats); 68 | DUMP_CASE (OMX_OTHER_FormatBinary); 69 | DUMP_CASE (OMX_OTHER_FormatText); 70 | DUMP_CASE (OMX_OTHER_FormatTextSKM2); 71 | DUMP_CASE (OMX_OTHER_FormatText3GP5); 72 | default: return "unknown OMX_OTHER_FORMATTYPE"; 73 | } 74 | } 75 | 76 | const char* dump_OMX_AUDIO_CODINGTYPE (OMX_AUDIO_CODINGTYPE coding){ 77 | switch (coding){ 78 | DUMP_CASE (OMX_AUDIO_CodingUnused) 79 | DUMP_CASE (OMX_AUDIO_CodingAutoDetect) 80 | DUMP_CASE (OMX_AUDIO_CodingPCM) 81 | DUMP_CASE (OMX_AUDIO_CodingADPCM) 82 | DUMP_CASE (OMX_AUDIO_CodingAMR) 83 | DUMP_CASE (OMX_AUDIO_CodingGSMFR) 84 | DUMP_CASE (OMX_AUDIO_CodingGSMEFR) 85 | DUMP_CASE (OMX_AUDIO_CodingGSMHR) 86 | DUMP_CASE (OMX_AUDIO_CodingPDCFR) 87 | DUMP_CASE (OMX_AUDIO_CodingPDCEFR) 88 | DUMP_CASE (OMX_AUDIO_CodingPDCHR) 89 | DUMP_CASE (OMX_AUDIO_CodingTDMAFR) 90 | DUMP_CASE (OMX_AUDIO_CodingTDMAEFR) 91 | DUMP_CASE (OMX_AUDIO_CodingQCELP8) 92 | DUMP_CASE (OMX_AUDIO_CodingQCELP13) 93 | DUMP_CASE (OMX_AUDIO_CodingEVRC) 94 | DUMP_CASE (OMX_AUDIO_CodingSMV) 95 | DUMP_CASE (OMX_AUDIO_CodingG711) 96 | DUMP_CASE (OMX_AUDIO_CodingG723) 97 | DUMP_CASE (OMX_AUDIO_CodingG726) 98 | DUMP_CASE (OMX_AUDIO_CodingG729) 99 | DUMP_CASE (OMX_AUDIO_CodingAAC) 100 | DUMP_CASE (OMX_AUDIO_CodingMP3) 101 | DUMP_CASE (OMX_AUDIO_CodingSBC) 102 | DUMP_CASE (OMX_AUDIO_CodingVORBIS) 103 | DUMP_CASE (OMX_AUDIO_CodingWMA) 104 | DUMP_CASE (OMX_AUDIO_CodingRA) 105 | DUMP_CASE (OMX_AUDIO_CodingMIDI) 106 | DUMP_CASE (OMX_AUDIO_CodingFLAC) 107 | DUMP_CASE (OMX_AUDIO_CodingDDP) 108 | DUMP_CASE (OMX_AUDIO_CodingDTS) 109 | DUMP_CASE (OMX_AUDIO_CodingWMAPRO) 110 | DUMP_CASE (OMX_AUDIO_CodingATRAC3) 111 | DUMP_CASE (OMX_AUDIO_CodingATRACX) 112 | DUMP_CASE (OMX_AUDIO_CodingATRACAAL) 113 | default: return "unknown OMX_AUDIO_CODINGTYPE"; 114 | } 115 | } 116 | 117 | const char* dump_OMX_VIDEO_CODINGTYPE (OMX_VIDEO_CODINGTYPE coding){ 118 | switch (coding){ 119 | DUMP_CASE (OMX_VIDEO_CodingUnused) 120 | DUMP_CASE (OMX_VIDEO_CodingAutoDetect) 121 | DUMP_CASE (OMX_VIDEO_CodingMPEG2) 122 | DUMP_CASE (OMX_VIDEO_CodingH263) 123 | DUMP_CASE (OMX_VIDEO_CodingMPEG4) 124 | DUMP_CASE (OMX_VIDEO_CodingWMV) 125 | DUMP_CASE (OMX_VIDEO_CodingRV) 126 | DUMP_CASE (OMX_VIDEO_CodingAVC) 127 | DUMP_CASE (OMX_VIDEO_CodingMJPEG) 128 | DUMP_CASE (OMX_VIDEO_CodingVP6) 129 | DUMP_CASE (OMX_VIDEO_CodingVP7) 130 | DUMP_CASE (OMX_VIDEO_CodingVP8) 131 | DUMP_CASE (OMX_VIDEO_CodingYUV) 132 | DUMP_CASE (OMX_VIDEO_CodingSorenson) 133 | DUMP_CASE (OMX_VIDEO_CodingTheora) 134 | DUMP_CASE (OMX_VIDEO_CodingMVC) 135 | default: return "unknown OMX_VIDEO_CODINGTYPE"; 136 | } 137 | } 138 | 139 | const char* dump_OMX_IMAGE_CODINGTYPE (OMX_IMAGE_CODINGTYPE coding){ 140 | switch (coding){ 141 | DUMP_CASE (OMX_IMAGE_CodingUnused) 142 | DUMP_CASE (OMX_IMAGE_CodingAutoDetect) 143 | DUMP_CASE (OMX_IMAGE_CodingJPEG) 144 | DUMP_CASE (OMX_IMAGE_CodingJPEG2K) 145 | DUMP_CASE (OMX_IMAGE_CodingEXIF) 146 | DUMP_CASE (OMX_IMAGE_CodingTIFF) 147 | DUMP_CASE (OMX_IMAGE_CodingGIF) 148 | DUMP_CASE (OMX_IMAGE_CodingPNG) 149 | DUMP_CASE (OMX_IMAGE_CodingLZW) 150 | DUMP_CASE (OMX_IMAGE_CodingBMP) 151 | DUMP_CASE (OMX_IMAGE_CodingTGA) 152 | DUMP_CASE (OMX_IMAGE_CodingPPM) 153 | default: return "unknown OMX_IMAGE_CODINGTYPE"; 154 | } 155 | } 156 | 157 | const char* dump_OMX_STATETYPE (OMX_STATETYPE state){ 158 | switch (state){ 159 | DUMP_CASE (OMX_StateInvalid) 160 | DUMP_CASE (OMX_StateLoaded) 161 | DUMP_CASE (OMX_StateIdle) 162 | DUMP_CASE (OMX_StateExecuting) 163 | DUMP_CASE (OMX_StatePause) 164 | DUMP_CASE (OMX_StateWaitForResources) 165 | default: return "unknown OMX_STATETYPE"; 166 | } 167 | } 168 | 169 | const char* dump_OMX_ERRORTYPE (OMX_ERRORTYPE error){ 170 | switch (error){ 171 | DUMP_CASE (OMX_ErrorNone) 172 | DUMP_CASE (OMX_ErrorInsufficientResources) 173 | DUMP_CASE (OMX_ErrorUndefined) 174 | DUMP_CASE (OMX_ErrorInvalidComponentName) 175 | DUMP_CASE (OMX_ErrorComponentNotFound) 176 | DUMP_CASE (OMX_ErrorInvalidComponent) 177 | DUMP_CASE (OMX_ErrorBadParameter) 178 | DUMP_CASE (OMX_ErrorNotImplemented) 179 | DUMP_CASE (OMX_ErrorUnderflow) 180 | DUMP_CASE (OMX_ErrorOverflow) 181 | DUMP_CASE (OMX_ErrorHardware) 182 | DUMP_CASE (OMX_ErrorInvalidState) 183 | DUMP_CASE (OMX_ErrorStreamCorrupt) 184 | DUMP_CASE (OMX_ErrorPortsNotCompatible) 185 | DUMP_CASE (OMX_ErrorResourcesLost) 186 | DUMP_CASE (OMX_ErrorNoMore) 187 | DUMP_CASE (OMX_ErrorVersionMismatch) 188 | DUMP_CASE (OMX_ErrorNotReady) 189 | DUMP_CASE (OMX_ErrorTimeout) 190 | DUMP_CASE (OMX_ErrorSameState) 191 | DUMP_CASE (OMX_ErrorResourcesPreempted) 192 | DUMP_CASE (OMX_ErrorPortUnresponsiveDuringAllocation) 193 | DUMP_CASE (OMX_ErrorPortUnresponsiveDuringDeallocation) 194 | DUMP_CASE (OMX_ErrorPortUnresponsiveDuringStop) 195 | DUMP_CASE (OMX_ErrorIncorrectStateTransition) 196 | DUMP_CASE (OMX_ErrorIncorrectStateOperation) 197 | DUMP_CASE (OMX_ErrorUnsupportedSetting) 198 | DUMP_CASE (OMX_ErrorUnsupportedIndex) 199 | DUMP_CASE (OMX_ErrorBadPortIndex) 200 | DUMP_CASE (OMX_ErrorPortUnpopulated) 201 | DUMP_CASE (OMX_ErrorComponentSuspended) 202 | DUMP_CASE (OMX_ErrorDynamicResourcesUnavailable) 203 | DUMP_CASE (OMX_ErrorMbErrorsInFrame) 204 | DUMP_CASE (OMX_ErrorFormatNotDetected) 205 | DUMP_CASE (OMX_ErrorContentPipeOpenFailed) 206 | DUMP_CASE (OMX_ErrorContentPipeCreationFailed) 207 | DUMP_CASE (OMX_ErrorSeperateTablesUsed) 208 | DUMP_CASE (OMX_ErrorTunnelingUnsupported) 209 | DUMP_CASE (OMX_ErrorDiskFull) 210 | DUMP_CASE (OMX_ErrorMaxFileSize) 211 | DUMP_CASE (OMX_ErrorDrmUnauthorised) 212 | DUMP_CASE (OMX_ErrorDrmExpired) 213 | DUMP_CASE (OMX_ErrorDrmGeneral) 214 | default: return "unknown OMX_ERRORTYPE"; 215 | } 216 | } 217 | 218 | const char* dump_OMX_EVENTTYPE (OMX_EVENTTYPE event){ 219 | switch (event){ 220 | DUMP_CASE (OMX_EventCmdComplete) 221 | DUMP_CASE (OMX_EventError) 222 | DUMP_CASE (OMX_EventMark) 223 | DUMP_CASE (OMX_EventPortSettingsChanged) 224 | DUMP_CASE (OMX_EventBufferFlag) 225 | DUMP_CASE (OMX_EventResourcesAcquired) 226 | DUMP_CASE (OMX_EventComponentResumed) 227 | DUMP_CASE (OMX_EventDynamicResourcesAvailable) 228 | DUMP_CASE (OMX_EventPortFormatDetected) 229 | DUMP_CASE (OMX_EventParamOrConfigChanged) 230 | default: return "unkonwn OMX_EVENTTYPE"; 231 | } 232 | } 233 | 234 | const char* dump_OMX_INDEXTYPE (OMX_INDEXTYPE type){ 235 | switch (type){ 236 | DUMP_CASE (OMX_IndexParamAudioInit) 237 | DUMP_CASE (OMX_IndexParamVideoInit) 238 | DUMP_CASE (OMX_IndexParamImageInit) 239 | DUMP_CASE (OMX_IndexParamOtherInit) 240 | default: return "other OMX_INDEXTYPE"; 241 | } 242 | } 243 | 244 | void dump_OMX_PARAM_PORTDEFINITIONTYPE ( 245 | OMX_PARAM_PORTDEFINITIONTYPE* port){ 246 | char domain[512]; 247 | char domain_info[512]; 248 | 249 | switch (port->eDomain){ 250 | case OMX_PortDomainAudio: 251 | strcpy (domain, "OMX_PortDomainAudio"); 252 | sprintf (domain_info, 253 | " cMIMEType: %s\n" 254 | " bFlagErrorConcealment: %s\n" 255 | " eEncoding: %s\n", 256 | port->format.video.cMIMEType, 257 | port->format.image.bFlagErrorConcealment ? "true" : "false", 258 | dump_OMX_AUDIO_CODINGTYPE ( 259 | port->format.audio.eEncoding)); 260 | break; 261 | case OMX_PortDomainVideo: 262 | strcpy (domain, "OMX_PortDomainVideo"); 263 | sprintf (domain_info, 264 | " cMIMEType: %s\n" 265 | " nFrameWidth: %d\n" 266 | " nFrameHeight: %d\n" 267 | " nStride: %d\n" 268 | " nSliceHeight: %d\n" 269 | " nBitrate: %d\n" 270 | " xFramerate: %d\n" 271 | " bFlagErrorConcealment: %s\n" 272 | " eCompressionFormat: %s\n" 273 | " eColorFormat: %s\n" 274 | , port->format.video.cMIMEType, 275 | port->format.video.nFrameWidth, 276 | port->format.video.nFrameHeight, 277 | port->format.video.nStride, 278 | port->format.video.nSliceHeight, 279 | port->format.video.nBitrate, 280 | port->format.video.xFramerate, 281 | port->format.image.bFlagErrorConcealment ? "true" : "false", 282 | dump_OMX_VIDEO_CODINGTYPE ( 283 | port->format.video.eCompressionFormat), 284 | dump_OMX_COLOR_FORMATTYPE ( 285 | port->format.video.eColorFormat)); 286 | break; 287 | case OMX_PortDomainImage: 288 | strcpy (domain, "OMX_PortDomainImage"); 289 | sprintf (domain_info, 290 | " cMIMEType: %s\n" 291 | " nFrameWidth: %d\n" 292 | " nFrameHeight: %d\n" 293 | " nStride: %d\n" 294 | " nSliceHeight: %d\n" 295 | " bFlagErrorConcealment: %s\n" 296 | " eCompressionFormat: %s\n" 297 | " eColorFormat: %s\n" 298 | , port->format.image.cMIMEType, 299 | port->format.image.nFrameWidth, 300 | port->format.image.nFrameHeight, 301 | port->format.image.nStride, 302 | port->format.image.nSliceHeight, 303 | port->format.image.bFlagErrorConcealment ? "true" : "false", 304 | dump_OMX_IMAGE_CODINGTYPE ( 305 | port->format.image.eCompressionFormat), 306 | dump_OMX_COLOR_FORMATTYPE ( 307 | port->format.image.eColorFormat)); 308 | break; 309 | case OMX_PortDomainOther: 310 | strcpy (domain, "OMX_PortDomainOther"); 311 | sprintf (domain_info, 312 | " eFormat: %s\n", 313 | dump_OMX_OTHER_FORMATTYPE ( 314 | port->format.other.eFormat)); 315 | break; 316 | default: 317 | strcpy (domain, "unknown"); 318 | strcpy (domain_info, " unknown"); 319 | break; 320 | } 321 | 322 | printf ( 323 | "nSize: %d\n" 324 | "nPortIndex: %d\n" 325 | "eDir: %s\n" 326 | "nBufferCountActual: %d\n" 327 | "nBufferCountMin: %d\n" 328 | "nBufferSize: %d\n" 329 | "bEnabled: %s\n" 330 | "bPopulated: %s\n" 331 | "eDomain: %s\n" 332 | " format:\n" 333 | "%s" 334 | "bBuffersContiguous: %s\n" 335 | "nBufferAlignment: %d\n", 336 | port->nSize, 337 | port->nPortIndex, 338 | port->eDir == OMX_DirInput ? "input" : "output", 339 | port->nBufferCountActual, 340 | port->nBufferCountMin, 341 | port->nBufferSize, 342 | port->bEnabled ? "true" : "false", 343 | port->bPopulated ? "true" : "false", 344 | domain, 345 | domain_info, 346 | port->bBuffersContiguous ? "true": "false", 347 | port->nBufferAlignment); 348 | } 349 | 350 | void dump_OMX_IMAGE_PARAM_PORTFORMATTYPE ( 351 | OMX_IMAGE_PARAM_PORTFORMATTYPE* port){ 352 | printf ( 353 | "nSize: %d\n" 354 | "nPortIndex: %d\n" 355 | "nIndex: %d\n" 356 | "eCompressionFormat: %s\n" 357 | "eColorFormat: %s\n", 358 | port->nSize, 359 | port->nPortIndex, 360 | port->nIndex, 361 | dump_OMX_IMAGE_CODINGTYPE (port->eCompressionFormat), 362 | dump_OMX_COLOR_FORMATTYPE (port->eColorFormat)); 363 | } 364 | 365 | void dump_OMX_BUFFERHEADERTYPE (OMX_BUFFERHEADERTYPE* header){ 366 | long long int timestamp = (long long int)header->nTimeStamp.nHighPart; 367 | timestamp = timestamp << 32; 368 | timestamp |= (long long int)header->nTimeStamp.nLowPart; 369 | 370 | printf ( 371 | "nSize: %d\n" 372 | "nAllocLen: %d\n" 373 | "nFilledLen: %d\n" 374 | "nOffset: %d\n" 375 | "hMarkTargetComponent: %s\n" 376 | "nTickCount: %d\n" 377 | "nTimeStamp: %lld\n" 378 | "nFlags: %X\n" 379 | "nOutputPortIndex: %d\n" 380 | "nInputPortIndex: %d\n", 381 | header->nSize, 382 | header->nAllocLen, 383 | header->nFilledLen, 384 | header->nOffset, 385 | header->hMarkTargetComponent ? "not null" : "null (no mark)", 386 | header->nTickCount, 387 | timestamp, 388 | header->nFlags, 389 | header->nOutputPortIndex, 390 | header->nInputPortIndex); 391 | } -------------------------------------------------------------------------------- /dump.h: -------------------------------------------------------------------------------- 1 | #ifndef DUMP_H 2 | #define DUMP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | const char* dump_OMX_COLOR_FORMATTYPE (OMX_COLOR_FORMATTYPE color); 9 | const char* dump_OMX_OTHER_FORMATTYPE (OMX_OTHER_FORMATTYPE format); 10 | const char* dump_OMX_AUDIO_CODINGTYPE (OMX_AUDIO_CODINGTYPE coding); 11 | const char* dump_OMX_VIDEO_CODINGTYPE (OMX_VIDEO_CODINGTYPE coding); 12 | const char* dump_OMX_IMAGE_CODINGTYPE (OMX_IMAGE_CODINGTYPE coding); 13 | const char* dump_OMX_STATETYPE (OMX_STATETYPE state); 14 | const char* dump_OMX_ERRORTYPE (OMX_ERRORTYPE error); 15 | const char* dump_OMX_EVENTTYPE (OMX_EVENTTYPE event); 16 | const char* dump_OMX_INDEXTYPE (OMX_INDEXTYPE type); 17 | void dump_OMX_PARAM_PORTDEFINITIONTYPE (OMX_PARAM_PORTDEFINITIONTYPE* port); 18 | void dump_OMX_IMAGE_PARAM_PORTFORMATTYPE (OMX_IMAGE_PARAM_PORTFORMATTYPE* port); 19 | void dump_OMX_BUFFERHEADERTYPE (OMX_BUFFERHEADERTYPE* header); 20 | 21 | #endif -------------------------------------------------------------------------------- /h264.c: -------------------------------------------------------------------------------- 1 | /* 2 | For the sake of simplicity, this example exits on error. 3 | 4 | Very quick OpenMAX IL explanation: 5 | 6 | - There are components. Each component performs an action. For example, the 7 | OMX.broadcom.camera module captures images and videos and the 8 | OMX.broadcom.image_encoder module encodes raw data from an image into multiple 9 | formats. Each component has input and output ports and receives and sends 10 | buffers with data. The main goal is to join these components to form a 11 | pipeline and do complex tasks. 12 | - There are two ways to connect components: with tunnels or manually. The 13 | non-tunneled ports need to manually allocate the buffers with 14 | OMX_AllocateBuffer() and free them with OMX_FreeBuffer(). 15 | - The components have states. 16 | - There are at least two threads: the thread that uses the application (CPU) and 17 | the thread that is used internally by OMX to execute the components (GPU). 18 | - There are two types of functions: blocking and non-blocking. The blocking 19 | functions are synchronous and the non-blocking are asynchronous. Being 20 | asynchronous means that the function returns immediately but the result is 21 | returned in a later time, so you need to wait until you receive an event. This 22 | example uses two non-blocking functions: OMX_SendCommand and 23 | OMX_FillThisBuffer. 24 | 25 | Note: The camera component has two video ports: "preview" and "video". The 26 | "preview" port must be enabled even if you're not using it (tunnel it to the 27 | null_sink component) because it is used to run AGC (automatic gain control) and 28 | AWB (auto white balance) algorithms. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "dump.h" 42 | 43 | #define OMX_INIT_STRUCTURE(x) \ 44 | memset (&(x), 0, sizeof (x)); \ 45 | (x).nSize = sizeof (x); \ 46 | (x).nVersion.nVersion = OMX_VERSION; \ 47 | (x).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ 48 | (x).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ 49 | (x).nVersion.s.nRevision = OMX_VERSION_REVISION; \ 50 | (x).nVersion.s.nStep = OMX_VERSION_STEP 51 | 52 | #define FILENAME "video.h264" 53 | 54 | #define VIDEO_FRAMERATE 30 55 | #define VIDEO_BITRATE 17000000 56 | #define VIDEO_IDR_PERIOD 0 //Disabled 57 | #define VIDEO_SEI OMX_FALSE 58 | #define VIDEO_EEDE OMX_FALSE 59 | #define VIDEO_EEDE_LOSS_RATE 0 60 | #define VIDEO_QP OMX_FALSE 61 | #define VIDEO_QP_I 0 //1 .. 51, 0 means off 62 | #define VIDEO_QP_P 0 //1 .. 51, 0 means off 63 | #define VIDEO_PROFILE OMX_VIDEO_AVCProfileHigh 64 | #define VIDEO_INLINE_HEADERS OMX_FALSE 65 | 66 | //Some settings doesn't work well 67 | #define CAM_WIDTH 1920 68 | #define CAM_HEIGHT 1080 69 | #define CAM_SHARPNESS 0 //-100 .. 100 70 | #define CAM_CONTRAST 0 //-100 .. 100 71 | #define CAM_BRIGHTNESS 50 //0 .. 100 72 | #define CAM_SATURATION 0 //-100 .. 100 73 | #define CAM_SHUTTER_SPEED_AUTO OMX_TRUE 74 | #define CAM_SHUTTER_SPEED 1.0/8.0 75 | #define CAM_ISO_AUTO OMX_TRUE 76 | #define CAM_ISO 100 //100 .. 800 77 | #define CAM_EXPOSURE OMX_ExposureControlAuto 78 | #define CAM_EXPOSURE_COMPENSATION 0 //-24 .. 24 79 | #define CAM_MIRROR OMX_MirrorNone 80 | #define CAM_ROTATION 0 //0 90 180 270 81 | #define CAM_COLOR_ENABLE OMX_FALSE 82 | #define CAM_COLOR_U 128 //0 .. 255 83 | #define CAM_COLOR_V 128 //0 .. 255 84 | #define CAM_NOISE_REDUCTION OMX_TRUE 85 | #define CAM_FRAME_STABILIZATION OMX_FALSE 86 | #define CAM_METERING OMX_MeteringModeAverage 87 | #define CAM_WHITE_BALANCE OMX_WhiteBalControlAuto 88 | //The gains are used if the white balance is set to off 89 | #define CAM_WHITE_BALANCE_RED_GAIN 1000 //0 .. 90 | #define CAM_WHITE_BALANCE_BLUE_GAIN 1000 //0 .. 91 | #define CAM_IMAGE_FILTER OMX_ImageFilterNone 92 | #define CAM_ROI_TOP 0 //0 .. 100 93 | #define CAM_ROI_LEFT 0 //0 .. 100 94 | #define CAM_ROI_WIDTH 100 //0 .. 100 95 | #define CAM_ROI_HEIGHT 100 //0 .. 100 96 | #define CAM_DRC OMX_DynRangeExpOff 97 | 98 | /* 99 | Possible values: 100 | 101 | CAM_EXPOSURE 102 | OMX_ExposureControlOff 103 | OMX_ExposureControlAuto 104 | OMX_ExposureControlNight 105 | OMX_ExposureControlBackLight 106 | OMX_ExposureControlSpotlight 107 | OMX_ExposureControlSports 108 | OMX_ExposureControlSnow 109 | OMX_ExposureControlBeach 110 | OMX_ExposureControlLargeAperture 111 | OMX_ExposureControlSmallAperture 112 | OMX_ExposureControlVeryLong 113 | OMX_ExposureControlFixedFps 114 | OMX_ExposureControlNightWithPreview 115 | OMX_ExposureControlAntishake 116 | OMX_ExposureControlFireworks 117 | 118 | CAM_IMAGE_FILTER 119 | OMX_ImageFilterNone 120 | OMX_ImageFilterEmboss 121 | OMX_ImageFilterNegative 122 | OMX_ImageFilterSketch 123 | OMX_ImageFilterOilPaint 124 | OMX_ImageFilterHatch 125 | OMX_ImageFilterGpen 126 | OMX_ImageFilterSolarize 127 | OMX_ImageFilterWatercolor 128 | OMX_ImageFilterPastel 129 | OMX_ImageFilterFilm 130 | OMX_ImageFilterBlur 131 | OMX_ImageFilterColourSwap 132 | OMX_ImageFilterWashedOut 133 | OMX_ImageFilterColourPoint 134 | OMX_ImageFilterPosterise 135 | OMX_ImageFilterColourBalance 136 | OMX_ImageFilterCartoon 137 | 138 | CAM_METERING 139 | OMX_MeteringModeAverage 140 | OMX_MeteringModeSpot 141 | OMX_MeteringModeMatrix 142 | OMX_MeteringModeBacklit 143 | 144 | CAM_MIRROR 145 | OMX_MirrorNone 146 | OMX_MirrorHorizontal 147 | OMX_MirrorVertical 148 | OMX_MirrorBoth 149 | 150 | CAM_WHITE_BALANCE 151 | OMX_WhiteBalControlOff 152 | OMX_WhiteBalControlAuto 153 | OMX_WhiteBalControlSunLight 154 | OMX_WhiteBalControlCloudy 155 | OMX_WhiteBalControlShade 156 | OMX_WhiteBalControlTungsten 157 | OMX_WhiteBalControlFluorescent 158 | OMX_WhiteBalControlIncandescent 159 | OMX_WhiteBalControlFlash 160 | OMX_WhiteBalControlHorizon 161 | 162 | CAM_DRC 163 | OMX_DynRangeExpOff 164 | OMX_DynRangeExpLow 165 | OMX_DynRangeExpMedium 166 | OMX_DynRangeExpHigh 167 | 168 | VIDEO_PROFILE 169 | OMX_VIDEO_AVCProfileHigh 170 | OMX_VIDEO_AVCProfileBaseline 171 | OMX_VIDEO_AVCProfileMain 172 | */ 173 | 174 | //Data of each component 175 | typedef struct { 176 | //The handle is obtained with OMX_GetHandle() and is used on every function 177 | //that needs to manipulate a component. It is released with OMX_FreeHandle() 178 | OMX_HANDLETYPE handle; 179 | //Bitwise OR of flags. Used for blocking the current thread and waiting an 180 | //event. Used with vcos_event_flags_get() and vcos_event_flags_set() 181 | VCOS_EVENT_FLAGS_T flags; 182 | //The fullname of the component 183 | OMX_STRING name; 184 | } component_t; 185 | 186 | //Events used with vcos_event_flags_get() and vcos_event_flags_set() 187 | typedef enum { 188 | EVENT_ERROR = 0x1, 189 | EVENT_PORT_ENABLE = 0x2, 190 | EVENT_PORT_DISABLE = 0x4, 191 | EVENT_STATE_SET = 0x8, 192 | EVENT_FLUSH = 0x10, 193 | EVENT_MARK_BUFFER = 0x20, 194 | EVENT_MARK = 0x40, 195 | EVENT_PORT_SETTINGS_CHANGED = 0x80, 196 | EVENT_PARAM_OR_CONFIG_CHANGED = 0x100, 197 | EVENT_BUFFER_FLAG = 0x200, 198 | EVENT_RESOURCES_ACQUIRED = 0x400, 199 | EVENT_DYNAMIC_RESOURCES_AVAILABLE = 0x800, 200 | EVENT_FILL_BUFFER_DONE = 0x1000, 201 | EVENT_EMPTY_BUFFER_DONE = 0x2000, 202 | } component_event; 203 | 204 | //Prototypes 205 | OMX_ERRORTYPE event_handler ( 206 | OMX_IN OMX_HANDLETYPE comp, 207 | OMX_IN OMX_PTR app_data, 208 | OMX_IN OMX_EVENTTYPE event, 209 | OMX_IN OMX_U32 data1, 210 | OMX_IN OMX_U32 data2, 211 | OMX_IN OMX_PTR event_data); 212 | OMX_ERRORTYPE fill_buffer_done ( 213 | OMX_IN OMX_HANDLETYPE comp, 214 | OMX_IN OMX_PTR app_data, 215 | OMX_IN OMX_BUFFERHEADERTYPE* buffer); 216 | void wake (component_t* component, VCOS_UNSIGNED event); 217 | void wait ( 218 | component_t* component, 219 | VCOS_UNSIGNED events, 220 | VCOS_UNSIGNED* retrieved_events); 221 | void init_component (component_t* component); 222 | void deinit_component (component_t* component); 223 | void load_camera_drivers (component_t* component); 224 | void change_state (component_t* component, OMX_STATETYPE state); 225 | void enable_port (component_t* component, OMX_U32 port); 226 | void disable_port (component_t* component, OMX_U32 port); 227 | void enable_encoder_output_port ( 228 | component_t* encoder, 229 | OMX_BUFFERHEADERTYPE** encoder_output_buffer); 230 | void disable_encoder_output_port ( 231 | component_t* encoder, 232 | OMX_BUFFERHEADERTYPE* encoder_output_buffer); 233 | void set_camera_settings (component_t* camera); 234 | void set_h264_settings (component_t* encoder); 235 | 236 | //Function that is called when a component receives an event from a secondary 237 | //thread 238 | OMX_ERRORTYPE event_handler ( 239 | OMX_IN OMX_HANDLETYPE comp, 240 | OMX_IN OMX_PTR app_data, 241 | OMX_IN OMX_EVENTTYPE event, 242 | OMX_IN OMX_U32 data1, 243 | OMX_IN OMX_U32 data2, 244 | OMX_IN OMX_PTR event_data){ 245 | component_t* component = (component_t*)app_data; 246 | 247 | switch (event){ 248 | case OMX_EventCmdComplete: 249 | switch (data1){ 250 | case OMX_CommandStateSet: 251 | printf ("event: %s, OMX_CommandStateSet, state: %s\n", 252 | component->name, dump_OMX_STATETYPE (data2)); 253 | wake (component, EVENT_STATE_SET); 254 | break; 255 | case OMX_CommandPortDisable: 256 | printf ("event: %s, OMX_CommandPortDisable, port: %d\n", 257 | component->name, data2); 258 | wake (component, EVENT_PORT_DISABLE); 259 | break; 260 | case OMX_CommandPortEnable: 261 | printf ("event: %s, OMX_CommandPortEnable, port: %d\n", 262 | component->name, data2); 263 | wake (component, EVENT_PORT_ENABLE); 264 | break; 265 | case OMX_CommandFlush: 266 | printf ("event: %s, OMX_CommandFlush, port: %d\n", 267 | component->name, data2); 268 | wake (component, EVENT_FLUSH); 269 | break; 270 | case OMX_CommandMarkBuffer: 271 | printf ("event: %s, OMX_CommandMarkBuffer, port: %d\n", 272 | component->name, data2); 273 | wake (component, EVENT_MARK_BUFFER); 274 | break; 275 | } 276 | break; 277 | case OMX_EventError: 278 | printf ("event: %s, %s\n", component->name, dump_OMX_ERRORTYPE (data1)); 279 | wake (component, EVENT_ERROR); 280 | break; 281 | case OMX_EventMark: 282 | printf ("event: %s, OMX_EventMark\n", component->name); 283 | wake (component, EVENT_MARK); 284 | break; 285 | case OMX_EventPortSettingsChanged: 286 | printf ("event: %s, OMX_EventPortSettingsChanged, port: %d\n", 287 | component->name, data1); 288 | wake (component, EVENT_PORT_SETTINGS_CHANGED); 289 | break; 290 | case OMX_EventParamOrConfigChanged: 291 | printf ("event: %s, OMX_EventParamOrConfigChanged, data1: %d, data2: " 292 | "%X\n", component->name, data1, data2); 293 | wake (component, EVENT_PARAM_OR_CONFIG_CHANGED); 294 | break; 295 | case OMX_EventBufferFlag: 296 | printf ("event: %s, OMX_EventBufferFlag, port: %d\n", 297 | component->name, data1); 298 | wake (component, EVENT_BUFFER_FLAG); 299 | break; 300 | case OMX_EventResourcesAcquired: 301 | printf ("event: %s, OMX_EventResourcesAcquired\n", component->name); 302 | wake (component, EVENT_RESOURCES_ACQUIRED); 303 | break; 304 | case OMX_EventDynamicResourcesAvailable: 305 | printf ("event: %s, OMX_EventDynamicResourcesAvailable\n", 306 | component->name); 307 | wake (component, EVENT_DYNAMIC_RESOURCES_AVAILABLE); 308 | break; 309 | default: 310 | //This should never execute, just ignore 311 | printf ("event: unknown (%X)\n", event); 312 | break; 313 | } 314 | 315 | return OMX_ErrorNone; 316 | } 317 | 318 | //Function that is called when a component fills a buffer with data 319 | OMX_ERRORTYPE fill_buffer_done ( 320 | OMX_IN OMX_HANDLETYPE comp, 321 | OMX_IN OMX_PTR app_data, 322 | OMX_IN OMX_BUFFERHEADERTYPE* buffer){ 323 | component_t* component = (component_t*)app_data; 324 | 325 | printf ("event: %s, fill_buffer_done\n", component->name); 326 | wake (component, EVENT_FILL_BUFFER_DONE); 327 | 328 | return OMX_ErrorNone; 329 | } 330 | 331 | void wake (component_t* component, VCOS_UNSIGNED event){ 332 | vcos_event_flags_set (&component->flags, event, VCOS_OR); 333 | } 334 | 335 | void wait ( 336 | component_t* component, 337 | VCOS_UNSIGNED events, 338 | VCOS_UNSIGNED* retrieved_events){ 339 | VCOS_UNSIGNED set; 340 | if (vcos_event_flags_get (&component->flags, events | EVENT_ERROR, 341 | VCOS_OR_CONSUME, VCOS_SUSPEND, &set)){ 342 | fprintf (stderr, "error: vcos_event_flags_get\n"); 343 | exit (1); 344 | } 345 | if (set == EVENT_ERROR){ 346 | exit (1); 347 | } 348 | if (retrieved_events){ 349 | *retrieved_events = set; 350 | } 351 | } 352 | 353 | void init_component (component_t* component){ 354 | printf ("initializing component %s\n", component->name); 355 | 356 | OMX_ERRORTYPE error; 357 | 358 | //Create the event flags 359 | if (vcos_event_flags_create (&component->flags, "component")){ 360 | fprintf (stderr, "error: vcos_event_flags_create\n"); 361 | exit (1); 362 | } 363 | 364 | //Each component has an event_handler and fill_buffer_done functions 365 | OMX_CALLBACKTYPE callbacks_st; 366 | callbacks_st.EventHandler = event_handler; 367 | callbacks_st.FillBufferDone = fill_buffer_done; 368 | 369 | //Get the handle 370 | if ((error = OMX_GetHandle (&component->handle, component->name, component, 371 | &callbacks_st))){ 372 | fprintf (stderr, "error: OMX_GetHandle: %s\n", dump_OMX_ERRORTYPE (error)); 373 | exit (1); 374 | } 375 | 376 | //Disable all the ports 377 | OMX_INDEXTYPE types[] = { 378 | OMX_IndexParamAudioInit, 379 | OMX_IndexParamVideoInit, 380 | OMX_IndexParamImageInit, 381 | OMX_IndexParamOtherInit 382 | }; 383 | OMX_PORT_PARAM_TYPE ports_st; 384 | OMX_INIT_STRUCTURE (ports_st); 385 | 386 | int i; 387 | for (i=0; i<4; i++){ 388 | if ((error = OMX_GetParameter (component->handle, types[i], &ports_st))){ 389 | fprintf (stderr, "error: OMX_GetParameter: %s\n", 390 | dump_OMX_ERRORTYPE (error)); 391 | exit (1); 392 | } 393 | 394 | OMX_U32 port; 395 | for (port=ports_st.nStartPortNumber; 396 | portname); 407 | 408 | OMX_ERRORTYPE error; 409 | 410 | vcos_event_flags_delete (&component->flags); 411 | 412 | if ((error = OMX_FreeHandle (component->handle))){ 413 | fprintf (stderr, "error: OMX_FreeHandle: %s\n", dump_OMX_ERRORTYPE (error)); 414 | exit (1); 415 | } 416 | } 417 | 418 | void load_camera_drivers (component_t* component){ 419 | /* 420 | This is a specific behaviour of the Broadcom's Raspberry Pi OpenMAX IL 421 | implementation module because the OMX_SetConfig() and OMX_SetParameter() are 422 | blocking functions but the drivers are loaded asynchronously, that is, an 423 | event is fired to signal the completion. Basically, what you're saying is: 424 | 425 | "When the parameter with index OMX_IndexParamCameraDeviceNumber is set, load 426 | the camera drivers and emit an OMX_EventParamOrConfigChanged event" 427 | 428 | The red LED of the camera will be turned on after this call. 429 | */ 430 | 431 | printf ("loading camera drivers\n"); 432 | 433 | OMX_ERRORTYPE error; 434 | 435 | OMX_CONFIG_REQUESTCALLBACKTYPE cbs_st; 436 | OMX_INIT_STRUCTURE (cbs_st); 437 | cbs_st.nPortIndex = OMX_ALL; 438 | cbs_st.nIndex = OMX_IndexParamCameraDeviceNumber; 439 | cbs_st.bEnable = OMX_TRUE; 440 | if ((error = OMX_SetConfig (component->handle, OMX_IndexConfigRequestCallback, 441 | &cbs_st))){ 442 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 443 | exit (1); 444 | } 445 | 446 | OMX_PARAM_U32TYPE dev_st; 447 | OMX_INIT_STRUCTURE (dev_st); 448 | dev_st.nPortIndex = OMX_ALL; 449 | //ID for the camera device 450 | dev_st.nU32 = 0; 451 | if ((error = OMX_SetParameter (component->handle, 452 | OMX_IndexParamCameraDeviceNumber, &dev_st))){ 453 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 454 | dump_OMX_ERRORTYPE (error)); 455 | exit (1); 456 | } 457 | 458 | wait (component, EVENT_PARAM_OR_CONFIG_CHANGED, 0); 459 | } 460 | 461 | void change_state (component_t* component, OMX_STATETYPE state){ 462 | printf ("changing %s state to %s\n", component->name, 463 | dump_OMX_STATETYPE (state)); 464 | 465 | OMX_ERRORTYPE error; 466 | 467 | if ((error = OMX_SendCommand (component->handle, OMX_CommandStateSet, state, 468 | 0))){ 469 | fprintf (stderr, "error: OMX_SendCommand: %s\n", 470 | dump_OMX_ERRORTYPE (error)); 471 | exit (1); 472 | } 473 | } 474 | 475 | void enable_port (component_t* component, OMX_U32 port){ 476 | printf ("enabling port %d (%s)\n", port, component->name); 477 | 478 | OMX_ERRORTYPE error; 479 | 480 | if ((error = OMX_SendCommand (component->handle, OMX_CommandPortEnable, 481 | port, 0))){ 482 | fprintf (stderr, "error: OMX_SendCommand: %s\n", 483 | dump_OMX_ERRORTYPE (error)); 484 | exit (1); 485 | } 486 | } 487 | 488 | void disable_port (component_t* component, OMX_U32 port){ 489 | printf ("disabling port %d (%s)\n", port, component->name); 490 | 491 | OMX_ERRORTYPE error; 492 | 493 | if ((error = OMX_SendCommand (component->handle, OMX_CommandPortDisable, 494 | port, 0))){ 495 | fprintf (stderr, "error: OMX_SendCommand: %s\n", 496 | dump_OMX_ERRORTYPE (error)); 497 | exit (1); 498 | } 499 | } 500 | 501 | void enable_encoder_output_port ( 502 | component_t* encoder, 503 | OMX_BUFFERHEADERTYPE** encoder_output_buffer){ 504 | //The port is not enabled until the buffer is allocated 505 | OMX_ERRORTYPE error; 506 | 507 | enable_port (encoder, 201); 508 | 509 | OMX_PARAM_PORTDEFINITIONTYPE port_st; 510 | OMX_INIT_STRUCTURE (port_st); 511 | port_st.nPortIndex = 201; 512 | if ((error = OMX_GetParameter (encoder->handle, OMX_IndexParamPortDefinition, 513 | &port_st))){ 514 | fprintf (stderr, "error: OMX_GetParameter: %s\n", 515 | dump_OMX_ERRORTYPE (error)); 516 | exit (1); 517 | } 518 | printf ("allocating %s output buffer\n", encoder->name); 519 | if ((error = OMX_AllocateBuffer (encoder->handle, encoder_output_buffer, 201, 520 | 0, port_st.nBufferSize))){ 521 | fprintf (stderr, "error: OMX_AllocateBuffer: %s\n", 522 | dump_OMX_ERRORTYPE (error)); 523 | exit (1); 524 | } 525 | 526 | wait (encoder, EVENT_PORT_ENABLE, 0); 527 | } 528 | 529 | void disable_encoder_output_port ( 530 | component_t* encoder, 531 | OMX_BUFFERHEADERTYPE* encoder_output_buffer){ 532 | //The port is not disabled until the buffer is released 533 | OMX_ERRORTYPE error; 534 | 535 | disable_port (encoder, 201); 536 | 537 | //Free encoder output buffer 538 | printf ("releasing %s output buffer\n", encoder->name); 539 | if ((error = OMX_FreeBuffer (encoder->handle, 201, encoder_output_buffer))){ 540 | fprintf (stderr, "error: OMX_FreeBuffer: %s\n", dump_OMX_ERRORTYPE (error)); 541 | exit (1); 542 | } 543 | 544 | wait (encoder, EVENT_PORT_DISABLE, 0); 545 | } 546 | 547 | void set_camera_settings (component_t* camera){ 548 | printf ("configuring '%s' settings\n", camera->name); 549 | 550 | OMX_ERRORTYPE error; 551 | 552 | //Sharpness 553 | OMX_CONFIG_SHARPNESSTYPE sharpness_st; 554 | OMX_INIT_STRUCTURE (sharpness_st); 555 | sharpness_st.nPortIndex = OMX_ALL; 556 | sharpness_st.nSharpness = CAM_SHARPNESS; 557 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonSharpness, 558 | &sharpness_st))){ 559 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 560 | exit (1); 561 | } 562 | 563 | //Contrast 564 | OMX_CONFIG_CONTRASTTYPE contrast_st; 565 | OMX_INIT_STRUCTURE (contrast_st); 566 | contrast_st.nPortIndex = OMX_ALL; 567 | contrast_st.nContrast = CAM_CONTRAST; 568 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonContrast, 569 | &contrast_st))){ 570 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 571 | exit (1); 572 | } 573 | 574 | //Saturation 575 | OMX_CONFIG_SATURATIONTYPE saturation_st; 576 | OMX_INIT_STRUCTURE (saturation_st); 577 | saturation_st.nPortIndex = OMX_ALL; 578 | saturation_st.nSaturation = CAM_SATURATION; 579 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonSaturation, 580 | &saturation_st))){ 581 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 582 | exit (1); 583 | } 584 | 585 | //Brightness 586 | OMX_CONFIG_BRIGHTNESSTYPE brightness_st; 587 | OMX_INIT_STRUCTURE (brightness_st); 588 | brightness_st.nPortIndex = OMX_ALL; 589 | brightness_st.nBrightness = CAM_BRIGHTNESS; 590 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonBrightness, 591 | &brightness_st))){ 592 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 593 | exit (1); 594 | } 595 | 596 | //Exposure value 597 | OMX_CONFIG_EXPOSUREVALUETYPE exposure_value_st; 598 | OMX_INIT_STRUCTURE (exposure_value_st); 599 | exposure_value_st.nPortIndex = OMX_ALL; 600 | exposure_value_st.eMetering = CAM_METERING; 601 | exposure_value_st.xEVCompensation = 602 | (OMX_S32)((CAM_EXPOSURE_COMPENSATION<<16)/6.0); 603 | exposure_value_st.nShutterSpeedMsec = (OMX_U32)((CAM_SHUTTER_SPEED)*1e6); 604 | exposure_value_st.bAutoShutterSpeed = CAM_SHUTTER_SPEED_AUTO; 605 | exposure_value_st.nSensitivity = CAM_ISO; 606 | exposure_value_st.bAutoSensitivity = CAM_ISO_AUTO; 607 | if ((error = OMX_SetConfig (camera->handle, 608 | OMX_IndexConfigCommonExposureValue, &exposure_value_st))){ 609 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 610 | exit (1); 611 | } 612 | 613 | //Exposure control 614 | OMX_CONFIG_EXPOSURECONTROLTYPE exposure_control_st; 615 | OMX_INIT_STRUCTURE (exposure_control_st); 616 | exposure_control_st.nPortIndex = OMX_ALL; 617 | exposure_control_st.eExposureControl = CAM_EXPOSURE; 618 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonExposure, 619 | &exposure_control_st))){ 620 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 621 | exit (1); 622 | } 623 | 624 | //Frame stabilisation 625 | OMX_CONFIG_FRAMESTABTYPE frame_stabilisation_st; 626 | OMX_INIT_STRUCTURE (frame_stabilisation_st); 627 | frame_stabilisation_st.nPortIndex = OMX_ALL; 628 | frame_stabilisation_st.bStab = CAM_FRAME_STABILIZATION; 629 | if ((error = OMX_SetConfig (camera->handle, 630 | OMX_IndexConfigCommonFrameStabilisation, &frame_stabilisation_st))){ 631 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 632 | exit (1); 633 | } 634 | 635 | //White balance 636 | OMX_CONFIG_WHITEBALCONTROLTYPE white_balance_st; 637 | OMX_INIT_STRUCTURE (white_balance_st); 638 | white_balance_st.nPortIndex = OMX_ALL; 639 | white_balance_st.eWhiteBalControl = CAM_WHITE_BALANCE; 640 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonWhiteBalance, 641 | &white_balance_st))){ 642 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 643 | exit (1); 644 | } 645 | 646 | //White balance gains (if white balance is set to off) 647 | if (!CAM_WHITE_BALANCE){ 648 | OMX_CONFIG_CUSTOMAWBGAINSTYPE white_balance_gains_st; 649 | OMX_INIT_STRUCTURE (white_balance_gains_st); 650 | white_balance_gains_st.xGainR = (CAM_WHITE_BALANCE_RED_GAIN << 16)/1000; 651 | white_balance_gains_st.xGainB = (CAM_WHITE_BALANCE_BLUE_GAIN << 16)/1000; 652 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCustomAwbGains, 653 | &white_balance_gains_st))){ 654 | fprintf (stderr, "error: OMX_SetConfig: %s\n", 655 | dump_OMX_ERRORTYPE (error)); 656 | exit (1); 657 | } 658 | } 659 | 660 | //Image filter 661 | OMX_CONFIG_IMAGEFILTERTYPE image_filter_st; 662 | OMX_INIT_STRUCTURE (image_filter_st); 663 | image_filter_st.nPortIndex = OMX_ALL; 664 | image_filter_st.eImageFilter = CAM_IMAGE_FILTER; 665 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonImageFilter, 666 | &image_filter_st))){ 667 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 668 | exit (1); 669 | } 670 | 671 | //Mirror 672 | OMX_CONFIG_MIRRORTYPE mirror_st; 673 | OMX_INIT_STRUCTURE (mirror_st); 674 | mirror_st.nPortIndex = 71; 675 | mirror_st.eMirror = CAM_MIRROR; 676 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonMirror, 677 | &mirror_st))){ 678 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 679 | exit (1); 680 | } 681 | 682 | //Rotation 683 | OMX_CONFIG_ROTATIONTYPE rotation_st; 684 | OMX_INIT_STRUCTURE (rotation_st); 685 | rotation_st.nPortIndex = 71; 686 | rotation_st.nRotation = CAM_ROTATION; 687 | if ((error = OMX_SetConfig (camera->handle, OMX_IndexConfigCommonRotate, 688 | &rotation_st))){ 689 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 690 | exit (1); 691 | } 692 | 693 | //Color enhancement 694 | OMX_CONFIG_COLORENHANCEMENTTYPE color_enhancement_st; 695 | OMX_INIT_STRUCTURE (color_enhancement_st); 696 | color_enhancement_st.nPortIndex = OMX_ALL; 697 | color_enhancement_st.bColorEnhancement = CAM_COLOR_ENABLE; 698 | color_enhancement_st.nCustomizedU = CAM_COLOR_U; 699 | color_enhancement_st.nCustomizedV = CAM_COLOR_V; 700 | if ((error = OMX_SetConfig (camera->handle, 701 | OMX_IndexConfigCommonColorEnhancement, &color_enhancement_st))){ 702 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 703 | exit (1); 704 | } 705 | 706 | //Denoise 707 | OMX_CONFIG_BOOLEANTYPE denoise_st; 708 | OMX_INIT_STRUCTURE (denoise_st); 709 | denoise_st.bEnabled = CAM_NOISE_REDUCTION; 710 | if ((error = OMX_SetConfig (camera->handle, 711 | OMX_IndexConfigStillColourDenoiseEnable, &denoise_st))){ 712 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 713 | exit (1); 714 | } 715 | 716 | //ROI 717 | OMX_CONFIG_INPUTCROPTYPE roi_st; 718 | OMX_INIT_STRUCTURE (roi_st); 719 | roi_st.nPortIndex = OMX_ALL; 720 | roi_st.xLeft = (CAM_ROI_LEFT << 16)/100; 721 | roi_st.xTop = (CAM_ROI_TOP << 16)/100; 722 | roi_st.xWidth = (CAM_ROI_WIDTH << 16)/100; 723 | roi_st.xHeight = (CAM_ROI_HEIGHT << 16)/100; 724 | if ((error = OMX_SetConfig (camera->handle, 725 | OMX_IndexConfigInputCropPercentages, &roi_st))){ 726 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 727 | exit (1); 728 | } 729 | 730 | //DRC 731 | OMX_CONFIG_DYNAMICRANGEEXPANSIONTYPE drc_st; 732 | OMX_INIT_STRUCTURE (drc_st); 733 | drc_st.eMode = CAM_DRC; 734 | if ((error = OMX_SetConfig (camera->handle, 735 | OMX_IndexConfigDynamicRangeExpansion, &drc_st))){ 736 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 737 | exit (1); 738 | } 739 | } 740 | 741 | void set_h264_settings (component_t* encoder){ 742 | printf ("configuring '%s' settings\n", encoder->name); 743 | 744 | OMX_ERRORTYPE error; 745 | 746 | if (!VIDEO_QP){ 747 | //Bitrate 748 | OMX_VIDEO_PARAM_BITRATETYPE bitrate_st; 749 | OMX_INIT_STRUCTURE (bitrate_st); 750 | bitrate_st.eControlRate = OMX_Video_ControlRateVariable; 751 | bitrate_st.nTargetBitrate = VIDEO_BITRATE; 752 | bitrate_st.nPortIndex = 201; 753 | if ((error = OMX_SetParameter (encoder->handle, OMX_IndexParamVideoBitrate, 754 | &bitrate_st))){ 755 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 756 | dump_OMX_ERRORTYPE (error)); 757 | exit (1); 758 | } 759 | }else{ 760 | //Quantization parameters 761 | OMX_VIDEO_PARAM_QUANTIZATIONTYPE quantization_st; 762 | OMX_INIT_STRUCTURE (quantization_st); 763 | quantization_st.nPortIndex = 201; 764 | //nQpB returns an error, it cannot be modified 765 | quantization_st.nQpI = VIDEO_QP_I; 766 | quantization_st.nQpP = VIDEO_QP_P; 767 | if ((error = OMX_SetParameter (encoder->handle, 768 | OMX_IndexParamVideoQuantization, &quantization_st))){ 769 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 770 | dump_OMX_ERRORTYPE (error)); 771 | exit (1); 772 | } 773 | } 774 | 775 | //Codec 776 | OMX_VIDEO_PARAM_PORTFORMATTYPE format_st; 777 | OMX_INIT_STRUCTURE (format_st); 778 | format_st.nPortIndex = 201; 779 | //H.264/AVC 780 | format_st.eCompressionFormat = OMX_VIDEO_CodingAVC; 781 | if ((error = OMX_SetParameter (encoder->handle, OMX_IndexParamVideoPortFormat, 782 | &format_st))){ 783 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 784 | dump_OMX_ERRORTYPE (error)); 785 | exit (1); 786 | } 787 | 788 | //IDR period 789 | OMX_VIDEO_CONFIG_AVCINTRAPERIOD idr_st; 790 | OMX_INIT_STRUCTURE (idr_st); 791 | idr_st.nPortIndex = 201; 792 | if ((error = OMX_GetConfig (encoder->handle, 793 | OMX_IndexConfigVideoAVCIntraPeriod, &idr_st))){ 794 | fprintf (stderr, "error: OMX_GetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 795 | exit (1); 796 | } 797 | idr_st.nIDRPeriod = VIDEO_IDR_PERIOD; 798 | if ((error = OMX_SetConfig (encoder->handle, 799 | OMX_IndexConfigVideoAVCIntraPeriod, &idr_st))){ 800 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 801 | exit (1); 802 | } 803 | 804 | //SEI 805 | OMX_PARAM_BRCMVIDEOAVCSEIENABLETYPE sei_st; 806 | OMX_INIT_STRUCTURE (sei_st); 807 | sei_st.nPortIndex = 201; 808 | sei_st.bEnable = VIDEO_SEI; 809 | if ((error = OMX_SetParameter (encoder->handle, 810 | OMX_IndexParamBrcmVideoAVCSEIEnable, &sei_st))){ 811 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 812 | dump_OMX_ERRORTYPE (error)); 813 | exit (1); 814 | } 815 | 816 | //EEDE 817 | OMX_VIDEO_EEDE_ENABLE eede_st; 818 | OMX_INIT_STRUCTURE (eede_st); 819 | eede_st.nPortIndex = 201; 820 | eede_st.enable = VIDEO_EEDE; 821 | if ((error = OMX_SetParameter (encoder->handle, OMX_IndexParamBrcmEEDEEnable, 822 | &eede_st))){ 823 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 824 | dump_OMX_ERRORTYPE (error)); 825 | exit (1); 826 | } 827 | 828 | OMX_VIDEO_EEDE_LOSSRATE eede_loss_rate_st; 829 | OMX_INIT_STRUCTURE (eede_loss_rate_st); 830 | eede_loss_rate_st.nPortIndex = 201; 831 | eede_loss_rate_st.loss_rate = VIDEO_EEDE_LOSS_RATE; 832 | if ((error = OMX_SetParameter (encoder->handle, 833 | OMX_IndexParamBrcmEEDELossRate, &eede_loss_rate_st))){ 834 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 835 | dump_OMX_ERRORTYPE (error)); 836 | exit (1); 837 | } 838 | 839 | //AVC Profile 840 | OMX_VIDEO_PARAM_AVCTYPE avc_st; 841 | OMX_INIT_STRUCTURE (avc_st); 842 | avc_st.nPortIndex = 201; 843 | if ((error = OMX_GetParameter (encoder->handle, 844 | OMX_IndexParamVideoAvc, &avc_st))){ 845 | fprintf (stderr, "error: OMX_GetParameter: %s\n", 846 | dump_OMX_ERRORTYPE (error)); 847 | exit (1); 848 | } 849 | avc_st.eProfile = VIDEO_PROFILE; 850 | if ((error = OMX_SetParameter (encoder->handle, 851 | OMX_IndexParamVideoAvc, &avc_st))){ 852 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 853 | dump_OMX_ERRORTYPE (error)); 854 | exit (1); 855 | } 856 | 857 | //Inline SPS/PPS 858 | OMX_CONFIG_PORTBOOLEANTYPE headers_st; 859 | OMX_INIT_STRUCTURE (headers_st); 860 | headers_st.nPortIndex = 201; 861 | headers_st.bEnabled = VIDEO_INLINE_HEADERS; 862 | if ((error = OMX_SetParameter (encoder->handle, 863 | OMX_IndexParamBrcmVideoAVCInlineHeaderEnable, &headers_st))){ 864 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 865 | dump_OMX_ERRORTYPE (error)); 866 | exit (1); 867 | } 868 | 869 | //Note: Motion vectors are not implemented in this program. 870 | //See for further details 871 | //https://github.com/gagle/raspberrypi-omxcam/blob/master/src/h264.c 872 | //https://github.com/gagle/raspberrypi-omxcam/blob/master/src/video.c 873 | } 874 | 875 | int main (){ 876 | OMX_ERRORTYPE error; 877 | OMX_BUFFERHEADERTYPE* encoder_output_buffer; 878 | component_t camera; 879 | component_t encoder; 880 | component_t null_sink; 881 | camera.name = "OMX.broadcom.camera"; 882 | encoder.name = "OMX.broadcom.video_encode"; 883 | null_sink.name = "OMX.broadcom.null_sink"; 884 | 885 | //Open the file 886 | int fd = open (FILENAME, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666); 887 | if (fd == -1){ 888 | fprintf (stderr, "error: open\n"); 889 | exit (1); 890 | } 891 | 892 | //Initialize Broadcom's VideoCore APIs 893 | bcm_host_init (); 894 | 895 | //Initialize OpenMAX IL 896 | if ((error = OMX_Init ())){ 897 | fprintf (stderr, "error: OMX_Init: %s\n", dump_OMX_ERRORTYPE (error)); 898 | exit (1); 899 | } 900 | 901 | //Initialize components 902 | init_component (&camera); 903 | init_component (&encoder); 904 | init_component (&null_sink); 905 | 906 | //Initialize camera drivers 907 | load_camera_drivers (&camera); 908 | 909 | //Configure camera port definition 910 | printf ("configuring %s port definition\n", camera.name); 911 | OMX_PARAM_PORTDEFINITIONTYPE port_st; 912 | OMX_INIT_STRUCTURE (port_st); 913 | port_st.nPortIndex = 71; 914 | if ((error = OMX_GetParameter (camera.handle, OMX_IndexParamPortDefinition, 915 | &port_st))){ 916 | fprintf (stderr, "error: OMX_GetParameter: %s\n", 917 | dump_OMX_ERRORTYPE (error)); 918 | exit (1); 919 | } 920 | 921 | port_st.format.video.nFrameWidth = CAM_WIDTH; 922 | port_st.format.video.nFrameHeight = CAM_HEIGHT; 923 | port_st.format.video.nStride = CAM_WIDTH; 924 | port_st.format.video.xFramerate = VIDEO_FRAMERATE << 16; 925 | port_st.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; 926 | port_st.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar; 927 | if ((error = OMX_SetParameter (camera.handle, OMX_IndexParamPortDefinition, 928 | &port_st))){ 929 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 930 | dump_OMX_ERRORTYPE (error)); 931 | exit (1); 932 | } 933 | 934 | //Preview port 935 | port_st.nPortIndex = 70; 936 | if ((error = OMX_SetParameter (camera.handle, OMX_IndexParamPortDefinition, 937 | &port_st))){ 938 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 939 | dump_OMX_ERRORTYPE (error)); 940 | exit (1); 941 | } 942 | 943 | printf ("configuring %s framerate\n", camera.name); 944 | OMX_CONFIG_FRAMERATETYPE framerate_st; 945 | OMX_INIT_STRUCTURE (framerate_st); 946 | framerate_st.nPortIndex = 71; 947 | framerate_st.xEncodeFramerate = port_st.format.video.xFramerate; 948 | if ((error = OMX_SetConfig (camera.handle, OMX_IndexConfigVideoFramerate, 949 | &framerate_st))){ 950 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 951 | exit (1); 952 | } 953 | 954 | //Preview port 955 | framerate_st.nPortIndex = 70; 956 | if ((error = OMX_SetConfig (camera.handle, OMX_IndexConfigVideoFramerate, 957 | &framerate_st))){ 958 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 959 | exit (1); 960 | } 961 | 962 | //Configure camera settings 963 | set_camera_settings (&camera); 964 | 965 | //Configure encoder port definition 966 | printf ("configuring %s port definition\n", encoder.name); 967 | OMX_INIT_STRUCTURE (port_st); 968 | port_st.nPortIndex = 201; 969 | if ((error = OMX_GetParameter (encoder.handle, OMX_IndexParamPortDefinition, 970 | &port_st))){ 971 | fprintf (stderr, "error: OMX_GetParameter: %s\n", 972 | dump_OMX_ERRORTYPE (error)); 973 | exit (1); 974 | } 975 | port_st.format.video.nFrameWidth = CAM_WIDTH; 976 | port_st.format.video.nFrameHeight = CAM_HEIGHT; 977 | port_st.format.video.nStride = CAM_WIDTH; 978 | port_st.format.video.xFramerate = VIDEO_FRAMERATE << 16; 979 | //Despite being configured later, these two fields need to be set 980 | port_st.format.video.nBitrate = VIDEO_QP ? 0 : VIDEO_BITRATE; 981 | port_st.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; 982 | if ((error = OMX_SetParameter (encoder.handle, OMX_IndexParamPortDefinition, 983 | &port_st))){ 984 | fprintf (stderr, "error: OMX_SetParameter: %s\n", 985 | dump_OMX_ERRORTYPE (error)); 986 | exit (1); 987 | } 988 | 989 | //Configure H264 990 | set_h264_settings (&encoder); 991 | 992 | //Setup tunnels: camera (video) -> video_encode, camera (preview) -> null_sink 993 | printf ("configuring tunnels\n"); 994 | if ((error = OMX_SetupTunnel (camera.handle, 71, encoder.handle, 200))){ 995 | fprintf (stderr, "error: OMX_SetupTunnel: %s\n", 996 | dump_OMX_ERRORTYPE (error)); 997 | exit (1); 998 | } 999 | if ((error = OMX_SetupTunnel (camera.handle, 70, null_sink.handle, 240))){ 1000 | fprintf (stderr, "error: OMX_SetupTunnel: %s\n", 1001 | dump_OMX_ERRORTYPE (error)); 1002 | exit (1); 1003 | } 1004 | 1005 | //Change state to IDLE 1006 | change_state (&camera, OMX_StateIdle); 1007 | wait (&camera, EVENT_STATE_SET, 0); 1008 | change_state (&encoder, OMX_StateIdle); 1009 | wait (&encoder, EVENT_STATE_SET, 0); 1010 | change_state (&null_sink, OMX_StateIdle); 1011 | wait (&null_sink, EVENT_STATE_SET, 0); 1012 | 1013 | //Enable the ports 1014 | enable_port (&camera, 71); 1015 | wait (&camera, EVENT_PORT_ENABLE, 0); 1016 | enable_port (&camera, 70); 1017 | wait (&camera, EVENT_PORT_ENABLE, 0); 1018 | enable_port (&null_sink, 240); 1019 | wait (&null_sink, EVENT_PORT_ENABLE, 0); 1020 | enable_port (&encoder, 200); 1021 | wait (&encoder, EVENT_PORT_ENABLE, 0); 1022 | enable_encoder_output_port (&encoder, &encoder_output_buffer); 1023 | 1024 | //Change state to EXECUTING 1025 | change_state (&camera, OMX_StateExecuting); 1026 | wait (&camera, EVENT_STATE_SET, 0); 1027 | change_state (&encoder, OMX_StateExecuting); 1028 | wait (&encoder, EVENT_STATE_SET, 0); 1029 | wait (&encoder, EVENT_PORT_SETTINGS_CHANGED, 0); 1030 | change_state (&null_sink, OMX_StateExecuting); 1031 | wait (&null_sink, EVENT_STATE_SET, 0); 1032 | 1033 | //Enable camera capture port. This basically says that the port 71 will be 1034 | //used to get data from the camera. If you're capturing a still, the port 72 1035 | //must be used 1036 | printf ("enabling %s capture port\n", camera.name); 1037 | OMX_CONFIG_PORTBOOLEANTYPE capture_st; 1038 | OMX_INIT_STRUCTURE (capture_st); 1039 | capture_st.nPortIndex = 71; 1040 | capture_st.bEnabled = OMX_TRUE; 1041 | if ((error = OMX_SetConfig (camera.handle, OMX_IndexConfigPortCapturing, 1042 | &capture_st))){ 1043 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 1044 | exit (1); 1045 | } 1046 | 1047 | //Record ~3000 ms 1048 | struct timespec spec; 1049 | clock_gettime (CLOCK_MONOTONIC, &spec); 1050 | long now = spec.tv_sec*1000 + spec.tv_nsec/1.0e6; 1051 | long end = now + 3000; 1052 | 1053 | while (1){ 1054 | //Get the buffer data 1055 | if ((error = OMX_FillThisBuffer (encoder.handle, encoder_output_buffer))){ 1056 | fprintf (stderr, "error: OMX_FillThisBuffer: %s\n", 1057 | dump_OMX_ERRORTYPE (error)); 1058 | exit (1); 1059 | } 1060 | 1061 | //Wait until it's filled 1062 | wait (&encoder, EVENT_FILL_BUFFER_DONE, 0); 1063 | 1064 | //Append the buffer into the file 1065 | if (pwrite (fd, encoder_output_buffer->pBuffer, 1066 | encoder_output_buffer->nFilledLen, 1067 | encoder_output_buffer->nOffset) == -1){ 1068 | fprintf (stderr, "error: pwrite\n"); 1069 | exit (1); 1070 | } 1071 | 1072 | clock_gettime (CLOCK_MONOTONIC, &spec); 1073 | if (spec.tv_sec*1000 + spec.tv_nsec/1.0e6 >= end) break; 1074 | } 1075 | 1076 | printf ("------------------------------------------------\n"); 1077 | 1078 | //Disable camera capture port 1079 | printf ("disabling %s capture port\n", camera.name); 1080 | capture_st.bEnabled = OMX_FALSE; 1081 | if ((error = OMX_SetConfig (camera.handle, OMX_IndexConfigPortCapturing, 1082 | &capture_st))){ 1083 | fprintf (stderr, "error: OMX_SetConfig: %s\n", dump_OMX_ERRORTYPE (error)); 1084 | exit (1); 1085 | } 1086 | 1087 | //Change state to IDLE 1088 | change_state (&camera, OMX_StateIdle); 1089 | wait (&camera, EVENT_STATE_SET, 0); 1090 | change_state (&encoder, OMX_StateIdle); 1091 | wait (&encoder, EVENT_STATE_SET, 0); 1092 | change_state (&null_sink, OMX_StateIdle); 1093 | wait (&null_sink, EVENT_STATE_SET, 0); 1094 | 1095 | //Disable the tunnel ports 1096 | disable_port (&camera, 71); 1097 | wait (&camera, EVENT_PORT_DISABLE, 0); 1098 | disable_port (&camera, 70); 1099 | wait (&camera, EVENT_PORT_DISABLE, 0); 1100 | disable_port (&null_sink, 240); 1101 | wait (&null_sink, EVENT_PORT_DISABLE, 0); 1102 | disable_port (&encoder, 200); 1103 | wait (&encoder, EVENT_PORT_DISABLE, 0); 1104 | disable_encoder_output_port (&encoder, encoder_output_buffer); 1105 | 1106 | //Change state to LOADED 1107 | change_state (&camera, OMX_StateLoaded); 1108 | wait (&camera, EVENT_STATE_SET, 0); 1109 | change_state (&encoder, OMX_StateLoaded); 1110 | wait (&encoder, EVENT_STATE_SET, 0); 1111 | change_state (&null_sink, OMX_StateLoaded); 1112 | wait (&null_sink, EVENT_STATE_SET, 0); 1113 | 1114 | //Deinitialize components 1115 | deinit_component (&camera); 1116 | deinit_component (&encoder); 1117 | deinit_component (&null_sink); 1118 | 1119 | //Deinitialize OpenMAX IL 1120 | if ((error = OMX_Deinit ())){ 1121 | fprintf (stderr, "error: OMX_Deinit: %s\n", dump_OMX_ERRORTYPE (error)); 1122 | exit (1); 1123 | } 1124 | 1125 | //Deinitialize Broadcom's VideoCore APIs 1126 | bcm_host_deinit (); 1127 | 1128 | //Close the file 1129 | if (close (fd)){ 1130 | fprintf (stderr, "error: close\n"); 1131 | exit (1); 1132 | } 1133 | 1134 | printf ("ok\n"); 1135 | 1136 | return 0; 1137 | } --------------------------------------------------------------------------------