1 /******************************************************************************
2 *
3 * Project: jpip read driver
4 * Purpose: GDAL bindings for JPIP.
5 * Author: Norman Barker, ITT VIS, norman.barker@gmail.com
6 *
7 ******************************************************************************
8 * ITT Visual Information Systems grants you use of this code, under the
9 * following license:
10 *
11 * Copyright (c) 2000-2007, ITT Visual Information Solutions
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 **/
30
31 #ifdef DEBUG_BOOL
32 #define DO_NOT_USE_DEBUG_BOOL
33 #endif
34
35 #include "gdal_frmts.h"
36 #include "jpipkakdataset.h"
37
38 CPL_CVSID("$Id: jpipkakdataset.cpp cee3d5d3864c1d7f493306700a19ef22f0fecbee 2021-03-08 22:53:56 +0100 Even Rouault $")
39
40 /*
41 ** The following are for testing premature stream termination support.
42 ** This is a mechanism to test handling of failed or incomplete reads
43 ** from the server, and is not normally active. For this reason we
44 ** don't worry about the non-threadsafe nature of the debug support
45 ** variables below.
46 */
47
48 #ifdef DEBUG
49 # define PST_DEBUG 1
50 #endif
51
52 #ifdef PST_DEBUG
53 static int nPSTTargetInstance = -1;
54 static int nPSTThisInstance = -1;
55 static int nPSTTargetOffset = -1;
56 #endif
57
58 static bool kakadu_initialized = false;
59
60 /************************************************************************/
61 /* ==================================================================== */
62 /* Set up messaging services */
63 /* ==================================================================== */
64 /************************************************************************/
65
66 class jpipkak_kdu_cpl_error_message : public kdu_message
67 {
68 public: // Member classes
69 using kdu_message::put_text;
70
jpipkak_kdu_cpl_error_message(CPLErr eErrClass)71 explicit jpipkak_kdu_cpl_error_message( CPLErr eErrClass ) :
72 m_eErrClass ( eErrClass ),
73 m_pszError ( nullptr )
74 {}
75
put_text(const char * string)76 void put_text(const char *string) override
77 {
78 if( m_pszError == nullptr )
79 m_pszError = CPLStrdup( string );
80 else
81 {
82 m_pszError = (char *)
83 CPLRealloc(m_pszError, strlen(m_pszError) + strlen(string)+1 );
84 strcat( m_pszError, string );
85 }
86 }
87
88 class JP2KAKException
89 {
90 };
91
flush(bool end_of_message=false)92 void flush(bool end_of_message=false) override
93 {
94 if( m_pszError == nullptr )
95 return;
96 if( m_pszError[strlen(m_pszError)-1] == '\n' )
97 m_pszError[strlen(m_pszError)-1] = '\0';
98
99 CPLError( m_eErrClass, CPLE_AppDefined, "%s", m_pszError );
100 CPLFree( m_pszError );
101 m_pszError = nullptr;
102
103 if( end_of_message && m_eErrClass == CE_Failure )
104 {
105 throw new JP2KAKException();
106 }
107 }
108
109 private:
110 CPLErr m_eErrClass;
111 char *m_pszError;
112 };
113
114 /************************************************************************/
115 /* ==================================================================== */
116 /* JPIPKAKRasterBand */
117 /* ==================================================================== */
118 /************************************************************************/
119
120 /************************************************************************/
121 /* JPIPKAKRasterBand() */
122 /************************************************************************/
123
JPIPKAKRasterBand(int nBandIn,int nDiscardLevelsIn,kdu_codestream * oCodeStreamIn,int nResCount,JPIPKAKDataset * poBaseDSIn)124 JPIPKAKRasterBand::JPIPKAKRasterBand( int nBandIn, int nDiscardLevelsIn,
125 kdu_codestream *oCodeStreamIn,
126 int nResCount,
127 JPIPKAKDataset *poBaseDSIn )
128
129 {
130 this->nBand = nBandIn;
131 poBaseDS = poBaseDSIn;
132
133 eDataType = poBaseDSIn->eDT;
134
135 this->nDiscardLevels = nDiscardLevelsIn;
136 this->oCodeStream = oCodeStreamIn;
137
138 oCodeStream->apply_input_restrictions( 0, 0, nDiscardLevels, 0, nullptr );
139 oCodeStream->get_dims( 0, band_dims );
140
141 nRasterXSize = band_dims.size.x;
142 nRasterYSize = band_dims.size.y;
143
144 /* -------------------------------------------------------------------- */
145 /* Use a 2048x128 "virtual" block size unless the file is small. */
146 /* -------------------------------------------------------------------- */
147 if( nRasterXSize >= 2048 )
148 nBlockXSize = 2048;
149 else
150 nBlockXSize = nRasterXSize;
151
152 if( nRasterYSize >= 256 )
153 nBlockYSize = 128;
154 else
155 nBlockYSize = nRasterYSize;
156
157 /* -------------------------------------------------------------------- */
158 /* Figure out the color interpretation for this band. */
159 /* -------------------------------------------------------------------- */
160
161 eInterp = GCI_Undefined;
162
163 /* -------------------------------------------------------------------- */
164 /* Do we have any overviews? Only check if we are the full res */
165 /* image. */
166 /* -------------------------------------------------------------------- */
167 nOverviewCount = 0;
168 papoOverviewBand = nullptr;
169
170 if( nDiscardLevels == 0 )
171 {
172 int nXSize = nRasterXSize;
173 int nYSize = nRasterYSize;
174
175 for( int nDiscard = 1; nDiscard < nResCount; nDiscard++ )
176 {
177
178 nXSize = (nXSize + 1) / 2;
179 nYSize = (nYSize + 1) / 2;
180
181 if( (nXSize+nYSize) < 128 || nXSize < 4 || nYSize < 4 )
182 continue; /* skip super reduced resolution layers */
183
184 oCodeStream->apply_input_restrictions( 0, 0, nDiscard, 0, nullptr );
185 kdu_dims dims;
186 oCodeStream->get_dims( 0, dims );
187
188 if( (dims.size.x == nXSize || dims.size.x == nXSize-1)
189 && (dims.size.y == nYSize || dims.size.y == nYSize-1) )
190 {
191 nOverviewCount++;
192 papoOverviewBand = (JPIPKAKRasterBand **)
193 CPLRealloc( papoOverviewBand,
194 sizeof(void*) * nOverviewCount );
195 papoOverviewBand[nOverviewCount-1] =
196 new JPIPKAKRasterBand( nBand, nDiscard, oCodeStream, 0,
197 poBaseDS );
198 }
199 else
200 {
201 CPLDebug( "GDAL", "Discard %dx%d JPEG2000 overview layer,\n"
202 "expected %dx%d.",
203 dims.size.x, dims.size.y, nXSize, nYSize );
204 }
205 }
206 }
207 }
208
209 /************************************************************************/
210 /* ~JPIPKAKRasterBand() */
211 /************************************************************************/
212
~JPIPKAKRasterBand()213 JPIPKAKRasterBand::~JPIPKAKRasterBand()
214
215 {
216 for( int i = 0; i < nOverviewCount; i++ )
217 delete papoOverviewBand[i];
218
219 CPLFree( papoOverviewBand );
220 }
221
222 /************************************************************************/
223 /* GetOverviewCount() */
224 /************************************************************************/
225
GetOverviewCount()226 int JPIPKAKRasterBand::GetOverviewCount()
227
228 {
229 return nOverviewCount;
230 }
231
232 /************************************************************************/
233 /* GetOverview() */
234 /************************************************************************/
235
GetOverview(int iOverviewIndex)236 GDALRasterBand *JPIPKAKRasterBand::GetOverview( int iOverviewIndex )
237
238 {
239 if( iOverviewIndex < 0 || iOverviewIndex >= nOverviewCount )
240 return nullptr;
241 else
242 return papoOverviewBand[iOverviewIndex];
243 }
244
245 /************************************************************************/
246 /* IReadBlock() */
247 /************************************************************************/
248
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)249 CPLErr JPIPKAKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
250 void * pImage )
251 {
252 CPLDebug( "JPIPKAK", "IReadBlock(%d,%d) on band %d.",
253 nBlockXOff, nBlockYOff, nBand );
254
255 /* -------------------------------------------------------------------- */
256 /* Fix the buffer layer. */
257 /* -------------------------------------------------------------------- */
258 int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8;
259 int nLineSpace = nPixelSpace * nBlockXSize;
260 int nBandSpace = nLineSpace * nBlockYSize;
261
262 /* -------------------------------------------------------------------- */
263 /* Zoom up file window based on overview level so we are */
264 /* referring to the full res image. */
265 /* -------------------------------------------------------------------- */
266 int nZoom = 1 << nDiscardLevels;
267
268 int xOff = nBlockXOff * nBlockXSize * nZoom;
269 int yOff = nBlockYOff * nBlockYSize * nZoom;
270 int xSize = nBlockXSize * nZoom;
271 int ySize = nBlockYSize * nZoom;
272
273 int nBufXSize = nBlockXSize;
274 int nBufYSize = nBlockYSize;
275
276 /* -------------------------------------------------------------------- */
277 /* Make adjustments for partial blocks on right and bottom. */
278 /* -------------------------------------------------------------------- */
279 if( xOff + xSize > poBaseDS->GetRasterXSize() )
280 {
281 xSize = poBaseDS->GetRasterXSize() - xOff;
282 nBufXSize= MAX(xSize/nZoom,1);
283 }
284
285 if( yOff + ySize > poBaseDS->GetRasterYSize() )
286 {
287 ySize = poBaseDS->GetRasterYSize() - yOff;
288 nBufYSize = MAX(ySize/nZoom,1);
289 }
290
291 /* -------------------------------------------------------------------- */
292 /* Start the reader and run till complete. */
293 /* -------------------------------------------------------------------- */
294 GDALAsyncReader* ario = poBaseDS->
295 BeginAsyncReader(xOff, yOff, xSize, ySize,
296 pImage, nBufXSize,nBufYSize,
297 eDataType, 1, &nBand,
298 nPixelSpace, nLineSpace, nBandSpace, nullptr);
299
300 if( ario == nullptr )
301 return CE_Failure;
302
303 int nXBufOff; // absolute x image offset
304 int nYBufOff; // absolute y image offset
305 int nXBufSize;
306 int nYBufSize;
307
308 GDALAsyncStatusType status;
309
310 do
311 {
312 status = ario->GetNextUpdatedRegion(-1.0,
313 &nXBufOff, &nYBufOff,
314 &nXBufSize, &nYBufSize );
315 } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
316
317 poBaseDS->EndAsyncReader(ario);
318
319 if (status == GARIO_ERROR)
320 return CE_Failure;
321 else
322 return CE_None;
323 }
324
325 /************************************************************************/
326 /* IRasterIO() */
327 /************************************************************************/
328
329 CPLErr
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)330 JPIPKAKRasterBand::IRasterIO( GDALRWFlag eRWFlag,
331 int nXOff, int nYOff, int nXSize, int nYSize,
332 void * pData, int nBufXSize, int nBufYSize,
333 GDALDataType eBufType,
334 GSpacing nPixelSpace, GSpacing nLineSpace,
335 GDALRasterIOExtraArg* psExtraArg)
336
337 {
338 /* -------------------------------------------------------------------- */
339 /* We need various criteria to skip out to block based methods. */
340 /* -------------------------------------------------------------------- */
341 if( poBaseDS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize,
342 nBufXSize, nBufYSize,
343 eBufType, 1, &nBand ) )
344 return GDALPamRasterBand::IRasterIO(
345 eRWFlag, nXOff, nYOff, nXSize, nYSize,
346 pData, nBufXSize, nBufYSize, eBufType,
347 nPixelSpace, nLineSpace, psExtraArg );
348
349 /* -------------------------------------------------------------------- */
350 /* Otherwise do this as a single uncached async rasterio. */
351 /* -------------------------------------------------------------------- */
352 GDALAsyncReader* ario =
353 poBaseDS->BeginAsyncReader(nXOff, nYOff, nXSize, nYSize,
354 pData, nBufXSize, nBufYSize, eBufType,
355 1, &nBand,
356 static_cast<int>(nPixelSpace),
357 static_cast<int>(nLineSpace), 0, nullptr);
358
359 if( ario == nullptr )
360 return CE_Failure;
361
362 GDALAsyncStatusType status;
363
364 do
365 {
366 int nXBufOff,nYBufOff,nXBufSize,nYBufSize;
367
368 status = ario->GetNextUpdatedRegion(-1.0,
369 &nXBufOff, &nYBufOff,
370 &nXBufSize, &nYBufSize );
371 } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
372
373 poBaseDS->EndAsyncReader(ario);
374
375 if (status == GARIO_ERROR)
376 return CE_Failure;
377 else
378 return CE_None;
379 }
380
381 /*****************************************/
382 /* JPIPKAKDataset() */
383 /*****************************************/
JPIPKAKDataset()384 JPIPKAKDataset::JPIPKAKDataset()
385 {
386 adfGeoTransform[0] = 0.0;
387 adfGeoTransform[1] = 1.0;
388 adfGeoTransform[2] = 0.0;
389 adfGeoTransform[3] = 0.0;
390 adfGeoTransform[4] = 0.0;
391 adfGeoTransform[5] = 1.0;
392
393 pGlobalMutex = CPLCreateMutex();
394 CPLReleaseMutex(pGlobalMutex);
395 }
396
397 /*****************************************/
398 /* ~JPIPKAKDataset() */
399 /*****************************************/
~JPIPKAKDataset()400 JPIPKAKDataset::~JPIPKAKDataset()
401 {
402 char** papszOptions = nullptr;
403 papszOptions = CSLSetNameValue(papszOptions,
404 "CLOSE_PERSISTENT", CPLSPrintf("JPIPKAK:%p", this));
405 CPLHTTPDestroyResult(CPLHTTPFetch("", papszOptions));
406 CSLDestroy(papszOptions);
407
408 Deinitialize();
409
410 CPLFree(pszProjection);
411 pszProjection = nullptr;
412
413 CPLFree(pszPath);
414
415 if (nGCPCount > 0 )
416 {
417 GDALDeinitGCPs( nGCPCount, pasGCPList );
418 CPLFree( pasGCPList );
419 }
420 }
421
422 /************************************************************************/
423 /* Deinitialize() */
424 /* */
425 /* Cleanup stuff that we will rebuild during a */
426 /* reinitialization. */
427 /************************************************************************/
428
Deinitialize()429 void JPIPKAKDataset::Deinitialize()
430
431 {
432 CPLFree(pszCid);
433 pszCid = nullptr;
434
435 // frees decompressor as well
436 if (poCodestream)
437 {
438 poCodestream->destroy();
439 delete poCodestream;
440 poCodestream = nullptr;
441 }
442
443 delete poDecompressor;
444 poDecompressor = nullptr;
445
446 delete poCache;
447 poCache = nullptr;
448
449 bNeedReinitialize = TRUE;
450 }
451
452 /************************************************************************/
453 /* KakaduInitialize() */
454 /************************************************************************/
455
KakaduInitialize()456 void JPIPKAKDataset::KakaduInitialize()
457
458 {
459 /* -------------------------------------------------------------------- */
460 /* Initialize Kakadu warning/error reporting subsystem. */
461 /* -------------------------------------------------------------------- */
462 if( !kakadu_initialized )
463 {
464 kakadu_initialized = true;
465
466 jpipkak_kdu_cpl_error_message oErrHandler( CE_Failure );
467 jpipkak_kdu_cpl_error_message oWarningHandler( CE_Warning );
468 CPL_IGNORE_RET_VAL(oErrHandler);
469 CPL_IGNORE_RET_VAL(oWarningHandler);
470
471 kdu_customize_warnings(new jpipkak_kdu_cpl_error_message( CE_Warning ) );
472 kdu_customize_errors(new jpipkak_kdu_cpl_error_message( CE_Failure ) );
473 }
474 }
475
476 /*****************************************/
477 /* Initialize() */
478 /*****************************************/
Initialize(const char * pszDatasetName,int bReinitializing)479 int JPIPKAKDataset::Initialize(const char* pszDatasetName, int bReinitializing )
480 {
481 KakaduInitialize();
482
483 // create necessary http headers
484 CPLString osHeaders = "HEADERS=Accept: jpp-stream";
485 CPLString osPersistent;
486
487 osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", this );
488
489 char *apszOptions[] = {
490 (char *) osHeaders.c_str(),
491 (char *) osPersistent.c_str(),
492 nullptr
493 };
494
495 // Setup url to have http in place of jpip protocol indicator.
496 CPLString osURL = "http";
497 osURL += (pszDatasetName + 4);
498
499 CPLAssert( STARTS_WITH(pszDatasetName, "jpip") );
500
501 // make initial request to the server for a session, we are going to
502 // assume that the jpip communication is stateful, rather than one-shot
503 // stateless requests append pszUrl with jpip request parameters for a
504 // stateful session (multi-shot communications)
505 // "cnew=http&type=jpp-stream&stream=0&tid=0&len="
506 CPLString osRequest;
507 osRequest.Printf("%s?%s%i", osURL.c_str(),
508 "cnew=http&type=jpp-stream&stream=0&tid=0&len=", 2000);
509
510 CPLHTTPResult *psResult = CPLHTTPFetch(osRequest, apszOptions);
511
512 if ( psResult == nullptr)
513 return FALSE;
514
515 if( psResult->nDataLen == 0 || psResult->pabyData == nullptr )
516 {
517
518 CPLError(CE_Failure, CPLE_AppDefined,
519 "No data was returned from the given URL" );
520 CPLHTTPDestroyResult( psResult );
521 return FALSE;
522 }
523
524 if (psResult->nStatus != 0)
525 {
526 CPLError(CE_Failure, CPLE_AppDefined,
527 "Curl reports error: %d: %s", psResult->nStatus, psResult->pszErrBuf );
528 CPLHTTPDestroyResult( psResult );
529 return FALSE;
530 }
531
532 // parse the response headers, and the initial data until we get to the
533 // codestream definition
534 char** pszHdrs = psResult->papszHeaders;
535 const char* pszCnew = CSLFetchNameValue(pszHdrs, "JPIP-cnew");
536
537 if( pszCnew == nullptr )
538 {
539 if( psResult->pszContentType != nullptr
540 && STARTS_WITH_CI(psResult->pszContentType, "text/html") )
541 CPLDebug( "JPIPKAK", "%s",
542 psResult->pabyData );
543
544 CPLHTTPDestroyResult( psResult );
545 CPLError(CE_Failure, CPLE_AppDefined,
546 "Unable to parse required cnew and tid response headers" );
547
548 return FALSE;
549 }
550
551 // parse cnew response
552 // JPIP-cnew:
553 // cid=DC69DF980A641A4BBDEB50E484A66578,path=MyPath,transport=http
554 char **papszTokens = CSLTokenizeString2( pszCnew, ",", CSLT_HONOURSTRINGS);
555 for (int i = 0; i < CSLCount(papszTokens); i++)
556 {
557 // looking for cid, path
558 if (STARTS_WITH_CI(papszTokens[i], "cid"))
559 {
560 char *pszKey = nullptr;
561 const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey );
562 pszCid = CPLStrdup(pszValue);
563 CPLFree( pszKey );
564 }
565
566 if (STARTS_WITH_CI(papszTokens[i], "path"))
567 {
568 char *pszKey = nullptr;
569 const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey );
570 pszPath = CPLStrdup(pszValue);
571 CPLFree( pszKey );
572 }
573 }
574
575 CSLDestroy(papszTokens);
576
577 if( pszPath == nullptr || pszCid == nullptr )
578 {
579 CPLHTTPDestroyResult(psResult);
580 CPLError(CE_Failure, CPLE_AppDefined, "Error parsing path and cid from cnew - %s", pszCnew);
581 return FALSE;
582 }
583
584 // Okay, good to go with JPIP, get to the codestream before returning
585 // successful initialization of the driver
586 try
587 {
588 poCache = new kdu_cache();
589 poCodestream = new kdu_codestream();
590 poDecompressor = new kdu_region_decompressor();
591
592 int bFinished = FALSE;
593 int bError = FALSE;
594 bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen,
595 bError );
596 CPLHTTPDestroyResult(psResult);
597
598 // continue making requests in the main thread to get all the available
599 // metadata for data bin 0, and reach the codestream
600
601 // format the new request
602 // and set as pszRequestUrl;
603 // get the protocol from the original request
604 size_t found = osRequest.find_first_of("/");
605 CPLString osProtocol = osRequest.substr(0, found + 2);
606 osRequest.erase(0, found + 2);
607 // find context path
608 found = osRequest.find_first_of("/");
609 osRequest.erase(found);
610
611 osRequestUrl.Printf("%s%s/%s?cid=%s&stream=0&len=%i", osProtocol.c_str(), osRequest.c_str(), pszPath, pszCid, 2000);
612
613 while (!bFinished && !bError )
614 {
615 psResult = CPLHTTPFetch(osRequestUrl, apszOptions);
616 bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen,
617 bError );
618 CPLHTTPDestroyResult(psResult);
619 }
620
621 if( bError )
622 return FALSE;
623
624 // clean up osRequest, remove variable len= parameter
625 size_t pos = osRequestUrl.find_last_of("&");
626 osRequestUrl.erase(pos);
627
628 // create codestream
629 poCache->set_read_scope(KDU_MAIN_HEADER_DATABIN, 0, 0);
630 poCodestream->create(poCache);
631 poCodestream->set_persistent();
632
633 /* -------------------------------------------------------------------- */
634 /* If this is a reinitialization then we can hop out at this */
635 /* point. The rest of the stuff was already done, and */
636 /* hopefully the configuration is unchanged. */
637 /* -------------------------------------------------------------------- */
638 if( bReinitializing )
639 {
640 bNeedReinitialize = FALSE;
641 return TRUE;
642 }
643
644 /* -------------------------------------------------------------------- */
645 /* Collect GDAL raster configuration information. */
646 /* -------------------------------------------------------------------- */
647 kdu_channel_mapping oChannels;
648 oChannels.configure(*poCodestream);
649 kdu_coords* ref_expansion = new kdu_coords(1, 1);
650
651 // get available resolutions, image width / height etc.
652 kdu_dims view_dims = poDecompressor->
653 get_rendered_image_dims(*poCodestream, &oChannels, -1, 0,
654 *ref_expansion, *ref_expansion,
655 KDU_WANT_OUTPUT_COMPONENTS);
656
657 nRasterXSize = view_dims.size.x;
658 nRasterYSize = view_dims.size.y;
659
660 // Establish the datatype - we will use the same datatype for
661 // all bands based on the first. This really doesn't do something
662 // great for >16 bit images.
663 if( poCodestream->get_bit_depth(0) > 8
664 && poCodestream->get_signed(0) )
665 {
666 eDT = GDT_Int16;
667 }
668 else if( poCodestream->get_bit_depth(0) > 8
669 && !poCodestream->get_signed(0) )
670 {
671 eDT = GDT_UInt16;
672 }
673 else
674 eDT = GDT_Byte;
675
676 if( (poCodestream->get_bit_depth(0) % 8) != 0
677 && poCodestream->get_bit_depth(0) < 16 )
678 SetMetadataItem(
679 "NBITS",
680 CPLString().Printf("%d",poCodestream->get_bit_depth(0)),
681 "IMAGE_STRUCTURE" );
682
683 // TODO add color interpretation
684
685 // calculate overviews
686 siz_params* siz_in = poCodestream->access_siz();
687 kdu_params* cod_in = siz_in->access_cluster("COD");
688
689 delete ref_expansion;
690
691 siz_in->get("Scomponents", 0, 0, nComps);
692 siz_in->get("Sprecision", 0, 0, nBitDepth);
693
694 cod_in->get("Clayers", 0, 0, nQualityLayers);
695 cod_in->get("Clevels", 0, 0, nResLevels);
696
697 bYCC=TRUE;
698 cod_in->get("Cycc", 0, 0, bYCC);
699 }
700 catch(...)
701 {
702 CPLError(CE_Failure, CPLE_AppDefined,
703 "Trapped Kakadu exception attempting to initialize JPIP access." );
704 return FALSE;
705 }
706
707 /* -------------------------------------------------------------------- */
708 /* YCC images are always processed as 3 bands. */
709 /* -------------------------------------------------------------------- */
710 if( bYCC )
711 nBands = 3;
712 else
713 nBands = nComps;
714
715 /* -------------------------------------------------------------------- */
716 /* Setup band objects. */
717 /* -------------------------------------------------------------------- */
718 int iBand;
719
720 for( iBand = 1; iBand <= nBands; iBand++ )
721 {
722 JPIPKAKRasterBand *poBand =
723 new JPIPKAKRasterBand(iBand,0,poCodestream,nResLevels,
724 this );
725
726 SetBand( iBand, poBand );
727 }
728
729 // set specific metadata items
730 CPLString osNQualityLayers;
731 osNQualityLayers.Printf("%i", nQualityLayers);
732 CPLString osNResolutionLevels;
733 osNResolutionLevels.Printf("%i", nResLevels);
734 CPLString osNComps;
735 osNComps.Printf("%i", nComps);
736 CPLString osBitDepth;
737 osBitDepth.Printf("%i", nBitDepth);
738
739 SetMetadataItem("JPIP_NQUALITYLAYERS", osNQualityLayers.c_str(), "JPIP");
740 SetMetadataItem("JPIP_NRESOLUTIONLEVELS", osNResolutionLevels.c_str(), "JPIP");
741 SetMetadataItem("JPIP_NCOMPS", osNComps.c_str(), "JPIP");
742 SetMetadataItem("JPIP_SPRECISION", osBitDepth.c_str(), "JPIP");
743
744 if( bYCC )
745 SetMetadataItem("JPIP_YCC", "YES", "JPIP");
746 else
747 SetMetadataItem("JPIP_YCC", "NO", "JPIP");
748
749 /* ==================================================================== */
750 /* Parse geojp2, or gmljp2, we will assume that the core */
751 /* metadata of gml or a geojp2 uuid have been sent in the */
752 /* initial metadata response. */
753 /* If the server has used placeholder boxes for this */
754 /* information then the image will be interpreted as x,y */
755 /* ==================================================================== */
756 GDALJP2Metadata oJP2Geo;
757 int nLen = poCache->get_databin_length(KDU_META_DATABIN, nCodestream, 0);
758
759 if( nLen == 0 )
760 {
761 CPLError(CE_Failure, CPLE_AppDefined,
762 "Unable to open stream to parse metadata boxes" );
763 return FALSE;
764 }
765
766 // create in memory file using vsimem
767 CPLString osFileBoxName;
768 osFileBoxName.Printf("/vsimem/jpip/%s.dat", pszCid);
769 VSILFILE *fpLL = VSIFOpenL(osFileBoxName.c_str(), "w+");
770 poCache->set_read_scope(KDU_META_DATABIN, nCodestream, 0);
771 kdu_byte* pabyBuffer = (kdu_byte *)CPLMalloc(nLen);
772 poCache->read(pabyBuffer, nLen);
773 VSIFWriteL(pabyBuffer, nLen, 1, fpLL);
774 CPLFree( pabyBuffer );
775
776 VSIFFlushL(fpLL);
777 VSIFSeekL(fpLL, 0, SEEK_SET);
778
779 nPamFlags |= GPF_NOSAVE;
780
781 try
782 {
783 oJP2Geo.ReadBoxes(fpLL);
784 // parse gml first, followed by geojp2 as a fallback
785 if (oJP2Geo.ParseGMLCoverageDesc() || oJP2Geo.ParseJP2GeoTIFF())
786 {
787 pszProjection = CPLStrdup(oJP2Geo.pszProjection);
788 bGeoTransformValid = TRUE;
789
790 memcpy(adfGeoTransform, oJP2Geo.adfGeoTransform,
791 sizeof(double) * 6 );
792 nGCPCount = oJP2Geo.nGCPCount;
793 pasGCPList = oJP2Geo.pasGCPList;
794
795 oJP2Geo.pasGCPList = nullptr;
796 oJP2Geo.nGCPCount = 0;
797
798 int iBox;
799
800 for( iBox = 0;
801 oJP2Geo.papszGMLMetadata
802 && oJP2Geo.papszGMLMetadata[iBox] != nullptr;
803 iBox++ )
804 {
805 char *pszName = nullptr;
806 const char *pszXML =
807 CPLParseNameValue( oJP2Geo.papszGMLMetadata[iBox],
808 &pszName );
809 CPLString osDomain;
810 char *apszMDList[2];
811
812 osDomain.Printf( "xml:%s", pszName );
813 apszMDList[0] = (char *) pszXML;
814 apszMDList[1] = nullptr;
815
816 GDALPamDataset::SetMetadata( apszMDList, osDomain );
817 CPLFree( pszName );
818 }
819 }
820 else
821 {
822 // treat as Cartesian, no geo metadata
823 CPLError(CE_Warning, CPLE_AppDefined,
824 "Parsed metadata boxes from jpip stream, geographic metadata not found - is the server using placeholders for this data?" );
825 }
826 }
827 catch(...)
828 {
829 CPLError(CE_Failure, CPLE_AppDefined,
830 "Unable to parse geographic metadata boxes from jpip stream" );
831 }
832
833 VSIFCloseL(fpLL);
834 VSIUnlink( osFileBoxName.c_str());
835
836 bNeedReinitialize = FALSE;
837
838 return TRUE;
839 }
840
841 /******************************************/
842 /* ReadVBAS() */
843 /******************************************/
ReadVBAS(GByte * pabyData,int nLen)844 long JPIPKAKDataset::ReadVBAS(GByte* pabyData, int nLen )
845 {
846 int c = -1;
847 long val = 0;
848 nVBASLen = 0;
849
850 while ((c & 0x80) != 0)
851 {
852 if (nVBASLen >= 9)
853 {
854 CPLError(CE_Failure, CPLE_AppDefined,
855 "VBAS Length not supported");
856 return -1;
857 }
858
859 if (nPos > nLen)
860 {
861 CPLError(CE_Failure, CPLE_AppDefined,
862 "EOF reached before completing VBAS");
863 return -1;
864 }
865
866 #ifdef PST_DEBUG
867 if( nPSTThisInstance == nPSTTargetInstance
868 && nPos >= nPSTTargetOffset )
869 {
870 CPLError(CE_Failure, CPLE_AppDefined,
871 "Artificial PST EOF reached before completing VBAS");
872 return -1;
873 }
874 #endif
875
876 c = pabyData[nPos];
877 nPos++;
878
879 val = (val << 7) | (long)(c & 0x7F);
880
881 if (nVBASLen == 0)
882 nVBASFirstByte = c;
883
884 nVBASLen++;
885 }
886
887 return val;
888 }
889
890 /******************************************/
891 /* ReadSegment() */
892 /******************************************/
ReadSegment(GByte * pabyData,int nLen,int & bError)893 JPIPDataSegment* JPIPKAKDataset::ReadSegment(GByte* pabyData, int nLen,
894 int& bError )
895 {
896 long nId = ReadVBAS(pabyData, nLen);
897 bError = FALSE;
898
899 if (nId < 0)
900 {
901 bError = TRUE;
902 return nullptr;
903 }
904 else
905 {
906 JPIPDataSegment* segment = new JPIPDataSegment();
907 segment->SetId(nId);
908
909 if (nVBASFirstByte == 0)
910 {
911 segment->SetEOR(TRUE);
912 segment->SetId(pabyData[nPos]);
913 }
914 else
915 {
916 segment->SetEOR(FALSE);
917 nId &= ~(0x70 << ((nVBASLen -1) * 7));
918 segment->SetId(nId);
919 segment->SetFinal((nVBASFirstByte & 0x10) != 0);
920
921 int m = (nVBASFirstByte & 0x7F) >> 5;
922
923 if (m == 0)
924 {
925 CPLError(CE_Failure, CPLE_AppDefined,
926 "Invalid Bin-ID value format");
927 bError = TRUE;
928 delete segment;
929 return nullptr;
930 }
931 else if (m >= 2) {
932 nClassId = static_cast<int>(ReadVBAS(pabyData, nLen));
933 if (m > 2)
934 {
935 nCodestream = static_cast<int>(ReadVBAS(pabyData, nLen));
936 if( nCodestream < 0 )
937 {
938 bError = TRUE;
939 delete segment;
940 return nullptr;
941 }
942 }
943 }
944
945 long nNextVal;
946
947 segment->SetClassId(nClassId);
948 segment->SetCodestreamIdx(nCodestream);
949
950 nNextVal = ReadVBAS(pabyData, nLen);
951 if( nNextVal == -1 )
952 {
953 bError = TRUE;
954 delete segment;
955 return nullptr;
956 }
957 else
958 segment->SetOffset( nNextVal );
959
960 nNextVal = ReadVBAS(pabyData, nLen);
961 if( nNextVal == -1 )
962 {
963 bError = TRUE;
964 delete segment;
965 return nullptr;
966 }
967 else
968 segment->SetLen(nNextVal);
969 }
970
971 if ((segment->GetLen() > 0) && (!segment->IsEOR()))
972 {
973 GByte* pabyDataSegment = (GByte *) CPLCalloc(1,segment->GetLen());
974
975 // copy data from input array pabyData to the data segment
976 memcpy(pabyDataSegment,
977 pabyData + nPos,
978 segment->GetLen());
979
980 segment->SetData(pabyDataSegment);
981 }
982
983 nPos += segment->GetLen();
984
985 if (!segment->IsEOR())
986 nDatabins++;
987
988 if ((segment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE) && (segment->IsEOR()))
989 bWindowDone = TRUE;
990
991 return segment;
992 }
993 }
994
995 /******************************************/
996 /* KakaduClassId() */
997 /******************************************/
KakaduClassId(int nClassIdIn)998 int JPIPKAKDataset::KakaduClassId(int nClassIdIn)
999 {
1000 if (nClassIdIn == 0)
1001 return KDU_PRECINCT_DATABIN;
1002 else if (nClassIdIn == 2)
1003 return KDU_TILE_HEADER_DATABIN;
1004 else if (nClassIdIn == 6)
1005 return KDU_MAIN_HEADER_DATABIN;
1006 else if (nClassIdIn == 8)
1007 return KDU_META_DATABIN;
1008 else if (nClassIdIn == 4)
1009 return KDU_TILE_DATABIN;
1010 else
1011 return -1;
1012 }
1013
1014 /******************************************/
1015 /* ReadFromInput() */
1016 /******************************************/
ReadFromInput(GByte * pabyData,int nLen,int & bError)1017 int JPIPKAKDataset::ReadFromInput(GByte* pabyData, int nLen, int &bError )
1018 {
1019 int res = FALSE;
1020 bError = FALSE;
1021
1022 if (nLen <= 0 )
1023 return FALSE;
1024
1025 #ifdef PST_DEBUG
1026 nPSTThisInstance++;
1027 if( CPLGetConfigOption( "PST_OFFSET", nullptr ) != nullptr )
1028 {
1029 nPSTTargetOffset = atoi(CPLGetConfigOption("PST_OFFSET","0"));
1030 nPSTTargetInstance = 0;
1031 }
1032 if( CPLGetConfigOption( "PST_INSTANCE", nullptr ) != nullptr )
1033 {
1034 nPSTTargetInstance = atoi(CPLGetConfigOption("PST_INSTANCE","0"));
1035 }
1036
1037 if( nPSTTargetOffset != -1 && nPSTThisInstance == 0 )
1038 {
1039 CPLDebug( "JPIPKAK", "Premature Stream Termination Activated, PST_OFFSET=%d, PST_INSTANCE=%d",
1040 nPSTTargetOffset, nPSTTargetInstance );
1041 }
1042 if( nPSTTargetOffset != -1
1043 && nPSTThisInstance == nPSTTargetInstance )
1044 {
1045 CPLDebug( "JPIPKAK", "Premature Stream Termination in force for this input instance, PST_OFFSET=%d, data length=%d",
1046 nPSTTargetOffset, nLen );
1047 }
1048 #endif // def PST_DEBUG
1049
1050 // parse the data stream, reading the vbas and adding to the kakadu cache
1051 // we could parse all the boxes by hand, and just add data to the kakadu cache
1052 // we will do it the easy way and retrieve the metadata through the kakadu query api
1053
1054 nPos = 0;
1055 JPIPDataSegment* pSegment = nullptr;
1056
1057 while ((pSegment = ReadSegment(pabyData, nLen, bError)) != nullptr)
1058 {
1059 if (pSegment->IsEOR())
1060 {
1061 if ((pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_IMAGE_DONE) ||
1062 (pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE))
1063 res = TRUE;
1064
1065 delete pSegment;
1066 break;
1067 }
1068 else
1069 {
1070 // add data to kakadu
1071 //CPLDebug("JPIPKAK", "Parsed JPIP Segment class=%i stream=%i id=%i offset=%i len=%i isFinal=%i isEOR=%i", pSegment->GetClassId(), pSegment->GetCodestreamIdx(), pSegment->GetId(), pSegment->GetOffset(), pSegment->GetLen(), pSegment->IsFinal(), pSegment->IsEOR());
1072 poCache->add_to_databin(KakaduClassId(static_cast<int>(pSegment->GetClassId())), pSegment->GetCodestreamIdx(),
1073 pSegment->GetId(), pSegment->GetData(),
1074 static_cast<int>(pSegment->GetOffset()),
1075 static_cast<int>(pSegment->GetLen()), pSegment->IsFinal());
1076
1077 delete pSegment;
1078 }
1079 }
1080
1081 return res;
1082 }
1083
1084 /************************************************************************/
1085 /* GetProjectionRef() */
1086 /************************************************************************/
1087
_GetProjectionRef()1088 const char *JPIPKAKDataset::_GetProjectionRef()
1089
1090 {
1091 if( pszProjection && *pszProjection )
1092 return pszProjection;
1093 else
1094 return GDALPamDataset::_GetProjectionRef();
1095 }
1096
1097 /************************************************************************/
1098 /* GetGeoTransform() */
1099 /************************************************************************/
1100
GetGeoTransform(double * padfTransform)1101 CPLErr JPIPKAKDataset::GetGeoTransform( double * padfTransform )
1102
1103 {
1104 if( bGeoTransformValid )
1105 {
1106 memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
1107
1108 return CE_None;
1109 }
1110 else
1111 return GDALPamDataset::GetGeoTransform( padfTransform );
1112 }
1113
1114 /************************************************************************/
1115 /* GetGCPCount() */
1116 /************************************************************************/
1117
GetGCPCount()1118 int JPIPKAKDataset::GetGCPCount()
1119
1120 {
1121 return nGCPCount;
1122 }
1123
1124 /************************************************************************/
1125 /* GetGCPProjection() */
1126 /************************************************************************/
1127
_GetGCPProjection()1128 const char *JPIPKAKDataset::_GetGCPProjection()
1129
1130 {
1131 if( nGCPCount > 0 )
1132 return pszProjection;
1133 else
1134 return "";
1135 }
1136
1137 /************************************************************************/
1138 /* GetGCP() */
1139 /************************************************************************/
1140
GetGCPs()1141 const GDAL_GCP *JPIPKAKDataset::GetGCPs()
1142
1143 {
1144 return pasGCPList;
1145 }
1146
1147 /************************************************************************/
1148 /* IRasterIO() */
1149 /************************************************************************/
1150
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,int nBandCount,int * panBandMap,GSpacing nPixelSpace,GSpacing nLineSpace,GSpacing nBandSpace,GDALRasterIOExtraArg * psExtraArg)1151 CPLErr JPIPKAKDataset::IRasterIO( GDALRWFlag eRWFlag,
1152 int nXOff, int nYOff, int nXSize, int nYSize,
1153 void * pData, int nBufXSize, int nBufYSize,
1154 GDALDataType eBufType,
1155 int nBandCount, int *panBandMap,
1156 GSpacing nPixelSpace, GSpacing nLineSpace,
1157 GSpacing nBandSpace,
1158 GDALRasterIOExtraArg* psExtraArg)
1159
1160 {
1161 /* -------------------------------------------------------------------- */
1162 /* We need various criteria to skip out to block based methods. */
1163 /* -------------------------------------------------------------------- */
1164 if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
1165 eBufType, nBandCount, panBandMap ) )
1166 return GDALPamDataset::IRasterIO(
1167 eRWFlag, nXOff, nYOff, nXSize, nYSize,
1168 pData, nBufXSize, nBufYSize, eBufType,
1169 nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg );
1170
1171 /* -------------------------------------------------------------------- */
1172 /* Otherwise do this as a single uncached async rasterio. */
1173 /* -------------------------------------------------------------------- */
1174 GDALAsyncReader* ario =
1175 BeginAsyncReader(nXOff, nYOff, nXSize, nYSize,
1176 pData, nBufXSize, nBufYSize, eBufType,
1177 nBandCount, panBandMap,
1178 static_cast<int>(nPixelSpace),
1179 static_cast<int>(nLineSpace),
1180 static_cast<int>(nBandSpace), nullptr);
1181
1182 if( ario == nullptr )
1183 return CE_Failure;
1184
1185 GDALAsyncStatusType status;
1186
1187 do
1188 {
1189 int nXBufOff,nYBufOff,nXBufSize,nYBufSize;
1190
1191 status = ario->GetNextUpdatedRegion(-1.0,
1192 &nXBufOff, &nYBufOff,
1193 &nXBufSize, &nYBufSize );
1194 } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
1195
1196 EndAsyncReader(ario);
1197
1198 if (status == GARIO_ERROR)
1199 return CE_Failure;
1200 else
1201 return CE_None;
1202 }
1203
1204 /************************************************************************/
1205 /* TestUseBlockIO() */
1206 /************************************************************************/
1207
1208 int
TestUseBlockIO(CPL_UNUSED int nXOff,CPL_UNUSED int nYOff,int nXSize,int nYSize,int nBufXSize,int nBufYSize,CPL_UNUSED GDALDataType eDataType,int nBandCount,int * panBandList) const1209 JPIPKAKDataset::TestUseBlockIO( CPL_UNUSED int nXOff, CPL_UNUSED int nYOff,
1210 int nXSize, int nYSize,
1211 int nBufXSize, int nBufYSize,
1212 CPL_UNUSED GDALDataType eDataType,
1213 int nBandCount, int *panBandList ) const
1214
1215 {
1216 /* -------------------------------------------------------------------- */
1217 /* Due to limitations in DirectRasterIO() we can only handle */
1218 /* it no duplicates in the band list. */
1219 /* -------------------------------------------------------------------- */
1220 int i, j;
1221
1222 for( i = 0; i < nBandCount; i++ )
1223 {
1224 for( j = i+1; j < nBandCount; j++ )
1225 if( panBandList[j] == panBandList[i] )
1226 return TRUE;
1227 }
1228
1229 /* -------------------------------------------------------------------- */
1230 /* The rest of the rules are io strategy stuff, and use */
1231 /* configuration checks. */
1232 /* -------------------------------------------------------------------- */
1233 int bUseBlockedIO = bForceCachedIO;
1234
1235 if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 )
1236 bUseBlockedIO = TRUE;
1237
1238 if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 )
1239 bUseBlockedIO = TRUE;
1240
1241 if( bUseBlockedIO
1242 && CPLTestBool( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) )
1243 bUseBlockedIO = FALSE;
1244
1245 return bUseBlockedIO;
1246 }
1247
1248 /*************************************************************************/
1249 /* BeginAsyncReader() */
1250 /*************************************************************************/
1251
1252 GDALAsyncReader*
BeginAsyncReader(int xOff,int yOff,int xSize,int ySize,void * pBuf,int bufXSize,int bufYSize,GDALDataType bufType,int nBandCount,int * pBandMap,int nPixelSpace,int nLineSpace,int nBandSpace,char ** papszOptions)1253 JPIPKAKDataset::BeginAsyncReader(int xOff, int yOff,
1254 int xSize, int ySize,
1255 void *pBuf,
1256 int bufXSize, int bufYSize,
1257 GDALDataType bufType,
1258 int nBandCount, int* pBandMap,
1259 int nPixelSpace, int nLineSpace,
1260 int nBandSpace,
1261 char **papszOptions)
1262 {
1263 CPLDebug( "JPIP", "BeginAsyncReadeR(%d,%d,%d,%d -> %dx%d)",
1264 xOff, yOff, xSize, ySize, bufXSize, bufYSize );
1265
1266 /* -------------------------------------------------------------------- */
1267 /* Recreate the code stream access if needed. */
1268 /* -------------------------------------------------------------------- */
1269 if( bNeedReinitialize )
1270 {
1271 CPLDebug( "JPIPKAK", "\n\nReinitializing after error! ******\n" );
1272
1273 Deinitialize();
1274 if( !Initialize(GetDescription(),TRUE) )
1275 return nullptr;
1276 }
1277
1278 /* -------------------------------------------------------------------- */
1279 /* Provide default packing if needed. */
1280 /* -------------------------------------------------------------------- */
1281 if( nPixelSpace == 0 )
1282 nPixelSpace = GDALGetDataTypeSize(bufType) / 8;
1283 if( nLineSpace == 0 )
1284 nLineSpace = nPixelSpace * bufXSize;
1285 if( nBandSpace == 0 )
1286 nBandSpace = nLineSpace * bufYSize;
1287
1288 /* -------------------------------------------------------------------- */
1289 /* check we have sensible values for windowing. */
1290 /* -------------------------------------------------------------------- */
1291 if (xOff > GetRasterXSize()
1292 || yOff > GetRasterYSize()
1293 || (xOff + xSize) > GetRasterXSize()
1294 || (yOff + ySize) > GetRasterYSize() )
1295 {
1296 CPLError( CE_Failure, CPLE_AppDefined,
1297 "Requested window (%d,%d %dx%d) off dataset.",
1298 xOff, yOff, xSize, ySize );
1299 return nullptr;
1300 }
1301
1302 /* -------------------------------------------------------------------- */
1303 /* Record request information. */
1304 /* -------------------------------------------------------------------- */
1305 JPIPKAKAsyncReader* ario = new JPIPKAKAsyncReader();
1306 ario->poDS = this;
1307 ario->nBufXSize = bufXSize;
1308 ario->nBufYSize = bufYSize;
1309 ario->eBufType = bufType;
1310 ario->nBandCount = nBandCount;
1311 ario->nXOff = xOff;
1312 ario->nYOff = yOff;
1313 ario->nXSize = xSize;
1314 ario->nYSize = ySize;
1315
1316 ario->panBandMap = new int[nBandCount];
1317 if (pBandMap)
1318 {
1319 for (int i = 0; i < nBandCount; i++)
1320 ario->panBandMap[i] = pBandMap[i];
1321 }
1322 else
1323 {
1324 for (int i = 0; i < nBandCount; i++)
1325 ario->panBandMap[i] = i+1;
1326 }
1327
1328 /* -------------------------------------------------------------------- */
1329 /* If the buffer type is of other than images type, we need to */
1330 /* allocate a private buffer the same type as the image which */
1331 /* will be converted later. */
1332 /* -------------------------------------------------------------------- */
1333 if( bufType != eDT )
1334 {
1335 ario->nPixelSpace = GDALGetDataTypeSize(eDT) / 8;
1336 ario->nLineSpace = ario->nPixelSpace * bufXSize;
1337 ario->nBandSpace = ario->nLineSpace * bufYSize;
1338
1339 ario->nAppPixelSpace = nPixelSpace;
1340 ario->nAppLineSpace = nLineSpace;
1341 ario->nAppBandSpace = nBandSpace;
1342
1343 ario->pBuf = VSI_MALLOC3_VERBOSE(bufXSize,bufYSize,ario->nPixelSpace*nBandCount);
1344 if( ario->pBuf == nullptr )
1345 {
1346 delete ario;
1347 return nullptr;
1348 }
1349
1350 ario->pAppBuf = pBuf;
1351 }
1352 else
1353 {
1354 ario->pBuf = pBuf;
1355 ario->pAppBuf = pBuf;
1356
1357 ario->nAppPixelSpace = nPixelSpace;
1358 ario->nPixelSpace = nPixelSpace;
1359
1360 ario->nAppLineSpace = nLineSpace;
1361 ario->nLineSpace = nLineSpace;
1362
1363 ario->nAppBandSpace = nBandSpace;
1364 ario->nBandSpace = nBandSpace;
1365 }
1366
1367 // parse options
1368 const char* pszLevel = CSLFetchNameValue(papszOptions, "LEVEL");
1369 const char* pszLayers = CSLFetchNameValue(papszOptions, "LAYERS");
1370 const char* pszPriority = CSLFetchNameValue(papszOptions, "PRIORITY");
1371
1372 if (pszLayers)
1373 ario->nQualityLayers = atoi(pszLayers);
1374 else
1375 ario->nQualityLayers = nQualityLayers;
1376
1377 if (pszPriority)
1378 {
1379 if (EQUAL(pszPriority, "0"))
1380 ario->bHighPriority = 0;
1381 else
1382 ario->bHighPriority = 1;
1383 }
1384 else
1385 ario->bHighPriority = 1;
1386
1387 /* -------------------------------------------------------------------- */
1388 /* Select an appropriate level based on the ratio of buffer */
1389 /* size to full resolution image. We aim for the next */
1390 /* resolution *lower* than we might expect for the target */
1391 /* buffer unless it falls on a power of two. This is because */
1392 /* the region decompressor only seems to support upsampling */
1393 /* via the numerator/denominator magic. */
1394 /* -------------------------------------------------------------------- */
1395 if (pszLevel)
1396 ario->nLevel = atoi(pszLevel);
1397 else
1398 {
1399 int nRXSize = xSize;
1400 int nRYSize = ySize;
1401 ario->nLevel = 0;
1402
1403 while( ario->nLevel < nResLevels
1404 && (nRXSize > bufXSize || nRYSize > bufYSize) )
1405 {
1406 nRXSize = (nRXSize+1) / 2;
1407 nRYSize = (nRYSize+1) / 2;
1408 ario->nLevel++;
1409 }
1410 }
1411
1412 ario->Start();
1413
1414 return ario;
1415 }
1416
1417 /************************************************************************/
1418 /* EndAsyncReader() */
1419 /************************************************************************/
1420
EndAsyncReader(GDALAsyncReader * poARIO)1421 void JPIPKAKDataset::EndAsyncReader(GDALAsyncReader *poARIO)
1422 {
1423 delete poARIO;
1424 }
1425
1426 /*****************************************/
1427 /* Open() */
1428 /*****************************************/
Open(GDALOpenInfo * poOpenInfo)1429 GDALDataset *JPIPKAKDataset::Open(GDALOpenInfo * poOpenInfo)
1430 {
1431 // test jpip and jpips, assuming jpip is using http as the transport layer
1432 // jpip = http, jpips = https (note SSL is allowed, but jpips is not in the ISO spec)
1433 if (STARTS_WITH_CI(poOpenInfo->pszFilename, "jpip://")
1434 || STARTS_WITH_CI(poOpenInfo->pszFilename, "jpips://"))
1435 {
1436 // perform the initial connection
1437 // using cpl_http for the connection
1438 if (CPLHTTPEnabled() == TRUE)
1439 {
1440 JPIPKAKDataset *poDS = new JPIPKAKDataset();
1441 if (poDS->Initialize(poOpenInfo->pszFilename,FALSE))
1442 {
1443 poDS->SetDescription( poOpenInfo->pszFilename );
1444 return poDS;
1445 }
1446 else
1447 {
1448 delete poDS;
1449 return nullptr;
1450 }
1451 }
1452 else
1453 {
1454 CPLError( CE_Failure, CPLE_OpenFailed,
1455 "Failed to open %s within JPIPKAK driver CPL HTTP not enabled.\n",
1456 poOpenInfo->pszFilename );
1457 return nullptr;
1458 }
1459 }
1460 else
1461 return nullptr;
1462 }
1463
1464 /************************************************************************/
1465 /* GDALRegister_JPIPKAK() */
1466 /************************************************************************/
1467
GDALRegister_JPIPKAK()1468 void GDALRegister_JPIPKAK()
1469 {
1470 if( !GDAL_CHECK_VERSION( "JPIPKAK driver" ) )
1471 return;
1472
1473 if( GDALGetDriverByName( "JPIPKAK" ) != nullptr )
1474 return;
1475
1476 GDALDriver *poDriver = new GDALDriver();
1477
1478 poDriver->SetDescription( "JPIPKAK" );
1479 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
1480 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "JPIP (based on Kakadu)" );
1481 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/raster/jpipkak.html" );
1482 poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jpp-stream" );
1483
1484 poDriver->pfnOpen = JPIPKAKDataset::Open;
1485 GetGDALDriverManager()->RegisterDriver( poDriver );
1486 }
1487
1488 /************************************************************************/
1489 /* JPIPKAKAsyncReader */
1490 /************************************************************************/
JPIPKAKAsyncReader()1491 JPIPKAKAsyncReader::JPIPKAKAsyncReader()
1492 {
1493 panBandMap = nullptr;
1494 pAppBuf = nullptr;
1495 pBuf = nullptr;
1496 nDataRead = 0;
1497 nAppPixelSpace = 0;
1498 nAppLineSpace = 0;
1499 nAppBandSpace = 0;
1500 nLevel = 0;
1501 nQualityLayers = 0;
1502 bHighPriority = FALSE;
1503 bComplete = FALSE;
1504 }
1505
1506 /************************************************************************/
1507 /* ~JPIPKAKAsyncReader */
1508 /************************************************************************/
~JPIPKAKAsyncReader()1509 JPIPKAKAsyncReader::~JPIPKAKAsyncReader()
1510 {
1511 Stop();
1512
1513 // don't own the buffer
1514 delete [] panBandMap;
1515
1516 if( pAppBuf != pBuf )
1517 CPLFree( pBuf );
1518 }
1519
1520 /************************************************************************/
1521 /* Start() */
1522 /************************************************************************/
1523
Start()1524 void JPIPKAKAsyncReader::Start()
1525 {
1526 JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1527
1528 // stop the currently running thread
1529 // start making requests to the server to the server
1530 nDataRead = 0;
1531 bComplete = 0;
1532
1533 // check a thread is not already running
1534 if (((bHighPriority) && (poJDS->bHighThreadRunning)) ||
1535 ((!bHighPriority) && (poJDS->bLowThreadRunning)))
1536 CPLError(CE_Failure, CPLE_AppDefined, "JPIPKAKAsyncReader supports at most two concurrent server communication threads");
1537 else
1538 {
1539 // Ensure we are working against full res
1540 ((JPIPKAKDataset*)poDS)->poCodestream->
1541 apply_input_restrictions( 0, 0, 0, 0, nullptr );
1542
1543 // calculate the url the worker function is going to retrieve
1544 // calculate the kakadu adjust image size
1545 channels.configure(*(((JPIPKAKDataset*)poDS)->poCodestream));
1546
1547 // find current canvas width and height in the cache and check we don't
1548 // exceed this in our process request
1549 kdu_dims view_dims;
1550 kdu_coords ref_expansion;
1551 ref_expansion.x = 1;
1552 ref_expansion.y = 1;
1553
1554 view_dims = ((JPIPKAKDataset*)poDS)->poDecompressor->
1555 get_rendered_image_dims(*((JPIPKAKDataset*)poDS)->poCodestream, &channels,
1556 -1, nLevel,
1557 ref_expansion );
1558
1559 kdu_coords* view_siz = view_dims.access_size();
1560
1561 // Establish the decimation implied by our resolution level.
1562 int nRes = 1;
1563 if (nLevel > 0)
1564 nRes = 2 << (nLevel - 1);
1565
1566 // setup expansion to account for the difference between
1567 // the selected level and the buffer resolution.
1568 exp_numerator.x = nBufXSize;
1569 exp_numerator.y = nBufYSize;
1570
1571 exp_denominator.x = (int) ceil(nXSize / (double) nRes);
1572 exp_denominator.y = (int) ceil(nYSize / (double) nRes);
1573
1574 // formulate jpip parameters and adjust offsets for current level
1575 int fx = view_siz->x / nRes;
1576 int fy = view_siz->y / nRes;
1577
1578 rr_win.pos.x = (int) ceil(nXOff / (1.0 * nRes)); // roffx
1579 rr_win.pos.y = (int) ceil(nYOff / (1.0 * nRes)); // roffy
1580 rr_win.size.x = (int) ceil(nXSize / (1.0 * nRes)); // rsizx
1581 rr_win.size.y = (int) ceil(nYSize / (1.0 * nRes)); // rsizy
1582
1583 if ( rr_win.pos.x + rr_win.size.x > fx)
1584 rr_win.size.x = fx - rr_win.pos.x;
1585 if ( rr_win.pos.y + rr_win.size.y > fy)
1586 rr_win.size.y = fy - rr_win.pos.y;
1587
1588 CPLString jpipUrl;
1589 CPLString comps;
1590
1591 if( poJDS->bYCC )
1592 {
1593 comps = "0,1,2";
1594 }
1595 else
1596 {
1597 for (int i = 0; i < nBandCount; i++)
1598 comps.Printf("%s%i,", comps.c_str(), panBandMap[i]-1);
1599
1600 comps.erase(comps.length() -1);
1601 }
1602
1603 jpipUrl.Printf("%s&type=jpp-stream&roff=%i,%i&rsiz=%i,%i&fsiz=%i,%i,closest&quality=%i&comps=%s",
1604 ((JPIPKAKDataset*)poDS)->osRequestUrl.c_str(),
1605 rr_win.pos.x, rr_win.pos.y,
1606 rr_win.size.x, rr_win.size.y,
1607 fx, fy,
1608 nQualityLayers, comps.c_str());
1609
1610 JPIPRequest* pRequest = new JPIPRequest();
1611 pRequest->bPriority = bHighPriority;
1612 pRequest->osRequest = jpipUrl;
1613 pRequest->poARIO = this;
1614
1615 if( bHighPriority )
1616 poJDS->bHighThreadFinished = 0;
1617 else
1618 poJDS->bLowThreadFinished = 0;
1619
1620 //CPLDebug("JPIPKAKAsyncReader", "THREADING TURNED OFF");
1621 if (CPLCreateThread(JPIPWorkerFunc, pRequest) == -1)
1622 CPLError(CE_Failure, CPLE_AppDefined,
1623 "Unable to create worker jpip thread" );
1624 // run in main thread as a test
1625 //JPIPWorkerFunc(pRequest);
1626 }
1627 }
1628
1629 /************************************************************************/
1630 /* Stop() */
1631 /************************************************************************/
Stop()1632 void JPIPKAKAsyncReader::Stop()
1633 {
1634 JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1635
1636 bComplete = 1;
1637 if (poJDS->pGlobalMutex)
1638 {
1639 if (((bHighPriority) && (!poJDS->bHighThreadFinished)) ||
1640 ((!bHighPriority) && (!poJDS->bLowThreadFinished)))
1641 {
1642 CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() requested." );
1643
1644 // stop the thread
1645 if (bHighPriority)
1646 {
1647 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1648 poJDS->bHighThreadRunning = 0;
1649 CPLReleaseMutex(poJDS->pGlobalMutex);
1650
1651 while (!poJDS->bHighThreadFinished)
1652 CPLSleep(0.1);
1653 }
1654 else
1655 {
1656 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1657 poJDS->bLowThreadRunning = 0;
1658 CPLReleaseMutex(poJDS->pGlobalMutex);
1659
1660 while (!poJDS->bLowThreadFinished)
1661 {
1662 CPLSleep(0.1);
1663 }
1664 }
1665 CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() confirmed." );
1666 }
1667 }
1668 }
1669
1670 /************************************************************************/
1671 /* GetNextUpdatedRegion() */
1672 /************************************************************************/
1673
1674 GDALAsyncStatusType
GetNextUpdatedRegion(double dfTimeout,int * pnxbufoff,int * pnybufoff,int * pnxbufsize,int * pnybufsize)1675 JPIPKAKAsyncReader::GetNextUpdatedRegion(double dfTimeout,
1676 int* pnxbufoff,
1677 int* pnybufoff,
1678 int* pnxbufsize,
1679 int* pnybufsize)
1680 {
1681 GDALAsyncStatusType result = GARIO_ERROR;
1682 JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1683
1684 long nSize = 0;
1685 // take a snapshot of the volatile variables
1686 if (bHighPriority)
1687 {
1688 const long s = poJDS->nHighThreadByteCount - nDataRead;
1689 nSize = s;
1690 }
1691 else
1692 {
1693 const long s = poJDS->nLowThreadByteCount - nDataRead;
1694 nSize = s;
1695 }
1696
1697 /* -------------------------------------------------------------------- */
1698 /* Wait for new data to return if required. */
1699 /* -------------------------------------------------------------------- */
1700 if ((nSize == 0) && dfTimeout != 0 )
1701 {
1702 // poll for change in cache size
1703 clock_t end_wait = 0;
1704
1705 end_wait = clock() + (int) (dfTimeout * CLOCKS_PER_SEC);
1706 while ((nSize == 0) && ((bHighPriority && poJDS->bHighThreadRunning) ||
1707 (!bHighPriority && poJDS->bLowThreadRunning)))
1708 {
1709 if (end_wait)
1710 if (clock() > end_wait && dfTimeout >= 0 )
1711 break;
1712
1713 CPLSleep(0.1);
1714
1715 if (bHighPriority)
1716 {
1717 const long s = poJDS->nHighThreadByteCount - nDataRead;
1718 nSize = s;
1719 }
1720 else
1721 {
1722 const long s = poJDS->nLowThreadByteCount - nDataRead;
1723 nSize = s;
1724 }
1725 }
1726 }
1727
1728 // if there is no pending data and we don't want to wait.
1729 if( nSize == 0 )
1730 {
1731 *pnxbufoff = 0;
1732 *pnybufoff = 0;
1733 *pnxbufsize = 0;
1734 *pnybufsize = 0;
1735
1736 // Indicate an error if the thread finished prematurely
1737 if( (bHighPriority
1738 && !poJDS->bHighThreadRunning
1739 && poJDS->bHighThreadFinished)
1740 || (!bHighPriority
1741 && !poJDS->bLowThreadRunning
1742 && poJDS->bLowThreadFinished) )
1743 {
1744 if( osErrorMsg != "" )
1745 CPLError( CE_Failure, CPLE_AppDefined,
1746 "%s", osErrorMsg.c_str() );
1747 else
1748 CPLError( CE_Failure, CPLE_AppDefined,
1749 "Working thread failed without complete data. (%d,%d,%d)",
1750 bHighPriority,
1751 poJDS->bHighThreadRunning,
1752 poJDS->bHighThreadFinished );
1753 poJDS->bNeedReinitialize = TRUE;
1754 return GARIO_ERROR;
1755 }
1756
1757 // Otherwise there is still pending data to wait for.
1758 return GARIO_PENDING;
1759 }
1760
1761 /* -------------------------------------------------------------------- */
1762 /* Establish the canvas region with the expansion factor */
1763 /* applied, and compute region from the original window cut */
1764 /* down to the target canvas. */
1765 /* -------------------------------------------------------------------- */
1766 kdu_dims view_dims, region;
1767 int nBytesPerPixel = GDALGetDataTypeSize(poJDS->eDT) / 8;
1768 int nPrecision = 0;
1769
1770 try
1771 {
1772 // Ensure we are working against full res
1773 poJDS->poCodestream->apply_input_restrictions( 0, 0, 0, 0, nullptr );
1774
1775 view_dims = poJDS->poDecompressor->get_rendered_image_dims(
1776 *poJDS->poCodestream, &channels,
1777 -1, nLevel, exp_numerator, exp_denominator );
1778
1779 double x_ratio, y_ratio;
1780 x_ratio = view_dims.size.x / (double) poDS->GetRasterXSize();
1781 y_ratio = view_dims.size.y / (double) poDS->GetRasterYSize();
1782
1783 region = rr_win;
1784
1785 region.pos.x = (int) ceil(region.pos.x * x_ratio);
1786 region.pos.y = (int) ceil(region.pos.y * y_ratio);
1787 region.size.x = (int) ceil(region.size.x * x_ratio);
1788 region.size.y = (int) ceil(region.size.y * y_ratio);
1789
1790 region.size.x = MIN(region.size.x,nBufXSize);
1791 region.size.y = MIN(region.size.y,nBufYSize);
1792
1793 if( region.pos.x + region.size.x > view_dims.size.x )
1794 region.size.x = view_dims.size.x - region.pos.x;
1795 if( region.pos.y + region.size.y > view_dims.size.y )
1796 region.size.y = view_dims.size.y - region.pos.y;
1797
1798 region.pos += view_dims.pos;
1799
1800 CPLAssert( nBytesPerPixel == 1 || nBytesPerPixel == 2 );
1801
1802 if( poJDS->poCodestream->get_bit_depth(0) > 16 )
1803 nPrecision = 16;
1804 }
1805 catch(...)
1806 {
1807 // The error handler should already have posted an error message.
1808 return GARIO_ERROR;
1809 }
1810
1811 /* ==================================================================== */
1812 /* Now we process the available cached jpeg2000 data into */
1813 /* imagery. The kdu_region_decompressor only seems to support */
1814 /* reading back one or three components at a time, we may need */
1815 /* to do several processing passes to get the bands we */
1816 /* want. We try to do groups of three were possible, and handle */
1817 /* the rest one band at a time. */
1818 /* ==================================================================== */
1819 int nBandsCompleted = 0;
1820
1821 while( nBandsCompleted < nBandCount )
1822 {
1823 /* -------------------------------------------------------------------- */
1824 /* Set up channel list requested. */
1825 /* -------------------------------------------------------------------- */
1826 std::vector<int> component_indices;
1827 unsigned int i;
1828
1829 if( nBandCount - nBandsCompleted >= 3 )
1830 {
1831 CPLDebug( "JPIPKAK", "process bands %d,%d,%d",
1832 panBandMap[nBandsCompleted],
1833 panBandMap[nBandsCompleted+1],
1834 panBandMap[nBandsCompleted+2] );
1835
1836 component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1837 component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1838 component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1839 }
1840 else
1841 {
1842 CPLDebug( "JPIPKAK", "process band %d",
1843 panBandMap[nBandsCompleted] );
1844 component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1845 }
1846
1847 /* -------------------------------------------------------------------- */
1848 /* Apply region, channel and overview level restrictions. */
1849 /* -------------------------------------------------------------------- */
1850 kdu_dims region_pass = region;
1851
1852 poJDS->poCodestream->apply_input_restrictions(
1853 static_cast<int>(component_indices.size()), &(component_indices[0]),
1854 nLevel, nQualityLayers, ®ion_pass,
1855 KDU_WANT_CODESTREAM_COMPONENTS);
1856
1857 channels.configure(*(poJDS->poCodestream));
1858
1859 for( i=0; i < component_indices.size(); i++ )
1860 channels.source_components[i] = component_indices[i];
1861
1862 kdu_dims incomplete_region = region_pass;
1863 kdu_coords origin = region_pass.pos;
1864
1865 int bIsDecompressing = FALSE;
1866
1867 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1868
1869 try
1870 {
1871
1872 bIsDecompressing = poJDS->poDecompressor->start(
1873 *(poJDS->poCodestream),
1874 &channels, -1, nLevel, nQualityLayers,
1875 region_pass, exp_numerator, exp_denominator, TRUE);
1876
1877 *pnxbufoff = 0;
1878 *pnybufoff = 0;
1879 *pnxbufsize = region_pass.access_size()->x;
1880 *pnybufsize = region_pass.access_size()->y;
1881
1882 // Setup channel buffers
1883 std::vector<kdu_byte*> channel_bufs;
1884
1885 for( i=0; i < component_indices.size(); i++ )
1886 channel_bufs.push_back(
1887 ((kdu_byte *) pBuf)
1888 + (i+nBandsCompleted-component_indices.size()) * nBandSpace );
1889
1890 int pixel_gap = nPixelSpace / nBytesPerPixel;
1891 int row_gap = nLineSpace / nBytesPerPixel;
1892
1893 while ((bIsDecompressing == 1) || (incomplete_region.area() != 0))
1894 {
1895 if( nBytesPerPixel == 1 )
1896 {
1897 bIsDecompressing = poJDS->poDecompressor->
1898 process(&(channel_bufs[0]), false,
1899 pixel_gap, origin, row_gap, 1000000, 0,
1900 incomplete_region, region_pass,
1901 0, false );
1902 }
1903 else if( nBytesPerPixel == 2 )
1904 {
1905 bIsDecompressing = poJDS->poDecompressor->
1906 process((kdu_uint16**) &(channel_bufs[0]), false,
1907 pixel_gap, origin, row_gap, 1000000, 0,
1908 incomplete_region, region_pass,
1909 nPrecision, false );
1910 }
1911
1912 CPLDebug( "JPIPKAK",
1913 "processed=%d,%d %dx%d - incomplete=%d,%d %dx%d",
1914 region_pass.pos.x, region_pass.pos.y,
1915 region_pass.size.x, region_pass.size.y,
1916 incomplete_region.pos.x, incomplete_region.pos.y,
1917 incomplete_region.size.x, incomplete_region.size.y );
1918 }
1919
1920 poJDS->poDecompressor->finish();
1921 CPLReleaseMutex(poJDS->pGlobalMutex);
1922 }
1923 catch(...)
1924 {
1925 poJDS->poDecompressor->finish();
1926 CPLReleaseMutex(poJDS->pGlobalMutex);
1927 // The error handler should already have posted an error message.
1928 return GARIO_ERROR;
1929 }
1930 } // nBandsCompleted < nBandCount
1931
1932 /* -------------------------------------------------------------------- */
1933 /* If the application buffer is of a different type than our */
1934 /* band we need to copy into the application buffer at this */
1935 /* point. */
1936 /* */
1937 /* We could optimize to only update affected area, but that is */
1938 /* always the whole area for the JPIP driver it seems. */
1939 /* -------------------------------------------------------------------- */
1940 if( pAppBuf != pBuf )
1941 {
1942 int iY, iBand;
1943 GByte *pabySrc = (GByte *) pBuf;
1944 GByte *pabyDst = (GByte *) pAppBuf;
1945
1946 for( iBand = 0; iBand < nBandCount; iBand++ )
1947 {
1948 for( iY = 0; iY < nBufYSize; iY++ )
1949 {
1950 GDALCopyWords( pabySrc + nLineSpace * iY + nBandSpace * iBand,
1951 poJDS->eDT, nPixelSpace,
1952 pabyDst + nAppLineSpace*iY + nAppBandSpace*iBand,
1953 eBufType, nAppPixelSpace,
1954 nBufXSize );
1955 }
1956 }
1957 }
1958
1959 /* -------------------------------------------------------------------- */
1960 /* has there been any more data read while were have been processing?*/
1961 /* -------------------------------------------------------------------- */
1962 long size = 0;
1963 if (bHighPriority)
1964 {
1965 const long x = poJDS->nHighThreadByteCount - nDataRead;
1966 size = x;
1967 }
1968 else
1969 {
1970 const long x = poJDS->nLowThreadByteCount - nDataRead;
1971 size = x;
1972 }
1973
1974 if ((bComplete) && (nSize == size))
1975 result = GARIO_COMPLETE;
1976 else
1977 result = GARIO_UPDATE;
1978
1979 nDataRead += nSize;
1980
1981 if( result == GARIO_ERROR )
1982 poJDS->bNeedReinitialize = TRUE;
1983
1984 return result;
1985 }
1986
1987 /************************************************************************/
1988 /* JPIPDataSegment() */
1989 /************************************************************************/
JPIPDataSegment()1990 JPIPDataSegment::JPIPDataSegment()
1991 {
1992 nId = 0;
1993 nAux = 0;
1994 nClassId = 0;
1995 nCodestream = 0;
1996 nOffset = 0;
1997 nLen = 0;
1998 pabyData = nullptr;
1999 bIsFinal = FALSE;
2000 bIsEOR = FALSE;
2001 }
2002
2003 /************************************************************************/
2004 /* ~JPIPDataSegment() */
2005 /************************************************************************/
~JPIPDataSegment()2006 JPIPDataSegment::~JPIPDataSegment()
2007 {
2008 CPLFree(pabyData);
2009 }
2010
2011 /************************************************************************/
2012 /* JPIPWorkerFunc() */
2013 /************************************************************************/
JPIPWorkerFunc(void * req)2014 static void JPIPWorkerFunc(void *req)
2015 {
2016 int nCurrentTransmissionLength = 2000;
2017 int nMinimumTransmissionLength = 2000;
2018
2019 JPIPRequest *pRequest = (JPIPRequest *)req;
2020 JPIPKAKDataset *poJDS =
2021 (JPIPKAKDataset*)(pRequest->poARIO->GetGDALDataset());
2022
2023 int bPriority = pRequest->bPriority;
2024
2025 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2026
2027 CPLDebug( "JPIPKAK", "working thread starting." );
2028 // set the running status
2029 if (bPriority)
2030 {
2031 poJDS->bHighThreadRunning = 1;
2032 poJDS->bHighThreadFinished = 0;
2033 }
2034 else
2035 {
2036 poJDS->bLowThreadRunning = 1;
2037 poJDS->bLowThreadFinished = 0;
2038 }
2039
2040 CPLReleaseMutex(poJDS->pGlobalMutex);
2041
2042 CPLString osHeaders = "HEADERS=";
2043 osHeaders += "Accept: jpp-stream";
2044 CPLString osPersistent;
2045
2046 osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", poJDS );
2047
2048 char *apszOptions[] = {
2049 (char *) osHeaders.c_str(),
2050 (char *) osPersistent.c_str(),
2051 nullptr
2052 };
2053
2054 while( true )
2055 {
2056 // modulate the len= parameter to use the currently available bandwidth
2057 long nStart = clock();
2058
2059 if (((bPriority) && (!poJDS->bHighThreadRunning)) || ((!bPriority) && (!poJDS->bLowThreadRunning)))
2060 break;
2061
2062 // make jpip requests
2063 // adjust transmission length
2064 CPLString osCurrentRequest;
2065 osCurrentRequest.Printf("%s&len=%i", pRequest->osRequest.c_str(), nCurrentTransmissionLength);
2066 CPLHTTPResult *psResult = CPLHTTPFetch(osCurrentRequest, apszOptions);
2067 if (psResult->nDataLen == 0)
2068 {
2069 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2070 if( psResult->pszErrBuf )
2071 pRequest->poARIO->
2072 osErrorMsg.Printf( "zero data returned from server, timeout?\n%s", psResult->pszErrBuf );
2073 else
2074 pRequest->poARIO->
2075 osErrorMsg = "zero data returned from server, timeout?";
2076
2077 // status is not being set, always zero in cpl_http
2078 CPLDebug("JPIPWorkerFunc", "zero data returned from server");
2079 CPLReleaseMutex(poJDS->pGlobalMutex);
2080 CPLHTTPDestroyResult(psResult);
2081 break;
2082 }
2083
2084 if( psResult->pszContentType != nullptr )
2085 CPLDebug( "JPIPKAK", "Content-type: %s", psResult->pszContentType );
2086
2087 if( psResult->pszContentType != nullptr
2088 && strstr(psResult->pszContentType,"html") != nullptr )
2089 {
2090 CPLDebug( "JPIPKAK", "%s", psResult->pabyData );
2091 }
2092
2093 int bytes = psResult->nDataLen;
2094 long nEnd = clock();
2095
2096 if ((nEnd - nStart) > 0)
2097 nCurrentTransmissionLength = (int) MAX(bytes / ((1.0 * (nEnd - nStart)) / CLOCKS_PER_SEC), nMinimumTransmissionLength);
2098
2099 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2100
2101 int bError;
2102 int bComplete = ((JPIPKAKDataset*)pRequest->poARIO->GetGDALDataset())->ReadFromInput(psResult->pabyData, psResult->nDataLen, bError);
2103 if (bPriority)
2104 poJDS->nHighThreadByteCount += psResult->nDataLen;
2105 else
2106 poJDS->nLowThreadByteCount += psResult->nDataLen;
2107
2108 pRequest->poARIO->SetComplete(bComplete);
2109
2110 CPLReleaseMutex(poJDS->pGlobalMutex);
2111 CPLHTTPDestroyResult(psResult);
2112
2113 if (bComplete || bError )
2114 break;
2115 }
2116
2117 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2118
2119 CPLDebug( "JPIPKAK", "Worker shutting down." );
2120
2121 if (bPriority)
2122 {
2123 poJDS->bHighThreadRunning = 0;
2124 poJDS->bHighThreadFinished = 1;
2125 }
2126 else
2127 {
2128 poJDS->bLowThreadRunning = 0;
2129 poJDS->bLowThreadFinished = 1;
2130 }
2131
2132 CPLReleaseMutex(poJDS->pGlobalMutex);
2133
2134 // end of thread
2135 delete pRequest;
2136 }
2137