1 /* 2 * Copyright 2007-2008, Haiku. Stephan Aßmus <superstippi@gmx.de> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 #include "MediaTrackVideoSupplier.h" 6 7 #include <new> 8 #include <stdio.h> 9 #include <string.h> 10 11 #include <MediaTrack.h> 12 13 #include "ColorSpaceToString.h" 14 15 using std::nothrow; 16 17 #define DEBUG_DECODED_FRAME 0 18 #if DEBUG_DECODED_FRAME 19 # include <Bitmap.h> 20 # include <BitmapStream.h> 21 # include <File.h> 22 # include <TranslatorRoster.h> 23 #endif // DEBUG_DECODED_FRAME 24 25 // constructor 26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track, 27 int32 trackIndex, status_t& initStatus) 28 : 29 VideoTrackSupplier(), 30 fVideoTrack(track), 31 32 fPerformanceTime(0), 33 fDuration(0), 34 fCurrentFrame(0), 35 36 fTrackIndex(trackIndex) 37 { 38 if (!fVideoTrack) { 39 printf("MediaTrackVideoSupplier() - no video track\n"); 40 return; 41 } 42 43 initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0); 44 45 fDuration = fVideoTrack->Duration(); 46 47 // for (bigtime_t time = 0; time < fDuration; time += 10000) { 48 // bigtime_t keyFrameTime = time; 49 // fVideoTrack->FindKeyFrameForTime(&keyFrameTime, 50 // B_MEDIA_SEEK_CLOSEST_BACKWARD); 51 // printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime); 52 // } 53 } 54 55 // destructor 56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier() 57 { 58 } 59 60 61 const media_format& 62 MediaTrackVideoSupplier::Format() const 63 { 64 return fFormat; 65 } 66 67 68 status_t 69 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const 70 { 71 if (!fVideoTrack) 72 return B_NO_INIT; 73 return fVideoTrack->EncodedFormat(format); 74 } 75 76 77 status_t 78 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const 79 { 80 if (!fVideoTrack) 81 return B_NO_INIT; 82 return fVideoTrack->GetCodecInfo(info); 83 } 84 85 86 status_t 87 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime, 88 const media_raw_video_format& format, bool& wasCached) 89 { 90 if (!fVideoTrack) 91 return B_NO_INIT; 92 if (!buffer) 93 return B_BAD_VALUE; 94 95 // Hack for single frame video (cover art). Media player really wants a 96 // video track and will not seek in the middle of a video frame. So we 97 // pretend to be a 25fps stream and keep rendering the same frame over 98 // and over again. 99 if (fVideoTrack->CountFrames() < 2) { 100 static int already = false; 101 if (already) { 102 wasCached = true; 103 return B_OK; 104 } 105 already = true; 106 } 107 108 status_t ret = B_OK; 109 if (format.display.format 110 != fFormat.u.raw_video.display.format 111 || fFormat.u.raw_video.display.bytes_per_row 112 != format.display.bytes_per_row) { 113 ret = _SwitchFormat(format.display.format, 114 format.display.bytes_per_row); 115 if (ret < B_OK) { 116 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 117 "unable to switch media format: %s\n", strerror(ret)); 118 return ret; 119 } 120 } 121 122 // read a frame 123 int64 frameCount = 1; 124 // TODO: how does this work for interlaced video (field count > 1)? 125 media_header mediaHeader; 126 ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader); 127 128 if (ret < B_OK && ret != B_LAST_BUFFER_ERROR) { 129 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 130 "error while reading frame of track: %s\n", strerror(ret)); 131 } else 132 fPerformanceTime = mediaHeader.start_time; 133 134 fCurrentFrame = fVideoTrack->CurrentFrame(); 135 if (performanceTime) 136 *performanceTime = fPerformanceTime; 137 138 #if DEBUG_DECODED_FRAME 139 if (modifiers() & B_SHIFT_KEY) { 140 BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 141 BTranslatorRoster* roster = BTranslatorRoster::Default(); 142 BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow()); 143 memcpy(bitmap->Bits(), buffer, bitmap->BitsLength()); 144 BBitmapStream bitmapStream(bitmap); 145 roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0); 146 bitmapStream.DetachBitmap(&bitmap); 147 delete bitmap; 148 } 149 #endif // DEBUG_DECODED_FRAME 150 151 if (ret == B_LAST_BUFFER_ERROR && fVideoTrack->CountFrames() < 2) 152 return B_OK; 153 return ret; 154 } 155 156 157 status_t 158 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame) 159 { 160 if (!fVideoTrack) 161 return B_NO_INIT; 162 163 if (fVideoTrack->CountFrames() < 2) 164 return B_OK; 165 166 //int64 wantedFrame = *frame; 167 status_t ret = fVideoTrack->FindKeyFrameForFrame(frame, 168 B_MEDIA_SEEK_CLOSEST_BACKWARD); 169 //printf("found keyframe for frame %lld -> %lld\n", wantedFrame, *frame); 170 return ret; 171 } 172 173 174 status_t 175 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime) 176 { 177 if (!fVideoTrack) 178 return B_NO_INIT; 179 180 if (fVideoTrack->CountFrames() < 2) 181 return B_OK; 182 183 bigtime_t _performanceTime = *performanceTime; 184 status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime, 185 B_MEDIA_SEEK_CLOSEST_BACKWARD); 186 if (ret < B_OK) 187 return ret; 188 189 ret = fVideoTrack->SeekToTime(performanceTime); 190 if (ret == B_OK) { 191 if (_performanceTime != *performanceTime) { 192 printf("seeked by time: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME 193 "\n", _performanceTime, *performanceTime); 194 } 195 fPerformanceTime = *performanceTime; 196 fCurrentFrame = fVideoTrack->CurrentFrame(); 197 } 198 199 return ret; 200 } 201 202 203 status_t 204 MediaTrackVideoSupplier::SeekToFrame(int64* frame) 205 { 206 if (!fVideoTrack) 207 return B_NO_INIT; 208 209 if (fVideoTrack->CountFrames() < 2) 210 return B_OK; 211 212 int64 wantFrame = *frame; 213 214 if (wantFrame == fCurrentFrame) 215 return B_OK; 216 217 status_t ret = fVideoTrack->FindKeyFrameForFrame(frame, 218 B_MEDIA_SEEK_CLOSEST_BACKWARD); 219 if (ret != B_OK) 220 return ret; 221 if (wantFrame > *frame) { 222 // Work around a rounding problem with some extractors and 223 // converting frames <-> time <-> internal time. 224 int64 nextWantFrame = wantFrame + 1; 225 if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame, 226 B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) { 227 if (nextWantFrame == wantFrame) { 228 wantFrame++; 229 *frame = wantFrame; 230 } 231 } 232 } 233 234 //if (wantFrame != *frame) { 235 // printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame); 236 //} 237 238 if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) { 239 // The current frame is already closer to the wanted frame 240 // than the next keyframe before it. 241 *frame = fCurrentFrame; 242 return B_OK; 243 } 244 245 ret = fVideoTrack->SeekToFrame(frame); 246 if (ret != B_OK) 247 return ret; 248 249 //if (wantFrame != *frame) { 250 // printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame, 251 // fCurrentFrame); 252 //} 253 254 fCurrentFrame = *frame; 255 fPerformanceTime = fVideoTrack->CurrentTime(); 256 257 return ret; 258 } 259 260 261 // #pragma mark - 262 263 264 BRect 265 MediaTrackVideoSupplier::Bounds() const 266 { 267 return BRect(0, 0, fFormat.u.raw_video.display.line_width - 1, 268 fFormat.u.raw_video.display.line_count - 1); 269 } 270 271 272 color_space 273 MediaTrackVideoSupplier::ColorSpace() const 274 { 275 return fFormat.u.raw_video.display.format; 276 } 277 278 279 uint32 280 MediaTrackVideoSupplier::BytesPerRow() const 281 { 282 return fFormat.u.raw_video.display.bytes_per_row; 283 } 284 285 286 // #pragma mark - 287 288 289 status_t 290 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow) 291 { 292 // get the encoded format 293 fFormat.Clear(); 294 status_t ret = fVideoTrack->EncodedFormat(&fFormat); 295 if (ret < B_OK) { 296 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 297 "fVideoTrack->EncodedFormat(): %s\n", strerror(ret)); 298 return ret; 299 } 300 301 // get ouput video frame size 302 uint32 width = fFormat.u.encoded_video.output.display.line_width; 303 uint32 height = fFormat.u.encoded_video.output.display.line_count; 304 if (format == B_NO_COLOR_SPACE) { 305 format = fFormat.u.encoded_video.output.display.format; 306 if (format == B_NO_COLOR_SPACE) { 307 // if still no preferred format, try the most commonly 308 // supported overlay format 309 format = B_YCbCr422; 310 } else { 311 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 312 "preferred color space: %s\n", 313 color_space_to_string(format)); 314 } 315 } 316 317 uint32 minBytesPerRow; 318 if (format == B_YCbCr422) 319 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 320 else 321 minBytesPerRow = width * 4; 322 bytesPerRow = max_c(bytesPerRow, minBytesPerRow); 323 324 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 325 if (ret < B_OK) { 326 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 327 "fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n", 328 strerror(ret)); 329 format = B_RGB32; 330 bytesPerRow = max_c(bytesPerRow, width * 4); 331 332 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 333 if (ret < B_OK) { 334 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 335 "fVideoTrack->DecodedFormat(): %s - giving up\n", 336 strerror(ret)); 337 return ret; 338 } 339 } 340 341 if (fFormat.u.raw_video.display.format != format) { 342 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 343 " codec changed colorspace of decoded format (%s -> %s)!\n", 344 color_space_to_string(format), 345 color_space_to_string(fFormat.u.raw_video.display.format)); 346 // check if the codec forgot to adjust bytes_per_row 347 format = fFormat.u.raw_video.display.format; 348 if (format == B_YCbCr422) 349 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 350 else 351 minBytesPerRow = width * 4; 352 if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) { 353 printf(" -> stupid codec forgot to adjust bytes_per_row!\n"); 354 355 ret = _SetDecodedFormat(width, height, format, minBytesPerRow); 356 } 357 } 358 359 if (fFormat.u.raw_video.last_active != height - 1) { 360 printf("should skip %" B_PRId32 " lines at bottom!\n", 361 (height - 1) - fFormat.u.raw_video.last_active); 362 } 363 364 return ret; 365 } 366 367 368 status_t 369 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height, 370 color_space format, uint32 bytesPerRow) 371 { 372 // specifiy the decoded format. we derive this information from 373 // the encoded format (width & height). 374 fFormat.Clear(); 375 // fFormat.u.raw_video.last_active = height - 1; 376 // fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 377 // fFormat.u.raw_video.pixel_width_aspect = 1; 378 // fFormat.u.raw_video.pixel_height_aspect = 1; 379 fFormat.u.raw_video.display.format = format; 380 fFormat.u.raw_video.display.line_width = width; 381 fFormat.u.raw_video.display.line_count = height; 382 fFormat.u.raw_video.display.bytes_per_row = bytesPerRow; 383 384 return fVideoTrack->DecodedFormat(&fFormat); 385 } 386 387